为每个 Android 版本安装 APK 的正确方法是什么?

Cod*_*Lee 6 android apk android-package-managers android-fileprovider

我正在制作一个 APK 安装应用程序,并且经历了很多。特别是版本控制。为此,我查阅了一些帖子和 StackOverFlow。但现在所有的帖子让我更加困惑。那么,什么是正确的方法呢?

首先,请检查我下面关于每个版本安装的代码。让我们谈谈什么是正确的方法。

KitKat(API 19)、棒棒糖(API 21)、MashMellow(API 23)

fun installApkBelowNougat(apkFile: File) {
  val apkUri = Uri.fromFile(apkFile)
  val intent = Intent(Intent.ACTION_VIEW).apply {
    setDataAndType(apkUri, "application/vnd.android.package-archive")
    flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
  }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,所有解决方案都是相同的,我们必须使用Uri.fromFile(file)来获得Uri.

牛轧糖(API 24)

fun installApkInNougat(apkFile: File) {
  val apkUri = FileProvider.getUriForFile(applicationContext, applicationContext.packageName + ".fileProvider", apkFile)
  val intent = Intent(Intent.ACTION_VIEW).apply {
    setDataAndType(apkUri, "application/vnd.android.package-archive")
    flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
    addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
  }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我们就不能再使用了Uri.fromFile(file)。因此,我们必须使用FileProvider“Nougat”来代替它。并且我们还必须<proivder/>在AndroidManifest.xml中这样写。

...
<provider
  android:name="androidx.core.content.FileProvider"
  android:authorities="${applicationId}.fileProvider"
  android:exported="false"
  android:grantUriPermissions="true">
     <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/filepath" />
     </provider>
...
Run Code Online (Sandbox Code Playgroud)

有人说我们不需要使用setDataAndType(). 相反,他们只是setData()在 Android7(Nougat) 中使用它。哪一种是正确的方法?两者都好吗?我们需要设置一个 Flag Intent.FLAG_GRANT_READ_URI_PERMISSION
编辑:您可以在此处找到何时使用setDataAndType()或仅使用两者之一。

在此输入图像描述

奥利奥(API 26)、派(API 28)、Q(API 29)、R(API 30)

fun installApkMoreThanO(apkFile: File) {
  f (!this.packageManager.canRequestPackageInstalls()) {
            val unknownIntent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).setData(
                Uri.parse(String.format("package:%s", this.packageName))
            )
            resultLauncher.launch(unknownIntent)
        } else {
            val apkUri = FileProvider.getUriForFile(applicationContext, applicationContext.packageName + ".fileProvider", apkFile)
            val intent = Intent(Intent.ACTION_VIEW, apkUri).apply {
                putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
                setDataAndType(apkUri, "application/vnd.android.package-archive")
                flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
                addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            }
            startActivity(intent)
            finish()
        }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,从 Oreo,我们必须添加此权限<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />才能安装 APK 文件。如果不这样做,应用程序就会毫无征兆地崩溃。而我们必须使用Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,因为自Android8(Oreo)以来,授予权限的方式已经发生了变化。在 Oreo 之前,我们可以一次性授予所有应用程序权限。但自 Oreo 以来,我们必须单独为每个应用程序授予权限。

我们可以使用registerActivitResult许可的结果。这意味着如果您到目前为止从未允许过该应用程序的权限,则必须允许该权限,并且允许后系统返回Activity.RESULT_OK或Activity.RESULT_CANCELED。然后你就可以做你想做的事了。

S(API 31)

fun installApkMoreThanR(apkFile: File) {
 val apkUri = FileProvider.getUriForFile(applicationContext, applicationContext.packageName + ".fileProvider", apkFile)
 val intent = Intent(Intent.ACTION_VIEW, apkUri).apply {
     putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false)
     setDataAndType(apkUri, "application/vnd.android.package-archive")
     flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
     addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
   }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,看起来我回到了有点旧的方式,但它确实有效并且很好。与Android 8~10的方式结果相同。我不使用的原因是,即使我们授予应用程序权限,Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES)我们也只能接收。它总是在 Android12 中RESULT_CANCELED返回。Activity.RESULT_CANCELED我不知道为什么。有些人说我们必须使用PackageInstallerAPI,因为ACTION_VIEWACTION_INSTALL_PACKAGE在 Android 10 中已弃用。但我测试了它,并ACTION_VIEW没有弃用。但哪种方式更好?从Android 10开始我们必须使用PackageInstallerAPI​​吗?