Android 安装 apk Intent.VIEW_ACTION 不使用文件提供程序
Android install apk with Intent.VIEW_ACTION not working with File provider
我的应用程序具有自动更新功能,可以下载 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(安装);抛出错误
No Activity found to handle 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 查看器?
这可能是问题所在,您有
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
在你的例子中它应该是
install.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
因为安装是意图的名称。
经过大量尝试,我已经能够通过为低于 Nougat 的任何东西创建不同的 Intent 来解决这个问题,因为使用 FileProvider 创建具有 Android Nougat 之前的版本的安装意图导致错误:
ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.INSTALL_PACKAGE dat=content://XXX.apk flg=0x1 }
在 Android 牛轧糖上使用普通 Uri 时会产生以下错误:
FileUriExposedException: file:///XXX.apk exposed beyond app through Intent.getData()
我的解决方案适用于模拟器上的 Android N 和 phone 运行 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牛轧糖 7.1 更新:
您还需要在清单中添加权限 REQUEST_INSTALL_PACKAGES。它从 Api 23 级(Android 6.0 棉花糖)开始可用,从 25 级(Android 7.1 牛轧糖)开始需要。
更新:
如果您尝试安装的文件在外部存储上,请记住请求对外部存储的读写权限。还要为 Android 牛轧糖及更高版本设置正确的 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 时遇到类似“解析包时出现问题”之类的错误。可能是您没有请求 read/write 权限,或者您尝试安装的文件不存在或已损坏。
更新 Android 奥利奥 8.0:
您必须检查是否允许当前应用程序在 Android Oreo 8.0 或更高版本上安装 APK。
您可以检查是否允许您的应用安装 APK,方法是使用 canRequestPackageInstalls method of PackageManager class. If it returns false, then you can launch intent with 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);
}
确保将以下代码添加到 activity 以接收意图结果。
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 < 24,您需要使用:
setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive")
而不是单独设置数据和类型:
data = Uri.fromFile(apkFile)
type = "application/vnd.android.package-archive"
否则,您将得到 ActivityNotFoundException
我在调用 start activity.after 暂停当前 activity 时遇到了这个问题,它突然回来并调用了 onResume。就像什么都没发生一样。我的问题是清单中的此权限:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
几乎没有人提到这一点。所以记住这一点。
在 sdk >= 24 你需要使用 provider 因为它需要一个以 file:/// 开头的意图
在 sdk 24 以下你应该给 uri 以 content:/// 开头
所以这就是为什么我们需要 sdk 24 及更高版本的文件提供程序。我认为我不需要为此编写任何代码,因为@just_user 已经写出了正确的答案。
https://stacklearn.ir
致所有面临“解析此包时出现问题”问题的人。
手动设置权限。
ContextCompat.checkSelfPermission(getApplicationContext(), READ_EXTERNAL_STORAGE);
我的应用程序具有自动更新功能,可以下载 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(安装);抛出错误
No Activity found to handle 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 查看器?
这可能是问题所在,您有
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
在你的例子中它应该是
install.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
因为安装是意图的名称。
经过大量尝试,我已经能够通过为低于 Nougat 的任何东西创建不同的 Intent 来解决这个问题,因为使用 FileProvider 创建具有 Android Nougat 之前的版本的安装意图导致错误:
ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.INSTALL_PACKAGE dat=content://XXX.apk flg=0x1 }
在 Android 牛轧糖上使用普通 Uri 时会产生以下错误:
FileUriExposedException: file:///XXX.apk exposed beyond app through Intent.getData()
我的解决方案适用于模拟器上的 Android N 和 phone 运行 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牛轧糖 7.1 更新:
您还需要在清单中添加权限 REQUEST_INSTALL_PACKAGES。它从 Api 23 级(Android 6.0 棉花糖)开始可用,从 25 级(Android 7.1 牛轧糖)开始需要。
更新:
如果您尝试安装的文件在外部存储上,请记住请求对外部存储的读写权限。还要为 Android 牛轧糖及更高版本设置正确的 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 时遇到类似“解析包时出现问题”之类的错误。可能是您没有请求 read/write 权限,或者您尝试安装的文件不存在或已损坏。
更新 Android 奥利奥 8.0:
您必须检查是否允许当前应用程序在 Android Oreo 8.0 或更高版本上安装 APK。
您可以检查是否允许您的应用安装 APK,方法是使用 canRequestPackageInstalls method of PackageManager class. If it returns false, then you can launch intent with 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);
}
确保将以下代码添加到 activity 以接收意图结果。
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 < 24,您需要使用:
setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive")
而不是单独设置数据和类型:
data = Uri.fromFile(apkFile)
type = "application/vnd.android.package-archive"
否则,您将得到 ActivityNotFoundException
我在调用 start activity.after 暂停当前 activity 时遇到了这个问题,它突然回来并调用了 onResume。就像什么都没发生一样。我的问题是清单中的此权限:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
几乎没有人提到这一点。所以记住这一点。 在 sdk >= 24 你需要使用 provider 因为它需要一个以 file:/// 开头的意图 在 sdk 24 以下你应该给 uri 以 content:/// 开头 所以这就是为什么我们需要 sdk 24 及更高版本的文件提供程序。我认为我不需要为此编写任何代码,因为@just_user 已经写出了正确的答案。 https://stacklearn.ir
致所有面临“解析此包时出现问题”问题的人。
手动设置权限。
ContextCompat.checkSelfPermission(getApplicationContext(), READ_EXTERNAL_STORAGE);