如何在安卓Oreo中管理来自未知来源的应用程序安装?

26

在Android Oreo(8.0)中,对于允许从未知来源安装应用程序的方式进行了更改(从用户的角度),以及获取安装权限的过程也发生了变化(从开发者的角度)。

由于我发现在开发者方面需要找到所有必要步骤特别困难,因此我认为在这里寻求解决方案并回答这个问题对于那些遇到相同障碍的人们而言将会很有用。

答案将包括以下问题:

  1. 如何检查是否允许请求包安装?
  2. 我需要请求什么确切的权限?
  3. 如何提示用户授予此权限?
  4. 如何提示用户安装指定的.apk文件?

(如果我还有任何遗漏,请感谢任何额外的答案或评论指出。)


你好,感谢回答。我有一个问题。我的 targetSdkVersion 是 22,而 compileSdkVersion 是 26。我该如何检查这种情况下的 getPackageManager().canRequestPackageInstalls()。该方法总是返回 false。 - Chirag Savsani
@Chirag Savsani:你不能这样做。正如我在下面的答案中所述,你必须声明一个目标SDK版本为26或更高版本。否则,该方法始终返回false-这是预期的行为。所以恐怕你无法避免对应用程序进行更高的目标SDK版本所需的更改。 - Patneu
2个回答

68

首先,在您的build.gradle或AndroidManifest.xml中声明一个26或更高版本(Android Oreo的API级别)的targetSdkVersion,所有这些工作都需要。

接下来回答上面的问题:

  1. 如何检查是否允许请求包安装?

您可以在Activity的任何代码中使用getPackageManager().canRequestPackageInstalls()进行检查。请注意,如果您没有声明适当的权限或目标错误的SDK版本,则此方法始终返回false

  1. 我必须请求什么确切的权限?

您必须在AndroidManifest.xml中声明Manifest.permission.REQUEST_INSTALL_PACKAGES,如下所示:

    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
  1. 我该如何提示用户授权此权限?

您可以使用IntentACTION_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")));
  1. 如何提示用户安装指定的 .apk 文件?

一旦您确认已被授予适当的权限,您可以在 Activity 的任何代码中(其中 this 指向您的 Activity 的 Context),使用以下代码提示用户安装您的 .apk 文件:

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


我已经开发了一个Android应用程序。例如,我的Android应用程序名称是MobileShopping应用程序。我尝试在Oreo移动设备上安装此MobileShopping应用程序。在MobileShopping应用程序内部,我有一个支付SDK,可以实现银行交易。在Oreo设备上,即使安装了MobileBanking应用程序,它仍然要求获得安装第三方应用程序的权限,即已经内置在MobileShopping应用程序中的支付SDK(根据开发)的权限。 - Achiever
1
我需要在Oreo设备上第一次尝试安装MobileShopping应用程序时仅向用户显示已授予权限的对话框。我不想向用户显示权限对话框或请求任何权限,即使是MobileShopping应用程序中存在的第三方应用程序访问。 - Achiever
“MobileShopping” 应用是我独自开发的应用程序。首先如何安装呢?- 通过将 APK 从系统文件夹传输到移动设备,通过 USB 安装。另外,“Payment SDK” 是其他人开发的单独的应用程序,当我尝试构建 “MobileShopping” 应用程序时会编译其 APK,并作为 PaymentSdk.apk 存储在资产文件夹中。 - Achiever
为什么你必须事后安装它,而不是将其编译为“移动购物”应用程序的依赖项?- 是的,怎么做呢?有任何想法吗? - Achiever
@Anish Kumar:据我所知,你不能直接得到用户是否打开了权限的反馈,因为你的Intent仅向用户显示适当的设置,他并不强制执行任何操作(因此,一个Toast解释给用户实际要在那里做什么可能会有用)。如果你想知道你是否现在被授予权限,你只需要在用户返回你的Activity时再次检查,就像第一步一样。 - Patneu
显示剩余8条评论

6

您可以使用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应用程序执行appops set <package name> REQUEST_INSTALL_PACKAGES allow操作? - Daniele

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接