You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何无需Google Play Store实现APP自动更新与静默安装?

嘿,这个场景我刚好帮企业客户落地过,完全适配你的Kiosk设备私有托管自动更新需求,分两部分解决你的核心痛点:版本检测触发更新,以及无用户干预的静默安装,一步步给你讲清楚:

一、版本检测:让APP知道有新版本可用

这里给你两种实用方案,按需选择:

1. 定时拉取版本配置文件(最通用)

思路很简单:在客户的私有网站上放一个轻量的版本配置文件(比如version.json),里面存好最新版本的关键信息,APP定期去拉取对比,发现新版本就触发下载。

  • 配置文件示例:
    {
      "version_code": 2,
      "version_name": "1.1.0",
      "apk_url": "https://客户私有域名/app/release/latest.apk",
      "apk_sha256": "xxxxxx..." // 可选,用于校验APK完整性
    }
    
  • APP端实现(Kotlin示例):
    可以用WorkManager做周期性任务(比如每6小时检查一次),或者APP前台常驻时在后台Service里触发:
    // 拉取并对比版本
    fun checkRemoteVersion() {
        val client = OkHttpClient()
        val request = Request.Builder().url("https://客户私有域名/version.json").build()
        client.newCall(request).enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                // 网络异常,记录日志下次重试
            }
    
            override fun onResponse(call: Call, response: Response) {
                response.body?.string()?.let { jsonStr ->
                    val json = JSONObject(jsonStr)
                    val remoteVersionCode = json.getInt("version_code")
                    val localVersionCode = BuildConfig.VERSION_CODE
    
                    if (remoteVersionCode > localVersionCode) {
                        // 触发下载流程
                        startDownloadApk(json.getString("apk_url"))
                    }
                }
            }
        })
    }
    
  • 注意:Kiosk模式下APP通常是前台常驻,用WorkManager能确保APP被系统回收后仍能执行检测任务。

2. 推送触发更新(更及时)

如果客户有自己的企业推送服务,新版本上线时直接给所有设备发推送,APP收到推送后立刻启动下载,不用等定时检测,适合紧急版本发布。

二、无用户干预的自动安装(核心难点)

这部分要重点关注Android版本权限差异,以及Kiosk模式的特殊权限优势:

1. 先搞定必要权限

AndroidManifest.xml里添加权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <!-- 可选,确保开机后能继续检测 -->
<!-- Android 10+ 不需要WRITE_EXTERNAL_STORAGE,用应用外部私有目录即可 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
  • 关键提醒:Kiosk设备通常会被设置为设备所有者(这是企业管控Kiosk的常规操作),我们可以用设备所有者权限自动授予REQUEST_INSTALL_PACKAGES,不用用户手动开启:
    val dpm = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
    val adminComponent = ComponentName(this, YourDeviceAdminReceiver::class.java)
    if (dpm.isAdminActive(adminComponent)) {
        // 自动授予安装权限
        dpm.setPermissionGrantState(
            adminComponent,
            packageName,
            Manifest.permission.REQUEST_INSTALL_PACKAGES,
            DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED
        )
    }
    

2. 静默安装实现(无用户确认)

普通APP做不到无交互安装,但Kiosk作为设备所有者应用,可以调用PackageInstaller实现静默安装,这是核心解决方案:

private fun installApkSilently(apkFile: File) {
    val packageInstaller = packageManager.packageInstaller
    val sessionParams = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
    val sessionId = packageInstaller.createSession(sessionParams)
    val session = packageInstaller.openSession(sessionId)

    // 把下载好的APK写入安装会话
    val inputStream = FileInputStream(apkFile)
    val outputStream = session.openWrite("update_apk", 0, -1)
    inputStream.copyTo(outputStream)
    session.fsync(outputStream)
    inputStream.close()
    outputStream.close()

    // 提交安装请求,用BroadcastReceiver接收结果
    val intent = Intent(this, InstallResultReceiver::class.java)
    val pendingIntent = PendingIntent.getBroadcast(
        this,
        0,
        intent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
    )
    session.commit(pendingIntent.intentSender)
    session.close()
}

// 接收安装结果的广播接收器
class InstallResultReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        val status = intent?.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE)
        when (status) {
            PackageInstaller.STATUS_SUCCESS -> {
                // 安装成功,自动重启APP
                restartApp(context)
            }
            else -> {
                val errorMsg = intent?.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)
                // 记录失败日志,下次检测时重试
            }
        }
    }

    private fun restartApp(context: Context?) {
        val intent = context?.packageManager?.getLaunchIntentForPackage(context.packageName)
        intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
        context?.startActivity(intent)
        // 杀死当前进程
        android.os.Process.killProcess(android.os.Process.myPid())
    }
}
三、完整流程梳理
  1. 首次安装后,APP启动时立刻检查服务器版本配置
  2. 发现新版本,后台下载APK到应用外部私有目录(比如getExternalFilesDir("apk")
  3. 下载完成后校验APK哈希值(可选),然后调用静默安装
  4. 安装成功后自动重启APP,完成更新
  5. 后台周期性检查版本,确保不会错过后续更新
四、额外注意事项
  • APK完整性校验:下载后对比服务器返回的SHA256值,避免下载损坏或被篡改的文件
  • 断点续传:用OkHttp的断点续传功能,避免网络中断后重新下载整个APK
  • 存储空间检查:下载前确认设备剩余空间足够,避免安装失败
  • 日志记录:把版本检测、下载、安装的全流程日志上传到客户的后台,方便排查问题

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

火山引擎 最新活动