当前位置: 首页 > 知识库问答 >
问题:

intent.view_action不与文件提供程序一起工作的Android安装apk

李康安
2023-03-14

我的应用程序有一个自动更新功能,下载一个APK,当下载完成时,intent.view_action打开应用程序,让用户安装下载的APK

         Uri uri = Uri.parse("file://" + destination);
         Intent install = new Intent(Intent.ACTION_VIEW);
        install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        install.setDataAndType(uri,
            manager.getMimeTypeForDownloadedFile(downloadId));
        activity.startActivity(install);

这适用于所有<24的设备

现在有了Android24,显然我们不允许再用File:///启动意图了,在谷歌搜索之后,建议使用文件提供程序

新代码:

Intent install = new Intent(Intent.ACTION_VIEW);
    install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    install.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    Uri apkUri = FileProvider.getUriForFile(AutoUpdate.this,
        BuildConfig.APPLICATION_ID + ".provider", file);
    install.setDataAndType(apkUri,
        manager.getMimeTypeForDownloadedFile(downloadId));
    activity.startActivity(install);

共有1个答案

周墨一
2023-03-14

经过大量的尝试,我已经能够通过为低于Nougat的任何东西创建不同的意图来解决这个问题,因为在Nougat导致错误之前,使用FileProvider创建Android版本的安装意图:

ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.INSTALL_PACKAGE dat=content://XXX.apk flg=0x1 }

在Android上使用普通Uri时,Nougat会产生以下错误:

FileUriExposedException: file:///XXX.apk exposed beyond app through Intent.getData()

我的解决方案是为我工作的Android N上的模拟器和一个运行Android M的手机。

File toInstall = new File(appDirectory, appName + ".apk");
Intent intent;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    Uri apkUri = FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID + ".fileprovider", toInstall);
    intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
    intent.setData(apkUri);
    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
    Uri apkUri = Uri.fromFile(toInstall);
    intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
activity.startActivity(intent);

首先通过调用下面的canreadwriteexternal()检查是否具有写权限,如果没有,请在下面调用requestpermission():

private static final int REQUEST_WRITE_PERMISSION = 786;

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (requestCode == REQUEST_WRITE_PERMISSION && grantResults[0] == PackageManager.PERMISSION_GRANTED)
        Toast.makeText(this, "Permission granted", Toast.LENGTH_LONG).show();
}

private void requestPermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
        requestPermissions(new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_PERMISSION);
}

private boolean canReadWriteExternal() {
    return Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
            ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED;
}

下面是一个用于外部存储中下载文件夹的文件提供程序的示例。AndroidManifest.xml:

<application ... >
    ...

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${applicationId}.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/filepaths" />
    </provider>
</application>

参考资料/xml/filepaths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_download" path="Download"/>
</paths>

您可以使用PackageManager类的canRequestPackageInstalls方法检查是否允许您的应用程序安装APK。如果返回false,则可以使用ACTION_MANAGE_UNKNOWN_APP_SOURCES操作启动intent以启动设置对话框,用户可以在该对话框中允许应用程序安装apk。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O 
        && !getPackageManager().canRequestPackageInstalls()) {
    Intent unknownAppSourceIntent = new Intent()
            .setAction(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES)
            .setData(Uri.parse(String.format("package:%s", getPackageName())));

    unknownAppSourceDialog.launch(unknownAppSourceIntent);
} else {
    // App already have the permission to install so launch the APK installation.
    startActivity(intent);
}

确保将以下代码添加到活动中以接收意图的结果。

ActivityResultLauncher<Intent> unknownAppSourceDialog = registerForActivityResult(
    new ActivityResultContracts.StartActivityForResult(),
    result -> {
        if (result.getResultCode() == Activity.RESULT_OK) {
            // User has allowed app to install APKs
            // so we can now launch APK installation.
            startActivity(intent);
        }
    });
 类似资料:
  • 嗨,我已经实现了融合位置提供程序Api,用于在服务中获取位置更新。我能够根据设置的间隔触发onlocationchange事件。但是,当我将setsmallstDisplace设置为10米时,即使设备静止,事件也会被触发。是否有人有类似的问题。请提供建议。下面是代码 为了找到两个位置值之间的距离,我使用了这种方法。 但是即使设备是静止的,我也能得到43.51 , 49.32 , 520.02的距离

  • 我有两个app:app1和app2。 App2具有: paths.xml: App2在其活动中从App1接收获取映像URI的请求。确定URI后,App2活动执行以下操作: 从App2接收回结果后,App1执行以下操作: 在App1中,从App2返回时,我得到以下异常: java.lang.SecurityException:权限拒绝:从ProcessRecord{52a99eb0 3493:com

  • 在我的Laravel应用程序中,我需要定期使用Guzzle将数据发送到API。 API使用承载令牌进行身份验证,并请求和接受原始JSON。为了进行测试,我使用Postman访问了API,一切都工作得很好。

  • 我在pom中添加了jasypt spring boot Starter1.18版本,因为我的spring boot版本是1.5.16。我正在尝试加密用于访问spring cloud配置服务器URL、用户名和密码的bootstrap.properties。密码是jasypt加密格式,但是,当连接到spring cloud config服务器时,它不会发送解密值。有人知道如何使用jasypt加密boo

  • 我想从Spring Boot应用程序创建一个war文件,我可以将其部署到独立的Tomcat容器中,而不是使用嵌入式容器。 我可以创建war文件,并使用单独运行它,它工作得很好。 我使用(使用Gradle、Tomcat7、Java1.7)构建了应用程序。 但当我将war文件部署到独立的Tomcat并启动它时,根据日志显示,应用程序似乎没有错误地启动,但我无法访问任何资源,控制器URL也无法工作。 当

  • 我是不是漏掉了什么?我很确定编码sshPrivateKey字节[]是一种方法(如果我不这样做,JSCH会抱怨错误的密钥),但我不确定我还缺少什么?