Android保活策略

tech2026-01-03  8

进程的划分

一、前台进程 相关场景: (1)某个进程持有一个正在与用户交互的Activity并且该Activity正处于resume状态 (2)某个进程持有一个Service,并且该Service调用startForeground()方法使之位于前台运行 (3)某个进程持有一个BroadcastReceiver,并且该BroadcastReceiver正在执行其onReceive()方法

用户正在使用的程序,一般系统是不会杀死前台进程的,除非用户强制停止应用或者系统内存不足等极端情况会杀死

二、可见进程 相关场景: (1)拥有不在前台、但仍对用户可见的 Activity(已调用 onPause()) (2)拥有绑定到可见Activity 的 Service

用户正在使用,看得到,但是摸不着,没有覆盖到整个屏幕,可见进程不包含任何前台组件,一般系统也是不会杀死可见进程的,除非要在资源吃紧的情况下

三、服务进程 相关场景:某个进程中运行着一个Service且该Service是通过startService()启动的,与用户看见的界面没有直接关联

在内存不足以维持所有前台进程和可见进程同时运行的情况下,服务进程会被杀死

四、后台进程 相关场景:在用户按了"back"或者"home"后,程序本身看不到了,但是其实还在运行的程序,比如Activity调用了onPause方法

系统可能随时终止它们,回收内存

五、空进程

某个进程不包含任何活跃的组件时该进程就会被置为空进程,完全没用,第一个干它

内存阈值

App在退到后台时系统并不会真正的杀掉这个进程,而是将其缓存起来。打开的应用越多,后台缓存的进程也越多。在系统内存不足的情况下,系统开始依据自身的一套进程回收机制来判断要杀掉哪些进程,以腾出内存来供给需要的App,这套进程回收内存的机制就叫 Low Memory Killer。那这个不足怎么来规定呢?那就是内存阈值,内存阈值在不同的手机上不一样,一旦低于该值,Android便开始按顺序关闭进程,Android开始结束优先级最低的空进程,进程是有它的优先级的,这个优先级通过进程的adj值来反映,它是linux内核分配给每个系统进程的一个值,代表进程的优先级,进程回收机制就是根据这个优先级来决定是否进行回收

oom_adj越大,占用物理内存越多,会被最先杀掉

进程保活方案

其实,要做到完全保活是不现实的,谷歌不会让你这么做的。在Android5.0以后,在应用退出后,ActivityManagerService不仅把主进程给杀死,另外把主进程所属的进程组一并杀死,这样一来,由于子进程和主进程在同一进程组,子进程在做的事情也就停止了

一、双进程守护(看看就好,实测无效)

双进程守护的思想就是,两个进程共同运行,如果有其中一个进程被杀,那么另一个进程就会将被杀的进程重新拉起,相互保护,在一定的意义上,维持进程的不断运行 首先MainActivity页面很简单,就三个按钮,用于启动停止服务,模拟进程被kill

class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setListener() } private fun setListener() { btn1.setOnClickListener { startService(Intent(this, LocalService::class.java)) } btn2.setOnClickListener { stopService(Intent(this, LocalService::class.java)) } btn3.setOnClickListener { stopService(Intent(this, GuardService::class.java)) } } }

新建AIDL,用于进程间的交互,注意此处要Rebuild

interface IMyAidlInterface { String getServiceName(); }

本地进程服务

