You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Kotlin监听APK安装状态 跨页面同步方案咨询

解决Android全局监听APK安装状态的最优方案

针对你开发应用商店时遇到的「跨页面监听APK安装成功/失败/取消状态」问题,以下是两种最优实现方案,优先推荐使用官方现代API:

方案一:PackageInstaller API + 全局状态容器(ViewModel + LiveData)

PackageInstaller是Android官方推荐的安装API,能完整监听安装全流程(成功、失败、用户取消),结合全局ViewModel可实现跨页面状态同步。

1. 定义全局状态容器

创建一个全局ViewModel,用于保存和分发安装状态:

class AppViewModel(application: Application) : AndroidViewModel(application) {
    // 用LiveData分发安装状态
    val installStatus = MutableLiveData<InstallStatus>()

    // 密封类定义所有可能的安装状态
    sealed class InstallStatus {
        object Success : InstallStatus() // 安装成功
        object Canceled : InstallStatus() // 用户取消
        data class Failed(val errorCode: Int) : InstallStatus() // 安装失败(带错误码)
    }
}

在Application类中初始化全局ViewModel实例:

class MyApp : Application() {
    lateinit var appViewModel: AppViewModel

    override fun onCreate() {
        super.onCreate()
        appViewModel = ViewModelProvider(
            this,
            ViewModelProvider.AndroidViewModelFactory.getInstance(this)
        )[AppViewModel::class.java]
    }
}

2. 实现安装状态监听回调

创建PackageInstaller.SessionCallback监听安装全流程:

class InstallSessionCallback(
    private val appViewModel: AppViewModel,
    private val packageInstaller: PackageInstaller
) : PackageInstaller.SessionCallback() {
    override fun onCreated(sessionId: Int) {}
    override fun onBadgingChanged(sessionId: Int) {}
    override fun onActiveChanged(sessionId: Int, active: Boolean) {}
    override fun onProgressChanged(sessionId: Int, progress: Float) {}

    override fun onFinished(sessionId: Int, success: Boolean) {
        when {
            success -> appViewModel.installStatus.postValue(AppViewModel.InstallStatus.Success)
            else -> {
                val sessionInfo = packageInstaller.getSessionInfo(sessionId)
                // 判断是否为用户取消安装
                val isCanceled = sessionInfo?.isActive == false
                if (isCanceled) {
                    appViewModel.installStatus.postValue(AppViewModel.InstallStatus.Canceled)
                } else {
                    appViewModel.installStatus.postValue(AppViewModel.InstallStatus.Failed(-1))
                }
            }
        }
        // 监听完成后注销回调
        packageInstaller.unregisterSessionCallback(this)
    }
}

3. 发起APK安装请求

封装安装方法,写入APK文件并启动安装流程:

fun startApkInstall(apkUri: Uri, context: Context) {
    val packageInstaller = context.packageManager.packageInstaller
    val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
    val sessionId = packageInstaller.createSession(params)
    val session = packageInstaller.openSession(sessionId)

    // 将APK写入安装会话
    context.contentResolver.openInputStream(apkUri)?.use { inputStream ->
        session.openWrite("apk", 0, -1).use { outputStream ->
            inputStream.copyTo(outputStream)
            session.fsync(outputStream)
        }
    }

    // 注册安装状态回调
    val appViewModel = (context.applicationContext as MyApp).appViewModel
    val callback = InstallSessionCallback(appViewModel, packageInstaller)
    packageInstaller.registerSessionCallback(callback, Handler(Looper.getMainLooper()))

    // 发起安装请求
    val intent = Intent(context, InstallResultReceiver::class.java)
    val pendingIntent = PendingIntent.getBroadcast(
        context,
        sessionId,
        intent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
    )
    session.commit(pendingIntent.intentSender)
    session.close()
}

// 可选:用BroadcastReceiver补充接收安装结果(也可仅依赖SessionCallback)
class InstallResultReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        val status = intent?.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE)
        val appViewModel = (context?.applicationContext as MyApp).appViewModel
        when (status) {
            PackageInstaller.STATUS_SUCCESS -> {
                appViewModel.installStatus.postValue(AppViewModel.InstallStatus.Success)
            }
            PackageInstaller.STATUS_FAILURE_USER_CANCELED -> {
                appViewModel.installStatus.postValue(AppViewModel.InstallStatus.Canceled)
            }
            else -> {
                val errorCode = intent?.getIntExtra(PackageInstaller.EXTRA_STATUS_CODE, -1) ?: -1
                appViewModel.installStatus.postValue(AppViewModel.InstallStatus.Failed(errorCode))
            }
        }
    }
}

4. 在任意页面监听状态更新

在需要更新UI的Activity/Fragment中观察ViewModel的状态:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val appViewModel = (application as MyApp).appViewModel
    appViewModel.installStatus.observe(this) { status ->
        when (status) {
            is AppViewModel.InstallStatus.Success -> {
                // 处理安装成功UI更新,比如弹窗、刷新列表
                Toast.makeText(this, "APK安装成功", Toast.LENGTH_SHORT).show()
            }
            is AppViewModel.InstallStatus.Canceled -> {
                Toast.makeText(this, "用户取消安装", Toast.LENGTH_SHORT).show()
            }
            is AppViewModel.InstallStatus.Failed -> {
                Toast.makeText(this, "安装失败,错误码:${status.errorCode}", Toast.LENGTH_SHORT).show()
            }
        }
    }
}

方案二:registerForActivityResult + 全局ViewModel(兼容旧API)

如果暂时无法迁移到PackageInstaller,可使用替代方案:用registerForActivityResult(替代已废弃的startActivityForResult)发起安装,再通过全局ViewModel分发结果。

1. 发起安装请求

在任意页面注册安装请求的Launcher:

private val installLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
    val appViewModel = (application as MyApp).appViewModel
    when (result.resultCode) {
        Activity.RESULT_OK -> {
            appViewModel.installStatus.postValue(AppViewModel.InstallStatus.Success)
        }
        Activity.RESULT_CANCELED -> {
            appViewModel.installStatus.postValue(AppViewModel.InstallStatus.Canceled)
        }
        else -> {
            appViewModel.installStatus.postValue(AppViewModel.InstallStatus.Failed(result.resultCode))
        }
    }
}

// 启动安装的方法
fun startApkInstall(apkUri: Uri) {
    val intent = Intent(Intent.ACTION_INSTALL_PACKAGE).apply {
        data = apkUri
        flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
    }
    installLauncher.launch(intent)
}

2. 监听状态更新

和方案一一样,在任意Activity/Fragment中观察appViewModel.installStatus即可更新UI。

关键注意事项

  • 权限处理:确保APK Uri有正确的读取权限,Android 10+需注意存储权限或使用MediaStore获取文件。
  • PendingIntent Flags:Android 12+必须添加FLAG_IMMUTABLE或FLAG_MUTABLE,避免崩溃。
  • 页面生命周期:使用LiveData自动处理生命周期,避免内存泄漏。

内容的提问来源于stack exchange,提问作者HieuNguyen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 11:09:58