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

时间:2016-08-25 14:10:18

标签: android android-intent android-fileprovider android-7.0-nougat viewaction

我的应用程序具有自动更新功能,可以下载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

现在使用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);

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

  

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

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

4 个答案:

答案 0 :(得分:114)

经过大量尝试后,我能够通过为Nougat之下的任何东西创建不同的Intent来解决这个问题,因为在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");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    Uri apkUri = FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID + ".provider", toInstall);
    Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
    intent.setData(apkUri);
    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    activity.startActivity(intent)
} else {
    Uri apkUri = Uri.fromFile(toInstall);
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    activity.startActivity(intent);
}

更新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;
}

以下是外部存储上的“下载”文件夹的文件提供程序示例。的的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>

如果在安装.apk时出现类似&#34之类的错误;解析包时会出现问题。&#34;可能是您没有要求读/写权限,或者您尝试安装的文件不存在或已损坏。

答案 1 :(得分:5)

这可能是问题,你有

intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 
你的例子中的

应该是

install.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

as install是意图的名称。

答案 2 :(得分:5)

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

 <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

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

答案 3 :(得分:1)

您应注意,对于<24的API,您需要使用:

        setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive")

而不是分别设置数据和类型:

data = Uri.fromFile(apkFile)
    type = "application/vnd.android.package-archive"

否则,您将获得ActivityNotFoundException