class LocalService : Service() { private val connection: ServiceConnection by lazy { object : ServiceConnection { override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) { val aidlInterface = IMyAidlInterface.Stub.asInterface(p1) try { Log.i("LocalService", "connected with" + aidlInterface.serviceName) } catch (e: RemoteException) { e.printStackTrace() } } override fun onServiceDisconnected(p0: ComponentName?) { Toast.makeText(this@LocalService, "守护进程连接断开,重新启动守护", Toast.LENGTH_SHORT).show() startService(Intent(this@LocalService, GuardService::class.java)) bindService( Intent(this@LocalService, GuardService::class.java), connection, Context.BIND_IMPORTANT ) } } } override fun onBind(intent: Intent): IBinder { return MyBinder() } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { Toast.makeText(this, "本地服务启动", Toast.LENGTH_SHORT).show() startService(Intent(this@LocalService, GuardService::class.java)) bindService(Intent(this, GuardService::class.java), connection, BIND_IMPORTANT) return START_STICKY } class MyBinder : IMyAidlInterface.Stub() { @Throws(RemoteException::class) override fun getServiceName(): String { return LocalService::class.java.name } } }

接下来是守护进程,需要注意的是,守护进程需要跑在另一个进程

<service android:name=".GuardService" android:enabled="true" android:exported="true" android:process=":RemoteProcess"/> class GuardService : Service() { private val connection: ServiceConnection by lazy { object : ServiceConnection { override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) { val aidlInterface = IMyAidlInterface.Stub.asInterface(p1) try { Log.i("LocalService", "connected with" + aidlInterface.serviceName) } catch (e: RemoteException) { e.printStackTrace() } } override fun onServiceDisconnected(p0: ComponentName?) { Toast.makeText(this@GuardService, "守护进程连接断开,重新启动本地", Toast.LENGTH_SHORT).show() startService(Intent(this@GuardService, LocalService::class.java)) bindService( Intent(this@GuardService, LocalService::class.java), connection, Context.BIND_IMPORTANT ) } } } override fun onBind(intent: Intent): IBinder { return MyBinder() } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { Toast.makeText(this, "守护进程服务启动", Toast.LENGTH_SHORT).show() bindService(Intent(this, LocalService::class.java), connection, BIND_IMPORTANT) return START_STICKY } class MyBinder : IMyAidlInterface.Stub() { @Throws(RemoteException::class) override fun getServiceName(): String { return LocalService::class.java.name } } }

二、设置前台服务,提升App进程优先级

class ForegroundService : Service() { companion object { private const val SERVICE_ID = 1 } override fun onCreate() { super.onCreate() Log.d("ForegroundServiceNew", "开启ForegroundService") } override fun onDestroy() { super.onDestroy() Log.d("ForegroundServiceNew", "销毁ForegroundService") } override fun onBind(intent: Intent): IBinder? { return null } @RequiresApi(api = Build.VERSION_CODES.O) override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { //判断版本 if (Build.VERSION.SDK_INT < 18) { //Android4.3以下版本 //将Service设置为前台服务,可以取消通知栏消息 startForeground(SERVICE_ID, Notification()) } else if (Build.VERSION.SDK_INT < 24) { //Android4.3 - 7.0之间 //将Service设置为前台服务,可以取消通知栏消息 startForeground(SERVICE_ID, Notification()) startService(Intent(this, InnerService::class.java)) } else { //Android 8.0以上 val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager if (manager != null) { val channel = NotificationChannel("channel", "name", NotificationManager.IMPORTANCE_NONE) manager.createNotificationChannel(channel) val builder = NotificationCompat.Builder(this, "channel") startForeground(SERVICE_ID, Notification()) } } return START_STICKY } class InnerService : Service() { override fun onBind(intent: Intent): IBinder? { return null } override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { startForeground(SERVICE_ID, Notification()) stopForeground(true) //移除通知栏消息 stopSelf() return super.onStartCommand(intent, flags, startId) } } }

最后在Activity中直接启动服务就行

startService(Intent(this, ForegroundService::class.java))

三、优雅保活 — 添加白名单

从 Android 6.0 开始,系统为了省电增加了休眠模式,系统待机一段时间后,会杀死后台正在运行的进程。但系统会有一个后台运行白名单,白名单里的应用将不会受到影响,在原生系统下,通过设置里面电池优化就可以看到这个白名单

大厂是通过和手机厂商的合作,将自己的应用默认加入到白名单中

<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />

判断我们的应用是否在白名单中

@RequiresApi(api = Build.VERSION_CODES.M) private fun isIgnoringBatteryOptimizations(): Boolean { var isIgnoring = false val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager if (powerManager != null) { isIgnoring = powerManager.isIgnoringBatteryOptimizations(packageName) } return isIgnoring }

申请加入白名单

@RequiresApi(api = Build.VERSION_CODES.M) fun requestIgnoreBatteryOptimizations() { try { val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) intent.data = Uri.parse("package:$packageName") startActivity(intent) } catch (e: Exception) { e.printStackTrace() } } }

