Android安装带有Intent.VIEW_ACTION的APK不适用于文件提供程序


73

我的应用程序具有自动更新功能,可以下载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:///来启动意图。

新代码:

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(install); 引发错误

找不到用于处理意图的活动{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查看器?


3
使用ACTION_INSTALL_PACKAGE。这为我工作作为N开发者预览版的6月版的。
CommonsWare,2016年

2
找不到可处理意图的活动{act = android.intent.action.INSTALL_PACKAGE ...
Johny19'8

1
在运行Android 7.0(版本NRD90M)的Nexus 9上进行测试时ACTION_INSTALL_PACKAGE,我没有使用FileProvider来提供APK的问题。与您相比Intent,除了操作字符串不同外,我不使用FLAG_ACTIVITY_CLEAR_TOPsetData()而是使用而不是setDataAndType()
CommonsWare,2016年

您从哪里获得“文件”?最后一个论点getUriForFile
列宁·拉贾·拉卡瑟卡兰(Linin Raj Rajasekaran),2016年

Intent.ACTION_INSTALL_PACKAGE已过时,请使用Intent.ACTION_VIEW
Kenneth

Answers:


166

经过大量尝试,我能够通过为低于Nougat的任何内容创建不同的Intent来解决此问题,例如使用FileProvider在Nougat导致错误之前使用Android版本创建安装Intent。

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

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

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);

Android Nougat 7.1更新:

您还需要在清单中添加权限REQUEST_INSTALL_PACKAGES。它可从Api级别23(Android 6.0棉花糖)获取,而从级别25(Android 7.1牛轧糖)需要。

更新:

如果您尝试安装的文件在外部存储器上,请记住要请求读取和写入外部存储器的权限。并为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;
}

这是外部存储上Download文件夹的文件提供程序示例。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>

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>

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


3
我的问题是我打了setFlags两次电话,这基本上重设了第一个标志。我不得不使用addFlag
列宁·拉杰·拉贾塞卡兰(Linin Raj Rajasekaran),2013年

1
我这样做了,但是却收到一条错误消息,提示“解析程序包时出现问题”。如果我使用公共目录,则错误消失了,但是不幸的是,我需要使用私有目录。
库里(Kuri)

33
很好的答案-谢谢!值得一提的是,从Oreo开始,您还必须向清单添加REQUEST_INSTALL_PACKAGES权限。否则,它只是默默地失败。
Jasongiss '17

5
在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);}
jpussacq

2
请记<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />需要为Android N(Nougot)
阿卡什阿明

13

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

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

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


我相信您的评论是相反的。对于SDK> = 24,URI必须以“ content:///”开头。在SDK 24以下,它应以'file:///'开头
Kenneth Argo'December'20

6

这可能是问题所在

intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 

在你的例子中应该是

install.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

as install是意图的名称。


有什么为您解决的吗?我无法与电子邮件应用程序共享文件uri。
Sunita 2016年

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

2

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

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

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

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

否则,您将获得ActivityNotFoundException


1
只要始终使用,DataAndType便没有问题。
Pierre
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.