Android保活策略

进程的划分

一、前台进程
相关场景:
(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在退到后台时系统并不会真正的kill掉这个进程,而是将其缓存起来。打开的应用越多,后台缓存的进程也越多。在系统内存不足的情况下,系统开始依据自身的一套进程回收机制来判断要kill掉哪些进程,以腾出内存来供给需要的app,这套进程回收内存的机制就叫 Low Memory Killer。那这个不足怎么来规定呢?那就是内存阈值,内存阈值在不同的手机上不一样,一旦低于该值,Android便开始按顺序关闭进程,Android开始结束优先级最低的空进程,读到这里,你或许有一个疑问,假设现在内存不足,空进程都被杀光了,现在要杀后台进程,但是手机中后台进程很多,难道要一次性全部都清理掉?当然不是的,进程是有它的优先级的,这个优先级通过进程的adj值来反映,它是linux内核分配给每个系统进程的一个值,代表进程的优先级,进程回收机制就是根据这个优先级来决定是否进行回收,adj值定义在com.android.server.am.ProcessList类中

oom_adj越大,占用物理内存越多会被最先kill掉,OK,那么现在对于进程如何保活这个问题就转化成,如何降低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))

猜你喜欢

转载自blog.csdn.net/qq_45485851/article/details/108403695