有这样的需求, 应用上架googlePlay权限审批没通过,需要分为两个版本,normal版本升级方案为原有的升级方案, 调用安装新版本apk需要 android.permission.REQUEST_INSTALL_PACKAGES 这个权限。 而Google Play升级不能有这个权限,需要去掉这个权限。 所以需要打两个渠道包,一个normal版本,一个Google Play版本。
app/build.gradle.kts中增加如下渠道配置:

另外, Google play升级的依赖:

app/build.gradle.kts完整代码如下:
Groovy
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.compose)
}
android {
// 1. 定义维度名称。
// flavorDimensions用于声明有哪些分类维度,它将所有的 productFlavors 划分为不同的组。每个组代表一个独立的维度。
flavorDimensions += "app_channel"
// 2. 配置产品变种
productFlavors {
create("normal") {
// dimension 是在具体的 productFlavor 内部使用的属性,用于指定当前这个 Flavor 属于哪一个预先声明好的维度
dimension = "app_channel"
// 注意:buildConfigField 的参数类型和值需要符合 Kotlin 字符串规范
// "false" 是字符串类型的值,传递给 Java/Kotlin 代码时会变成 boolean false
buildConfigField("boolean", "IS_GOOGLE_PLAY", "false")
}
create("googlePlay") {
dimension = "app_channel"
buildConfigField("boolean", "IS_GOOGLE_PLAY", "true")
}
}
// 在这里添加配置,开启 BuildConfig 自动生成
buildFeatures {
buildConfig = true
}
namespace = "com.example.androiddemos"
compileSdk {
version = release(36)
}
ndkVersion = "28.2.13676358"
externalNativeBuild {
cmake {
path = file("src/main/cpp/CMakeLists.txt")
version = "3.22.1"
}
}
defaultConfig {
applicationId = "com.example.androiddemos"
minSdk = 29
targetSdk = 36
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
ndk {
abiFilters.addAll(listOf("arm64-v8a", "armeabi-v7a"))
}
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
buildFeatures {
compose = true
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.graphics)
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.compose.material3)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
debugImplementation(libs.androidx.compose.ui.tooling)
debugImplementation(libs.androidx.compose.ui.test.manifest)
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("androidx.constraintlayout:constraintlayout:2.2.1")
implementation("androidx.camera:camera-core:1.4.0")
implementation("androidx.camera:camera-camera2:1.4.0")
implementation("androidx.camera:camera-lifecycle:1.4.0")
implementation("androidx.camera:camera-view:1.4.0")
// implementation("androidx.camera:camera-view-ktx:1.4.0")
// 仅 googlePlay 渠道编译时包含此依赖. 前面是渠道名,如果仅normal渠道包含某依赖,则写normalImplementation(...)
"googlePlayImplementation"("com.google.android.play:app-update:2.1.0")
// 如果用 Kotlin 扩展库也同理
"googlePlayImplementation"("com.google.android.play:app-update-ktx:2.1.0")
}
main资源目录的一个包里面新增统一的升级接口:
Kotlin
package com.example.androiddemos.update
import android.app.Activity
/**
* 检查版本升级
*/
interface IUpdateChecker {
fun check(activity: Activity)
}
新增Google play渠道资源目录,并在其中新增升级实现类以及AndroidManifest文件:

其中AndroidManifest内容如下:
XML
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- 强制移除该权限,防止AAR/sdk依赖带入REQUEST_INSTALL_PACKAGES,GP上架保护 -->
<uses-permission
android:name="android.permission.REQUEST_INSTALL_PACKAGES"
tools:node="remove" />
</manifest>
UpdaterImpl代码如下:
Kotlin
package com.example.androiddemos.update
import android.app.Activity
import android.app.AlertDialog
import android.util.Log
import com.example.androiddemos.MainActivity
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
import com.google.android.play.core.appupdate.AppUpdateOptions
import com.google.android.play.core.install.model.AppUpdateType
import com.google.android.play.core.install.model.UpdateAvailability
class UpdaterImpl(private val activity: Activity) : IUpdateChecker {
override fun check(activity: Activity) {
Log.d("Updater", "google play channel check update")
val appUpdateManager = AppUpdateManagerFactory.create(activity)
appUpdateManager.appUpdateInfo.addOnSuccessListener { info ->
if (info.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& info.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
) {
// 弹框
AlertDialog.Builder(activity)
.setTitle("发现新版本")
.setMessage("检测到新版本,是否立即更新?")
.setPositiveButton("确定") { _, _ ->
val options = AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build()
appUpdateManager.startUpdateFlowForResult(
info,
activity,
options,
MainActivity.REQUEST_CODE_GP_UPDATE
)
}
.setNegativeButton("取消", null)
.show()
}
}.addOnFailureListener { e ->
Log.e("Update", "GP check failed", e)
}
}
}
MainActivity加上常量 REQUEST_CODE_GP_UPDATE,以及onActivityResult响应升级结果:

