一:概念
1 進程概念
每個App在啟動前必須先創(chuàng)建一個進程,該進程是由Zygote fork出來的,進程具有獨立的資源空間,用于承載App上運行的各種Activity/Service等組件。
2 同一個應(yīng)用定義多進程的方式
大多數(shù)情況一個App就運行在一個進程中,除非在AndroidManifest.xml中配置Android:process屬性,或通過native代碼fork進程。
3 分類
Android中的進程分為用戶進程和內(nèi)核進程。
kthreadd進程:所有內(nèi)核進程的父進程。
init進程:所有用戶進程的父進程。
其中用戶進程中的幾個重要進程:
zygote進程:是上層所有java進程的父進程。由init進程fork而來。
system_server進程:承載java層framwork的所有serveices進程,由zygote進程fork而來。
mediaserver進程:托起整個c++ framework的所有service進程,有init進程孵化而來。
servicemanager進程:管理整個Binder架構(gòu), 由init進程孵化而來。
4 android進程間通信的方式
4.1 Binder
activity啟動Activity,啟動Service,ContentProvider的本質(zhì)都是Binder進程間通信機制。AIDL也是IPC機制的一種實現(xiàn),本質(zhì)也是通過Binder去實現(xiàn)的。
4.2 Socket
創(chuàng)建一個Process的時候,是通過system_server進程調(diào)用Process.start()方法請求Zygote進程去創(chuàng)建進程的,這種方式是通過Socket去完成的。
二:進程管理機制
1 App啟動內(nèi)存機制:
Android系統(tǒng)的設(shè)計理念正是希望應(yīng)用進程能盡量長時間地存活,以提升用戶體驗。應(yīng)用首次打開比較慢,這個過程有進程創(chuàng)建以及Application等信息的初始化,所以應(yīng)用在啟動之后,即便退到后臺并非立刻殺死,而是存活一段時間,這樣下次再使用則會非常快。
2 LMK策略:
為了防止剩余內(nèi)存過低,Android在內(nèi)核空間有LowMemoryKiller(簡稱LMK),是一種根據(jù)閾值級別觸發(fā)相應(yīng)力度的內(nèi)存回收的機制。其依據(jù)就是進程優(yōu)先級的量化指標:ADJ值。
ADJ值定義在ProcessList.java中。
從Android 7.0開始,ADJ采用100、200、300;在這之前的版本ADJ采用數(shù)字1、2、3,這樣的調(diào)整可以更進一步地細化進程的優(yōu)先級,比如在VISIBLE_APP_ADJ(100)與PERCEPTIBLE_APP_ADJ(200)之間,可以有ADJ=101、102級別的進程。
下表列出了進程adj值對應(yīng)的情景:
3 查看進程ADJ值的方法:
3.1
cat /proc/[PID]/oom_adj: 使用該命令會直接顯示出對應(yīng)進程號的adj值,這種方式在Android N以后會因為權(quán)限問題無法查看
cat /proc/[PID]/oom_score,查看計算出的優(yōu)先級值,這種方式在O上仍然可以用
3.2
dumpsys meminfo
使用dumpsys meminfo命令時,會列出當前系統(tǒng)的所有進程,不同進程放入不同的分類,對應(yīng)的分類名基本與lmk的分類一致。
4 進程保護策略
4.1 開啟一個像素的Activity
據(jù)說這個是手Q的進程保活方案,基本思想,系統(tǒng)一般是不會殺死前臺進程的。所以要使得進程常駐,我們只需要在鎖屏的時候在本進程開啟一個Activity,為了欺騙用戶,讓這個Activity的大小是1像素,并且透明無切換動畫,在開屏幕的時候,把這個Activity關(guān)閉掉,所以這個就需要監(jiān)聽系統(tǒng)鎖屏廣播
4.2 前臺服務(wù)
這種大部分人都了解,據(jù)說這個微信也用過的進程保活方案,這方案實際利用了Android前臺service的漏洞。
原理如下
對于 API level < 18 :調(diào)用startForeground(ID, new Notification()),發(fā)送空的Notification ,圖標則不會顯示。
對于 API level >= 18:在需要提優(yōu)先級的service A啟動一個InnerService,兩個服務(wù)同時startForeground,且綁定同樣的 ID。Stop 掉InnerService ,這樣通知欄圖標即被移除。
無論是QQ還是微信的進程保活策略都是通過提高進程優(yōu)先級,降低adj的方式去實現(xiàn),但是除非廠商愿意將你的應(yīng)用加入清理白名單,否則在app層面是無法做到真正的進程不死,只能是盡量保證進程不被kill。
5 Kill進程的方式
5.1 Process.killProcess(pid)方式
Process.killProcessQuiet(pid)
用戶空間流程:
Process.killProcess()-->Process.sendSignal()-->
android_util_Process. android_os_Process_sendSignal()-->kill()
注意:
kill()之后就進入內(nèi)核空間調(diào)用的是內(nèi)核的相關(guān)方法去完成殺進程的操作。
另外需要注意的調(diào)用這種方式殺進程,如果進程中有服務(wù)是在被殺掉以后重啟的,那么是需要重啟進程的,意思是說這種方式針對有這種服務(wù)的進程相當于先殺死在啟動的,重啟一下而已(在Log中會看到類似Scheduling restart of crashed service的信息)。
5.2 Force-stop方式:
AM.forceStopPackage(packageName) 這個是隱藏api,并且需要force-stop系統(tǒng)級權(quán)限
流程:
1)AMS.forceStopPackage(final String packageName, int userId);
2)AMS.forceStopPackage()-->
AMS.finishForceStopPackageLocked()
3)AMS.forceStopPackageLocked()-->
4)AMS.forceStopPackageLocked(9參的)
注意:
1)force-stop并不會殺persistent進程,當指定用戶userId時,不殺其他用戶空間的進程。
2)當app被force-stop后,無法接收到任何普通廣播,那么也就常見的監(jiān)聽手機網(wǎng)絡(luò)狀態(tài)的變化或者屏幕亮滅的廣播來拉起進程肯定是不可行;
3)當app被force-stop后,那么alarm鬧鐘一并被清理,無法實現(xiàn)定時響起的功能;
4)app被force-stop后,四大組件以及相關(guān)進程都被一一剪除清理,即便多進程架構(gòu)的app也無法拉起自己;
5)級聯(lián)誅殺:當app通過ClassLoader加載另一個app(在后一個app的ProcessRecord的pkgDeps中會有前一個app的包名信息,則殺掉后一個app,前一個app也會被干掉),則會在force-stop的過程中會被級聯(lián)誅殺;
6)生死與共:當app與另個app使用了share uid,則會在force-stop的過程,任意一方被殺則另一方也被殺,建立起生死與共的強關(guān)系。
5.3 killBackgroundProcesses方式
流程:
AM.killBackgroundProcesses(String packageName)
--> AMS.killBackgroundProcesses(final String packageName, int userId)
--> killPackageProcessesLocked();
同樣會調(diào)用到killPackageProcessesLocked方法殺進程,只不過入?yún)dj的設(shè)定值是ProcessList.SERVICE_ADJ即500,進程Adj值大于這個才會執(zhí)行后續(xù)殺進程方法,否則continue。這個方法需要添加權(quán)限KILL_BACKGROUND_PROCESSES(不是系統(tǒng)權(quán)限)。
5.4 adb shell中執(zhí)行 kill +pid的命令殺進程,這種方式最后也是調(diào)用的Process. killProcess()方法殺進程。但是沒有killing 相關(guān)的log打印出。這種方式需要root權(quán)限。
5.5 adb shell am force-stop +包名。這種方式會調(diào)用到AMS的forceStopPackage方法,因此會有相應(yīng)log打出,需要root權(quán)限。
若使用Runtime去執(zhí)行的話,也是需要force-stop權(quán)限的,系統(tǒng)級權(quán)限。