如何在 Android Oreo 中管理来自未知来源的安装?
How to manage installation from Unknown Sources in Android Oreo?
在 Android Oreo (8.0) 中,对如何允许安装来自未知来源的应用程序(从用户的角度)以及获取安装它们的权限的过程进行了一些更改(从开发者的角度来看)。
因为我发现在开发者这边特别难找到所有必要的步骤,所以我觉得在这里问解决方案并自己回答问题很有用,现在我找到了答案,以备将来参考给那些面临同样障碍的人。
答案将包括以下问题:
- 如何检查是否允许我请求软件包安装?
- 我必须请求什么确切的权限?
- 如何提示用户授予此权限?
- 如何提示用户安装指定的 .apk?
(如果我在这里仍然遗漏任何内容,我将不胜感激指出这一点的任何其他答案或评论。)
首先,您的申请需要在 build.gradle 或 AndroidManifest.xml 让所有这些都起作用。
接着回答上面的问题:
- How to check whether I'm allowed to request a package install?
您可以在您的 Activity 代码中的任何位置使用 getPackageManager().canRequestPackageInstalls()
进行检查。请注意,如果您未声明适当的权限或针对错误的 SDK 版本,此方法始终 returns false
。
- What exact permission do I have to request?
您必须在 AndroidManifest.xml 中声明 Manifest.permission.REQUEST_INSTALL_PACKAGES
,如下所示:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
- How can I prompt the user to grant this permission?
您可以使用 Intent ACTION_MANAGE_UNKNOWN_APP_SOURCES
:
将用户发送到适当的目的地
startActivity(new Intent(android.provider.Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES));
您还可以将用户更直接地转到您的应用程序的特定设置,方法是:
startActivity(new Intent(android.provider.Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:your.application.package")));
- How do I prompt the user to install a specified .apk?
一旦您确定您被授予适当的权限,您可以提示用户在您的 Activity 代码中的任何位置安装您的 .apk 文件(其中 this
指的是您的 Activity 的 Context
), 使用:
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setDataAndType(FileProvider.getUriForFile(this, "your.application.package.fileprovider", new File("/path/to/your/apk")), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
如果您想知道安装是成功、取消还是失败,您也可以添加intent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
并以startActivityForResult(Intent, int)
开头。
有关如何正确获取 .apk 文件的 URI 的信息,请参阅 FileProvider
。
在 adb shell 中,您可以使用 appops
管理应用的特定权限。
示例:
# To get a list of packagenames that is allowed to install from other sources
appops query-op REQUEST_INSTALL_PACKAGES allow
# To allow an app install packages from other sources
appops set <package name> REQUEST_INSTALL_PACKAGES deny
当 运行 adb shell appops help
:
时打印的帮助文本
AppOps service (appops) commands:
help
Print this help text.
set [--user <USER_ID>] <PACKAGE | UID> <OP> <MODE>
Set the mode for a particular application and operation.
get [--user <USER_ID>] <PACKAGE | UID> [<OP>]
Return the mode for a particular application and optional operation.
query-op [--user <USER_ID>] <OP> [<MODE>]
Print all packages that currently have the given op in the given mode.
reset [--user <USER_ID>] [<PACKAGE>]
Reset the given application or all applications to default modes.
write-settings
Immediately write pending changes to storage.
read-settings
Read the last written settings, replacing current state in RAM.
options:
<PACKAGE> an Android package name.
<OP> an AppOps operation.
<MODE> one of allow, ignore, deny, or default
<USER_ID> the user id under which the package is installed. If --user is not
specified, the current user is assumed.
在 Android Oreo (8.0) 中,对如何允许安装来自未知来源的应用程序(从用户的角度)以及获取安装它们的权限的过程进行了一些更改(从开发者的角度来看)。
因为我发现在开发者这边特别难找到所有必要的步骤,所以我觉得在这里问解决方案并自己回答问题很有用,现在我找到了答案,以备将来参考给那些面临同样障碍的人。
答案将包括以下问题:
- 如何检查是否允许我请求软件包安装?
- 我必须请求什么确切的权限?
- 如何提示用户授予此权限?
- 如何提示用户安装指定的 .apk?
(如果我在这里仍然遗漏任何内容,我将不胜感激指出这一点的任何其他答案或评论。)
首先,您的申请需要在 build.gradle 或 AndroidManifest.xml 让所有这些都起作用。
接着回答上面的问题:
- How to check whether I'm allowed to request a package install?
您可以在您的 Activity 代码中的任何位置使用 getPackageManager().canRequestPackageInstalls()
进行检查。请注意,如果您未声明适当的权限或针对错误的 SDK 版本,此方法始终 returns false
。
- What exact permission do I have to request?
您必须在 AndroidManifest.xml 中声明 Manifest.permission.REQUEST_INSTALL_PACKAGES
,如下所示:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
- How can I prompt the user to grant this permission?
您可以使用 Intent ACTION_MANAGE_UNKNOWN_APP_SOURCES
:
startActivity(new Intent(android.provider.Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES));
您还可以将用户更直接地转到您的应用程序的特定设置,方法是:
startActivity(new Intent(android.provider.Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:your.application.package")));
- How do I prompt the user to install a specified .apk?
一旦您确定您被授予适当的权限,您可以提示用户在您的 Activity 代码中的任何位置安装您的 .apk 文件(其中 this
指的是您的 Activity 的 Context
), 使用:
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setDataAndType(FileProvider.getUriForFile(this, "your.application.package.fileprovider", new File("/path/to/your/apk")), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
如果您想知道安装是成功、取消还是失败,您也可以添加intent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
并以startActivityForResult(Intent, int)
开头。
有关如何正确获取 .apk 文件的 URI 的信息,请参阅 FileProvider
。
在 adb shell 中,您可以使用 appops
管理应用的特定权限。
示例:
# To get a list of packagenames that is allowed to install from other sources
appops query-op REQUEST_INSTALL_PACKAGES allow
# To allow an app install packages from other sources
appops set <package name> REQUEST_INSTALL_PACKAGES deny
当 运行 adb shell appops help
:
AppOps service (appops) commands:
help
Print this help text.
set [--user <USER_ID>] <PACKAGE | UID> <OP> <MODE>
Set the mode for a particular application and operation.
get [--user <USER_ID>] <PACKAGE | UID> [<OP>]
Return the mode for a particular application and optional operation.
query-op [--user <USER_ID>] <OP> [<MODE>]
Print all packages that currently have the given op in the given mode.
reset [--user <USER_ID>] [<PACKAGE>]
Reset the given application or all applications to default modes.
write-settings
Immediately write pending changes to storage.
read-settings
Read the last written settings, replacing current state in RAM.
options:
<PACKAGE> an Android package name.
<OP> an AppOps operation.
<MODE> one of allow, ignore, deny, or default
<USER_ID> the user id under which the package is installed. If --user is not
specified, the current user is assumed.