双进程交互实现App自动重启

背景

你可能会好奇,有些手游(比如王者荣耀)是怎么实现资源更新后自动重启的?

这个体验确实不错,因为不需要用户手动点击桌面图标重启App,在一些数据恢复备份的场景中,很实用。比如,从云端拉取SQLite数据库db文件或一些prefs配置文件后,会直接覆盖到本地,但进程不重新启动的话,是不会生效的。

思路

基本思路其实很简单,利用Android应用对多进程的天然支持,来实现双进程互拉。

有的朋友一看见"双进程"、"互拉"这些词汇就会立马联想到保活,注意本文不是讲保活的哦。

为了方便讲解,我们定义主进程之外的另一个进程为 进程B。大致流程分这么几步:

  • 在主进程执行完一系列业务逻辑后,欲重启,先拉起进程B
  • 进程B启动后,主进程kill掉自己
  • 接着,进程B拉起主进程,然后再kill掉自己,此时主进程完成自动重启

实现

先在Manifest中声明进程B,为了良好的交互体验,需要实现一个Activity,进程名称自定义,比如此处叫"killer",是不是很贴切?

xml 复制代码
        <activity
            android:name=".KillerActivity"
            android:exported="false"
            android:launchMode="singleTask"
            android:process=":killer" />

关键实现来了:

kotlin 复制代码
class KillerActivity : FragmentActivity() {

    companion object {
        private const val EXTRA_MAIN_PID = "extra_main_pid"

        // 当主进程需要重启时,就直接调用此方法启动KillerActivity
        fun launch(activity: FragmentActivity) {
            activity.startActivity(
                Intent(activity, KillerActivity::class.java).apply {
                    putExtra(EXTRA_MAIN_PID, Process.myPid())
                }
            )
            activity.finish() // 主进程的Activity先关闭
            GlobalScope.launch {
                // 稍作延迟后,主进程kill掉自己
                delay(500L)
                killProcess()
            }
        }

        fun killProcess(pid: Int = Process.myPid()) {
            Process.killProcess(pid)
            exitProcess(0)
        }

        fun isMainProcessAlive(context: Context): Boolean = runCatching {
            (context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager)
                .runningAppProcesses.find { it.processName == context.packageName } != null
        }.getOrDefault(false)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        lifecycleScope.launch {
            // 此处可以插入一些Loading的UI显示,比如ProgressDialog之类的
            // ...
            
            // 二次检查,防止主进程没杀掉
            doubleCheckMainProcess()
            // 稍作等待后,再次启动主进程的Activity
            startActivity(Intent(this, MainActivity::class.java).apply {
                addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
            })
            
            // 进程B的KillerActivity先关闭,再kill掉自己
            finish()
            killProcess()
        }
    }

    private suspend fun doubleCheckMainProcess() {
        delay(1500L)
        if (isMainProcessAlive(this)) {
            val mainPid = intent.getIntExtra(EXTRA_MAIN_PID, 0)
            if (mainPid != 0) {
                killProcess(mainPid)
                delay(1500L)
            }
        }
    }
}

整个代码非常简单,只要搞清楚 killProcess 方法哪些时候是kill的哪个进程,就理解了。在任意一个需要重启当前进程(可以不只是主进程)的地方,调用:

kotlin 复制代码
KillerActivity.launch(this)  

即可。

相关推荐
tongyiixiaohuang2 小时前
WMS已发货状态反写金蝶云星空:单策略实战教程
android
好的佩奇3 小时前
Dart 之类修饰符
android·flutter·dart
alexhilton4 小时前
用Konsist与Lint强制执行设计系统契约
android·kotlin·android jetpack
唐青枫7 小时前
Kotlin Delegation 委托详解
kotlin
北京自在科技8 小时前
谷歌安卓 Gemini 迎来界面改版,Find Hub 接入 Gemini 支持物品记忆查找
android·安卓
YB137510 小时前
Jetpack Compose 动画Modifier.animateItem()
android
2401_8858850411 小时前
巴林语音通知接口如何对接?系统告警语音API接入教程
android·前端·网络·python·语音识别
收纳过往12 小时前
数据库加索引的时机:慢查询日志说话
android·开发语言·数据库·python·低代码·rxjava
狂热开发者13 小时前
车载中控要给鸿蒙用户,能不能和 Android 共用一个二维码?
android·华为·harmonyos
.Bryce.14 小时前
T2-LLM评测不等于Agent评测
android·java·人工智能·后端·gpt·agent·1024程序员节