申请时,会弹出这样的dialog

在申请的时候调用 startActivityForResult,在 onActivityResult 里再判断一次是否在白名单中

多厂商适配方法 几乎各个厂商都有自己的后台管理,就算应用加入了后台运行白名单,仍然可能会被厂商自己的后台管理干掉

如果能把应用加入厂商系统的后台管理白名单,可以进一步降低进程被杀的概率。不同的厂商在不同的地方进行设置,一般是在各自的手机管家,但是就算同一个厂商的系统,不同版本也可能是在不同地方设置。最理想的做法是,我们根据不同手机,甚至是不同的系统版本,给用户呈现一个图文操作步骤,并且提供一个按钮,直接跳转到指定页面进行设置,但需要对每个厂商每个版本进行适配,下面是一些主流厂商

首先定义下面两个方法,用于页面的跳转

//跳转到指定应用的首页 private fun showActivity(packageName: String) { val intent = packageManager.getLaunchIntentForPackage(packageName) startActivity(intent) } //跳转到指定应用的指定页面 private fun showActivity(packageName: String, activityDir: String) { val intent = Intent() intent.component = ComponentName(packageName, activityDir) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) startActivity(intent) }

(1)华为

厂商判断

fun isHuawei(): Boolean { return if (Build.BRAND == null) { false } else { Build.BRAND.toLowerCase() == "huawei" || Build.BRAND.toLowerCase() == "honor" } }

跳转到华为启动管理

private fun goHuaweiSetting() { try { showActivity( "com.huawei.systemmanager", "com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity" ) } catch (e: java.lang.Exception) { showActivity( "com.huawei.systemmanager", "com.huawei.systemmanager.optimize.bootstart.BootStartActivity" ) } }

操作步骤:应用启动管理 -> 关闭应用开关 -> 打开允许自启动

(2)小米

fun isXiaomi(): Boolean { //小米厂商判断 return Build.BRAND != null && Build.BRAND.toLowerCase() == "xiaomi" } private fun goXiaomiSetting() { //跳转小米安全中心的自启动管理页面 showActivity( "com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity" ) }

操作步骤:授权管理 -> 自启动管理 -> 允许应用自启动

(3)OPPO

fun isOPPO(): Boolean { return Build.BRAND != null && Build.BRAND.toLowerCase() == "oppo" } private fun goOPPOSetting() { try { showActivity("com.coloros.phonemanager") } catch (e1: java.lang.Exception) { try { showActivity("com.oppo.safe") } catch (e2: java.lang.Exception) { try { showActivity("com.coloros.oppoguardelf") } catch (e3: java.lang.Exception) { showActivity("com.coloros.safecenter") } } } }

操作步骤:权限隐私 -> 自启动管理 -> 允许应用自启动

(4)VIVO

fun isVIVO(): Boolean { return Build.BRAND != null && Build.BRAND.toLowerCase() == "vivo" } private fun goVIVOSetting() { showActivity("com.iqoo.secure") }

操作步骤:权限管理 -> 自启动 -> 允许应用自启动

(5)魅族

fun isMeizu(): Boolean { return Build.BRAND != null && Build.BRAND.toLowerCase() == "meizu" } private fun goMeizuSetting() { showActivity("com.meizu.safe") }

操作步骤:权限管理 -> 后台管理 -> 点击应用 -> 允许后台运行

最新回复(0)