本專欄專註分享大型Bat面試知識,後續會持續更新,喜歡的話麻煩點擊一個關注
進程保活的關鍵點有兩個,一個是進程優先順序的理解,優先順序越高存活幾率越大。二是弄清楚哪些場景會導致進程會kill,然後採取下面的策略對各種場景進行優化:
廢話不多說先上面試資料目錄
Android一般的進程優先順序劃分: 1.前臺進程 (Foreground process) 2.可見進程 (Visible process) 3.服務進程 (Service process) 4.後臺進程 (Background process) 5.空進程 (Empty process) 這是一種粗略的劃分,進程其實有一種具體的數值,稱作oom_adj,注意:數值越大優先順序越低:
如何查看某個進程的oom_adj數值呢? oom_adj 存儲在proc/PID/oom_adj文件中,其中PID是進程的id,直接 adb shell進入手機根目錄查看這個文件即可。
演示一下:以我自己的項目為例,app中有兩個進程,一個是主進程,另一個是運行service的進程取名為:remote。首先用android studio查看每個進程的PID:
然後分別查看app在前臺,app退到後臺,這2中場景主進程的oom_adj數值:
可見,當app在前臺時 oom_adj = 0,對應上面的表格是前臺進程。
當app退到後臺時,oom_adj = 6,對應後臺進程。
然後查看運行著service的進程:
ok,知道了進程優先順序的概念以及如何查看優先順序,我們就可以對app進程優化,然後通過查看這個數值判斷我們的優化是否有效果。
1.點擊home鍵使app長時間停留在後臺,內存不足被kill
處理這種情況前提是你的app至少運行了一個service,然後通過Service.startForeground() 設置為前臺服務,可以將oom_adj的數值由4降低到1,大大提高存活率。
//設置service為前臺服務,提高優先順序 if (Build.VERSION.SDK_INT < 18) { //Android4.3以下 ,此方法能有效隱藏Notification上的圖標 service.startForeground(GRAY_SERVICE_ID, new Notification()); } else if(Build.VERSION.SDK_INT>18 && Build.VERSION.SDK_INT<25){ //Android4.3 - Android7.0,此方法能有效隱藏Notification上的圖標 Intent innerIntent = new Intent(service, GrayInnerService.class); service.startService(innerIntent); service.startForeground(GRAY_SERVICE_ID, new Notification()); }else{ //Android7.1 google修復了此漏洞,暫無解決方法(現狀:Android7.1以上app啟動後通知欄會出現一條"正在運行"的通知消息) service.startForeground(GRAY_SERVICE_ID, new Notification()); }
經過改進之後,再來看下這個後臺service進程的oom_adj,發現被提升為前臺進程。
2.在大多數國產手機下,進入鎖屏狀態一段時間,省電機制會kill後臺進程
這種情況和上面不太一樣,是很過國產手機rom自帶的優化,當鎖屏一段時間之後,即使手機內存夠用為了省電,也會釋放掉一部分內存。
策略:註冊廣播監聽鎖屏和解鎖事件, 鎖屏後啟動一個1像素的透明Activity,這樣直接把進程的oom_adj數值降低到0,0是android進程的最高優先順序。 解鎖後銷毀這個透明Activity。這裡我把這個Activity放到:remote進程也就是我那個後臺服務進程,當然你也可以放到主進程,看你打算保活哪個進程。
我們可以寫一個KeepLiveManager來負責接收廣播,維護這個Activity的常見和銷毀,注意鎖屏廣播和解鎖分別是:ACTION_SCREEN_OOF和ACTION_USER_PRESENT,並且只能通過動態註冊來綁定,並且是綁定到你的後臺service裡面,onCreate綁定,onDestroy裡面解綁
配好之後把手機鎖屏,看下:remote進程的oom_adj:
3. 用戶手動釋放內存:包括手機自帶清理工具,和第三方app(360,獵豹清理大師等)
清理內存軟體會把 優先順序低於 前臺進程(oom_adj = 0)的所有進程放入清理列表,而當我們打開了清理軟體就意味著其他app不可能處於前臺。所以說理論上可以kill任何app。 以360安全衛士為例,打開內存清理:
因此這類場景唯一的處理辦法就是加入 手機rom 白名單,比如你打開小米,魅族的許可權管理 -> 自啟動管理可以看到 QQ,微信,天貓默認被勾選,這就是廠商合作。那我們普通app可以這麼做:在app的設置界面加一個選項,提示用戶自己去勾選自啟動,我封裝了一個工具類給出國內各廠商的自啟動的Intent跳轉方法:
/** * Created by carmelo on 2018/3/17. * 國內手機廠商白名單跳轉工具類 */
public class SettingUtils {
public static void enterWhiteListSetting(Context context){ try { context.startActivity(getSettingIntent()); }catch (Exception e){ context.startActivity(new Intent(Settings.ACTION_SETTINGS)); } }
private static Intent getSettingIntent(){
ComponentName componentName = null;
String brand = android.os.Build.BRAND;
switch (brand.toLowerCase()){ case "samsung": componentName = new ComponentName("com.samsung.android.sm", "com.samsung.android.sm.app.dashboard.SmartManagerDashBoardActivity"); break; case "huawei": componentName = new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity"); break; case "xiaomi": componentName = new ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity"); break; case "vivo": componentName = new ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity"); break; case "oppo": componentName = new ComponentName("com.coloros.oppoguardelf", "com.coloros.powermanager.fuelgaue.PowerUsageModelActivity"); break; case "360": componentName = new ComponentName("com.yulong.android.coolsafe", "com.yulong.android.coolsafe.ui.activity.autorun.AutoRunListActivity"); break; case "meizu": componentName = new ComponentName("com.meizu.safe", "com.meizu.safe.permission.SmartBGActivity"); break; case "oneplus": componentName = new ComponentName("com.oneplus.security", "com.oneplus.security.chainlaunch.view.ChainLaunchAppListActivity"); break; default: break; }
Intent intent = new Intent(); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if(componentName!=null){ intent.setComponent(componentName); }else{ intent.setAction(Settings.ACTION_SETTINGS); } return intent; } }
補充幾點:
就找到了魅族MX4 pro 後臺許可權的Activity。
分兩種情況,一是主進程(含有Activity沒有service),這種進程由於內存不足被kill之後,用戶再次打開app系統會恢復到上次的Activity,這個不在本文話題之內。另一種是service的後臺進程被kill,可以通過service自有api來重啟service:
@Override public int onStartCommand(Intent intent, int flags, int startId) { //..... return START_STICKY; // service被異常停止後,系統嘗試重啟service,不能保證100%重啟成功 }
配好START_STICKY後,通過android studio 釋放進程的工具測試下,可以發現:remote進程被kill之後馬上重啟了:
但它不是100%保證重啟成功,比如下面2種情況:(本人經過測試,這裡就不放效果圖了)
本文通過兩種 提高進程優先順序的方法,針對鎖屏 和非鎖屏模式下進程在後臺被kill的場景處理,把後臺進程優先順序提升到可見級別,基本可以保證絕大多數場景不會被kill。另外,針對含有service的進程被kill給出了可喚醒的辦法。
需要進程保活的代碼+如開頭的面試資料+如下視頻資料的
Android架構進階學習資源資料免費獲取