带有 Intent.VIEW_ACTION 的 Android 安装 apk 不适用于文件提供程序
Posted
技术标签:
【中文标题】带有 Intent.VIEW_ACTION 的 Android 安装 apk 不适用于文件提供程序【英文标题】:Android install apk with Intent.VIEW_ACTION not working with File provider 【发布时间】:2017-01-02 00:33:29 【问题描述】:我的应用有一个自动更新功能,它可以下载一个 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);
这适用于所有设备
现在使用 android 24 显然我们不再允许使用 file:/// 启动意图,并且在谷歌搜索后建议使用 A File Provider
新代码:
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);报错
未找到处理 Intent act=android.intent.action.VIEW 的 Activity dat=content://com.xxxx.xx.provider/MyFolder/Download/MyApkFile.apk typ=application/vnd.android.package-archive flg=0x4000000
有什么方法可以在 Android 7 (24) 中打开 APK 查看器?
【问题讨论】:
使用ACTION_INSTALL_PACKAGE
。 worked for me N 开发者预览版的六月版。
找不到处理 Intent 的 Activity act=android.intent.action.INSTALL_PACKAGE ...
在运行 Android 7.0(构建 NRD90M)的 Nexus 9 上进行测试时,我对 ACTION_INSTALL_PACKAGE
使用 FileProvider
提供 APK 没有任何问题。和你的Intent
相比,除了动作字符串不同,我没有用FLAG_ACTIVITY_CLEAR_TOP
,我用setData()
而不是setDataAndType()
。
你从哪里得到“文件”? getUriForFile
中的最后一个参数
Intent.ACTION_INSTALL_PACKAGE 已弃用,请使用 Intent.ACTION_VIEW
【参考方案1】:
经过大量尝试,我已经能够通过为低于 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 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 Level 23 (Android 6.0 Marshmallow) 开始可用,并且需要从 Level 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>
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 时遇到错误,例如“解析软件包时出现问题”。可能是您没有请求读/写权限,或者您尝试安装的文件不存在或已损坏。
Android Oreo 8.0 更新:
您必须检查当前应用程序是否允许在 Android Oreo 8.0 或更高版本上安装 APK。
您可以使用 PackageManager 类的 canRequestPackageInstalls 方法检查您的应用是否允许安装 APK。如果它返回 false,那么您可以使用 ACTION_MANAGE_UNKNOWN_APP_SOURCES 操作启动意图以启动设置对话框,用户可以在其中允许应用安装 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);
);
【讨论】:
我的问题是我两次调用setFlags
基本上重置了第一个标志。我不得不改用addFlag
。
我这样做了,但我收到一条错误消息,提示“解析包时出现问题”。如果我使用公共目录,错误就消失了,但不幸的是我需要使用私有目录。
很好的答案 - 谢谢!值得一提的是,从 Oreo 开始,您还必须将 REQUEST_INSTALL_PACKAGES 权限添加到清单中。否则它只是默默地失败。
在 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);
记下<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
是 Android N(Nougot) 所必需的【参考方案2】:
我在调用start activity时遇到了这个问题。暂停我当前的activity后,它突然回来并调用了onResume。就像什么都没发生一样。我的问题是清单中的这个权限:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
几乎没有人提到这一点。所以记住这一点。 在 sdk >= 24 你需要使用 provider 因为它需要一个以 file:/// 开头的意图 在 sdk 24 以下,你应该给 uri 以 content:/// 开头 所以这就是为什么我们需要 sdk 24 及更高版本的文件提供程序。我认为我不需要为此编写任何代码,因为@just_user 已经写了正确的答案。
【讨论】:
我相信你的评论是相反的。对于 SDK >= 24,URI 需要以“content:///”开头。在 SDK 24 以下,它应该以 'file:///' 开头【参考方案3】:这可能是问题,你有
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
在你的例子中应该是
install.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
因为 install 是意图的名称。
【讨论】:
有什么适合你的吗?我无法与电子邮件应用程序共享我的文件 uri。 我已经从使用 file:/// 切换到 content:// 并且有一段时间无法从文件提供程序打开文件。这解决了我的问题。【参考方案4】:您应该注意,对于 API
setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive")
而不是单独设置数据和类型:
data = Uri.fromFile(apkFile)
type = "application/vnd.android.package-archive"
否则,你会得到 ActivityNotFoundException
【讨论】:
一直使用DataAndType
就没有问题了。以上是关于带有 Intent.VIEW_ACTION 的 Android 安装 apk 不适用于文件提供程序的主要内容,如果未能解决你的问题,请参考以下文章