Android安装apk与Intent.VIEW_ACTION无法使用文件提供程序

Joh*_*y19 61 android android-intent android-fileprovider viewaction android-7.0-nougat

我的应用程序有一个自动更新功能,下载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);
Run Code Online (Sandbox Code Playgroud)

这适用于所有设备<24

现在使用Android 24显然我们不再允许使用file:///启动意图并且在一些谷歌搜索之后建议使用A文件提供程序

新代码:

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);
Run Code Online (Sandbox Code Playgroud)

现在activity.startActivity(安装); 抛出错误

找不到处理Intent的活动{act = android.intent.action.VIEW dat = content://com.xxxx.xx.provider/MyFolder/Download/MyApkFile.apk typ = application/vnd.android.package-archive flg = 0x4000000}

有什么方法可以在Android 7(24)中打开APK查看器吗?

jus*_*ser 142

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

ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.INSTALL_PACKAGE dat=content://XXX.apk flg=0x1 }
Run Code Online (Sandbox Code Playgroud)

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

FileUriExposedException: file:///XXX.apk exposed beyond app through Intent.getData()
Run Code Online (Sandbox Code Playgroud)

我的解决方案是在模拟器上使用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);
Run Code Online (Sandbox Code Playgroud)

Android Nougat 7.1更新:

您还需要在清单中添加权限REQUEST_INSTALL_PACKAGES.它可以从Api Level 23(Android 6.0 Marshmallow)获得,并且需要25级(Android 7.1 Nougat).

更新:

如果您尝试安装的文件位于外部存储上,请记住请求读取和写入外部存储的权限.并为Android Nougat及以上版本设置正确的FileProvider.

首先通过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;
}
Run Code Online (Sandbox Code Playgroud)

以下是外部存储上的"下载"文件夹的文件提供程序示例.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>
Run Code Online (Sandbox Code Playgroud)

resources/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>
Run Code Online (Sandbox Code Playgroud)

如果在安装.apk时遇到错误,说"解析包时出现问题".可能是您没有要求读/写权限,或者您尝试安装的文件不存在或已损坏.

  • 很好的答案 - 谢谢!值得一提的是,从**Oreo**开始,您还必须在清单中添加**REQUEST_INSTALL_PACKAGES**权限.否则它只会默默地失败. (23认同)
  • 在Android Oreo中你需要:if(Build.VERSION.SDK_INT> = Build.VERSION_CODES.O &&(!getPackageManager().canRequestPackageInstalls())){startActivityForResult(new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).setData(Uri.parse(String) .format("package:%s",getPackageName()))),1234); } (4认同)
  • 我的问题是我两次调用`setFlags`,这基本上重设了第一个标志。我不得不使用`addFlag`代替。 (2认同)
  • 记下 `&lt;uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /&gt;` 是 Android N(Nougot) 所必需的 (2认同)

Hos*_*ami 17

我在调用 start 活动时遇到了这个问题。暂停我当前的活动后,它突然回来并调用了 onResume。就像什么都没发生过一样。我的问题是清单中的此权限:

 <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
Run Code Online (Sandbox Code Playgroud)

几乎没有人提到这一点。所以记住这一点。在 sdk >= 24 中,您需要使用提供程序,因为它需要在 sdk 24 以下以 file:/// 开头的意图,您应该提供以 content:/// 开头的 uri,这就是为什么我们需要 sdk 24 及更高版本的文件提供程序。我认为我不需要为此编写任何代码,因为@just_user 已经写了正确的答案。


小智 6

这可能是问题所在

intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 
Run Code Online (Sandbox Code Playgroud)

在你的例子中应该是

install.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Run Code Online (Sandbox Code Playgroud)

as install是意图的名称。

  • 我已经从使用file:///切换到content://,并且有一段时间无法从文件提供者打开文件了。这解决了我的问题。 (2认同)