进程的划分
一、前台进程
相关场景:
(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))