- Android 進程優先級介紹
- Android 進程回收策略介紹
- Android 目前系統保活策略
- 項目中使用進程保活代碼
1、Android進程等級分級和等級介紹
Android 系統將盡量長時間地保持應用的進程,但是系統運行內存是有限的,所以爲了 新建進程或者運行更重要的進程,最終還是需要清除舊進程來回收內存。
所以分區進程重要程度,系統會根據進程的運行組件和組件的狀態,將每一個進程放入“重要性層級結構”,必要時系統會首先清除掉重要性最低的進程,在內存不足時,再清除重要性稍低的進程,以此類推,以回收系統資源。
進程重要性分級
前臺進程
可見進程
服務進程
後臺進程
空進程
前臺進程:用戶當前操作所必需的進程,只有在內部不足以支它們同時運行。系統纔會終止它們。
- 用戶正在交互的Activity(已調用 onResume())
- Service,綁定在用戶正在交互的Activity
- 正在前臺運行的Service(服務已經調用 startForeground())
- 正在執行生命週期的Service(onCreat,onStart,onDestory)
- 正在執行 onReceive 方法的廣播 BrocadcastReceiver
可見進程:沒有任何前臺組件,但是仍然會影響用戶在屏幕上所見內容的進程。
- 不在前臺,但是用戶仍然可見(已調用 onPause())例如沒dialog覆蓋
- 綁定在可見 Activity的Service
服務進程:沒有任何前臺組件,但是通過它們執行一些用戶關心的操作(音樂播放,網絡下載)
- 正在運行 startService() 方法啓動的服務
後臺服務:後臺進程對用戶體驗沒有直接影響,系統可以隨時終止它們
- 對用戶不可見的Activity的進程,(已調用 onstop方法)
空進程:保留這種進程的唯一作用是用做緩存,縮短下次在運行組件所需的啓動時間
- 不含任何活動組件的進程
2、Android 進程回收策略
衆所周知,Android是基於Linux系統的。在Android進程回收策略中,Android進程與Linux進程根據OOM_ADJ閾值進行區分:
OOM_ADJ >= 4:比較容易被殺死的進程
OOM_ADJ 0 ~ 3:不容易被殺死的進程
OOM_ADJ < 0 :純Linux進程,非Android進程
當Android系統察覺設備內存不足時,會按照閾值從大到小殺死進程。
具體的oom_adj值的意義我們可以查看AOSP中的com.android.server.am.ProcessList 文件(其中本人添加了一些中文註釋):
/**
1. Activity manager code dealing with processes.
*/
final class ProcessList {
...
// OOM adjustments for processes in various states:
// Adjustment used in certain places where we don't know it yet.
// (Generally this is something that is going to be cached, but we
// don't know the exact value in the cached range to assign yet.)
// 未知進程,通常是用作緩存
static final int UNKNOWN_ADJ = 16;
// This is a process only hosting activities that are not visible,
// so it can be killed without any disruption.
// 擁有不可視的Activity的進程,可以不影響影響用戶的情況下殺掉
static final int CACHED_APP_MAX_ADJ = 15;
static final int CACHED_APP_MIN_ADJ = 9;
// The B list of SERVICE_ADJ -- these are the old and decrepit
// services that aren't as shiny and interesting as the ones in the A list.
// 一些舊的服務進程
static final int SERVICE_B_ADJ = 8;
// This is the process of the previous application that the user was in.
// This process is kept above other things, because it is very common to
// switch back to the previous app. This is important both for recent
// task switch (toggling between the two top recent apps) as well as normal
// UI flow such as clicking on a URI in the e-mail app to view in the browser,
// and then pressing back to return to e-mail.
// 用戶使用的前一個進程
static final int PREVIOUS_APP_ADJ = 7;
// This is a process holding the home application -- we want to try
// avoiding killing it, even if it would normally be in the background,
// because the user interacts with it so much.
// 主界面進程
static final int HOME_APP_ADJ = 6;
// This is a process holding an application service -- killing it will not
// have much of an impact as far as the user is concerned.
// 持有應用服務的進程
static final int SERVICE_ADJ = 5;
// This is a process with a heavy-weight application. It is in the
// background, but we want to try to avoid killing it. Value set in
// system/rootdir/init.rc on startup.
// 重量級應用進程
static final int HEAVY_WEIGHT_APP_ADJ = 4;
// This is a process currently hosting a backup operation. Killing it
// is not entirely fatal but is generally a bad idea.
// 執行備份操作的進程
static final int BACKUP_APP_ADJ = 3;
// This is a process only hosting components that are perceptible to the
// user, and we really want to avoid killing them, but they are not
// immediately visible. An example is background music playback.
// 擁有用戶可感知組件的進程
static final int PERCEPTIBLE_APP_ADJ = 2;
// This is a process only hosting activities that are visible to the
// user, so we'd prefer they don't disappear.
// 擁有用戶僅可見、不可交互的Activity的進程
static final int VISIBLE_APP_ADJ = 1;
// This is the process running the current foreground app. We'd really
// rather not kill it!
// 前臺運行的進程
static final int FOREGROUND_APP_ADJ = 0;
// This is a system persistent process, such as telephony. Definitely
// don't want to kill it, but doing so is not completely fatal.
// 系統常駐進程
static final int PERSISTENT_PROC_ADJ = -12;
// The system process runs at the default adjustment.
// 系統進程
static final int SYSTEM_ADJ = -16;
// Special code for native processes that are not being managed by the system (so
// don't have an oom adj assigned by the system).
// 爲native進程保留,他們不被系統管理
static final int NATIVE_ADJ = -17;
...
}
Android 進程被殺死情況:
①觸發系統進程管理機制回收(Lowmemorykiller):這種方法會按照閾值從大到小進行清理
②被沒有進行Root的第三方應用殺死(使用killBackgroundProcess方法):這種方法只能殺死OOM_ADJ爲4以上的進程
③被進行Root的第三方應用殺死(使用force-stop或者kill):理論上來說可以殺死所有進程,但一般只會清理非系統關鍵進程和非前臺可見進程
④廠商的殺進程功能(force-stop或者kill):理論上來說可以殺死所有進程,包括Linux原生進程
⑤用戶主動“強行停止”進程(force-stop):只能停用第三方和非system/phone進程應用(停用system進程應用會造成Android系統重啓)
我們可以通過adb shell命令實時查看這個adj值。
adb shell
ps | grep <關鍵字>
USER PID PPID VSIZE RSS WCHAN PC NAME
root 1 0 812 668 ffffffff 00000000 S /init
root 2 0 0 0 ffffffff 00000000 S kthreadd
adb shell
cat /proc/PID/oom_adj
cat命令執行後,會得到一個adj整數數值。
3. Android 目前系統保活策略
A: Service的onStartCommand函數返回START_STICKY
START_STICKY是官方提供的參數,意思是當service被內存回收了,系統會對service進行重啓。面對360等內存回收,並沒什麼作用。
B:在service 的onDestory裏面重啓服務
onDestroy()方法只有在service正常停止的時候纔會被調用,面對上述回收的第二與第三種方法沒有效果。
C:守護線程相互監聽
AB兩個進程,A進程裏面輪詢檢查B進程是否存活,沒存活的話將其拉起,同樣B進程裏面輪詢檢查A進程是否存活,沒存活的話也將其拉起,而我們的後臺邏輯則隨便放在某個進程裏執行即可。
這種方法面對回收的時候,其實作用也不大,而且很消耗性能。另外,也有人提到過使用兩個native進程監控,那種方法沒試過。
D: AlarmManager or JobScheduler循環觸發
public class AlarmService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
initAlarm(this);
Log.d("czh", "AlarmService onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
private void initAlarm(Context context) {
Intent intent = new Intent(context, AlarmReceiver.class);
intent.setAction("repeating");
PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0);
//開始時間
long firsTime = SystemClock.elapsedRealtime();
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, firsTime, 5 * 1000, sender);
}
}
PendingIntent.getBroadcase的註冊廣播
E:與系統service綁定
論Android應用進程長存的可行性 一文中,提到用NotificationListenerService代替普通service,從而達到保活的作用。 原理是沒有問題,在小米4上親測過後,發現並沒什麼用。
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class SimulateNotificationService extends NotificationListenerService {
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
super.onNotificationPosted(sbn);
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
super.onNotificationRemoved(sbn);
}
}
<service android:name=".service.SimulateNotificationService"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
android:process=":test5">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
F:監聽系統Receiver保活
使用Receiver來檢測目標進程是否存活不失爲一個好方法,靜態註冊一系列廣播,什麼開機啓動、網絡狀態變化、時區地區變化、充電狀態變化等等等等,這聽起來好像很6,而且在大部分手機中都是可行的方案,但是對於深度定製的ROM,是的,又是深度定製,你沒有看錯,而且代表性人物還是魅族、小米,這兩個業界出了名的喜歡“深度定製”系統。
自從Android 3.1開始系統對我們的應用增加了一種叫做STOPPED的狀態,什麼叫STOPPED?就是安裝了之後從未啓動過的,大家可能經常在網上看到對開機廣播的解釋,說要想應用正確接收到開機廣播那麼就得先啓動一下應用,這個說法的技術支持就來源於此,因爲自Android 3.1後所有的系統廣播都會在Intent添加一個叫做FLAG_EXCLUDE_STOPPED_PACKAGES的標識,說白了就是所有處於STOPPED狀態的應用都不可以接收到系統廣播。
H:提高進程優先級, Notification提權
這種保活手段是應用範圍最廣泛。它是利用系統的漏洞來啓動一個前臺的Service進程,與普通的啓動方式區別在於,它不會在系統通知欄處出現一個Notification,看起來就如同運行着一個後臺Service進程一樣。這樣做帶來的好處就是,用戶無法察覺到你運行着一個前臺進程(因爲看不到Notification),但你的進程優先級又是高於普通後臺進程的。
這種方法面對第二種回收方式有效,但是面對小米之類的後臺回收,還是無能爲力。
public class NotificationService extends Service {
private final static int SERVICE_ID = 1001;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (Build.VERSION.SDK_INT < 18) {
startForeground(SERVICE_ID, new Notification());
} else {
Intent innerIntent = new Intent(this, InnerService.class);
startService(innerIntent);
startForeground(SERVICE_ID, new Notification());
}
return super.onStartCommand(intent, flags, startId);
}
/**
* 給 API >= 18
*/
public static class InnerService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(SERVICE_ID, new Notification());
stopForeground(true);
stopSelf();
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
}
I:不同的app進程,用廣播相互喚醒
如果你手機安裝了各種app,或者應用了各種第三方代sdk,即可互相喚醒。
假如你手機裏裝了支付寶、淘寶、天貓、UC等阿里系的app,那麼你打開任意一個阿里系的app後,有可能就順便把其他阿里系的app給喚醒了。
這個方法針對內存回收的三種方式均有效,只要有一個活着,其他的就會活下來。
這篇文章是東拼西湊出來的,目的是爲了加深印象,方面自己之後的學習。
分別引用:
安卓筆記俠