这个是按钮,点击就调用UpdaterImpl的check方法检查版本升级:

同样,normal渠道包也是新建以下同样结构(注意,UpdaterImpl的类名以及包名需要和Google play渠道的完全一致,否则编译不过 ):

其中AndroidManifest内容如下:
XML
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
</manifest>
UpdaterImpl代码如下:
Kotlin
package com.example.androiddemos.update
import android.app.Activity
import android.util.Log
class UpdaterImpl(private val activity: Activity) : IUpdateChecker {
override fun check(activity: Activity) {
// 使用非GP的更新逻辑
Log.d("Updater", "Normal channel does not support in-app updates")
// 待实现 ... ...
}
}
ok. 开始打Google play渠道包:


运行,点击检查升级:

日志:
bash
D google play channel check update
2026-10-05 20:58:48.681 15900-15900 PlayCore com.example.androiddemos E UID: [10404] PID: [15900] AppUpdateService : onError(-9)
2026-10-05 20:58:48.695 15900-15900 Update com.example.androiddemos E GP check failed
com.google.android.play.core.install.InstallException: -9: Install Error(-9): The Play Store app is either not installed or not the official version. (https://developer.android.com/reference/com/google/android/play/core/install/model/InstallErrorCode#ERROR_PLAY_STORE_NOT_FOUND)
at com.google.android.play.core.appupdate.zzr.zzj(com.google.android.play:app-update@@2.1.0:2)
at com.google.android.play.core.appupdate.zzr.zze(com.google.android.play:app-update@@2.1.0:1)
at com.google.android.play.core.appupdate.zzg.getAppUpdateInfo(com.google.android.play:app-update@@2.1.0:1)
at com.example.androiddemos.update.UpdaterImpl.check(UpdaterImpl.kt:16)
at com.example.androiddemos.MainActivityKt.Greeting$lambda$6$lambda$5$lambda$4(MainActivity.kt:92)
at com.example.androiddemos.MainActivityKt$$ExternalSyntheticLambda3.invoke(D8$$SyntheticClass:0)
at androidx.compose.foundation.ClickableNode.onPointerEvent-H0pRuoY(Clickable.kt:1009)
at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:436)
at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:422)
at androidx.compose.ui.input.pointer.NodeParent.dispatchMainEventPass(HitPathTracker.kt:275)
at androidx.compose.ui.input.pointer.HitPathTracker.dispatchChanges(HitPathTracker.kt:171)
at androidx.compose.ui.input.pointer.PointerInputEventProcessor.process-BIzXfog(PointerInputEventProcessor.kt:118)
at androidx.compose.ui.platform.AndroidComposeView.sendMotionEvent-8iAsVTc(AndroidComposeView.android.kt:2428)
at androidx.compose.ui.platform.AndroidComposeView.handleMotionEvent-8iAsVTc(AndroidComposeView.android.kt:2378)
at androidx.compose.ui.platform.AndroidComposeView.dispatchTouchEvent(AndroidComposeView.android.kt:2249)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3343)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:3004)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3343)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:3004)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3343)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:3004)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3343)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:3004)
at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:898)
at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:2077)
at android.app.Activity.dispatchTouchEvent(Activity.java:4516)
at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:817)
at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:805)
at android.view.View.dispatchPointerEvent(View.java:15273)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:8511)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:8245)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7594)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7651)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7617)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:7799)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:7625)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:7856)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7598)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7651)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7617)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:7625)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7598)
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:11132)
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:11034)
2026-10-05 20:58:48.695 15900-15900 Update com.example.androiddemos E at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:10987)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:11385)
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:310)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:374)
at android.os.Looper.loopOnce(Looper.java:163)
at android.os.Looper.loop(Looper.java:293)
at android.app.ActivityThread.loopProcess(ActivityThread.java:10170)
at android.app.ActivityThread.main(ActivityThread.java:10159)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:593)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1247)
异常意思是没安装Google应用商店。环境准备好才能正常检查升级。
再看下apk,其中没有 REQUEST_INSTALL_PACKAGES 权限:

同理,打下normal包,看下:

ok。有安装apk权限。 运行点击检查升级:

ok.