Android進(jìn)程詳解(一):進(jìn)程機(jī)制和多進(jìn)程總結(jié)

進(jìn)程介紹

先強化兩個進(jìn)程相關(guān)的概念

進(jìn)程:進(jìn)程是一個具有一定獨立功能的程序關(guān)于某個數(shù)據(jù)集合的一次運行活動,它是操作系統(tǒng)動態(tài)執(zhí)行的基本單元,是系統(tǒng)進(jìn)行資源分配的基本單位,在面向線程設(shè)計的計算機(jī)結(jié)構(gòu)中是線程的容器。

fork:fork函數(shù)UNIX及類UNIX系統(tǒng)中的分叉函數(shù),一個現(xiàn)有進(jìn)程可以調(diào)用fork函數(shù)創(chuàng)建一個新進(jìn)程。由fork創(chuàng)建的新進(jìn)程被稱為子進(jìn)程。fork函數(shù)被調(diào)用一次但返回兩次。兩次返回的唯一區(qū)別是子進(jìn)程中返回0值而父進(jìn)程中返回子進(jìn)程ID。子進(jìn)程是父進(jìn)程的副本,它將獲得父進(jìn)程數(shù)據(jù)空間、堆、棧等資源的副本。注意,子進(jìn)程持有的是上述存儲空間的“副本”,這意味著父子進(jìn)程間不共享這些存儲空間。

Android進(jìn)程機(jī)制

下面介紹一下android的進(jìn)程機(jī)制,Android進(jìn)程實際上是Linux(類UNIX系統(tǒng))進(jìn)程,在Linux中所有的進(jìn)程都是init進(jìn)程的子孫進(jìn)程,也就是說,所有的進(jìn)程都是直接或者間接地由init進(jìn)程fork出來的。

在android系統(tǒng)中也有一個初始進(jìn)程:Zygote進(jìn)程,是在系統(tǒng)啟動的過程,由init進(jìn)程創(chuàng)建的,啟動Zygote之后, init進(jìn)程會啟動runtime進(jìn)程。Zygote進(jìn)程負(fù)責(zé)后續(xù)Android應(yīng)用程序框架層的其它進(jìn)程的創(chuàng)建和啟動工作。Zygote進(jìn)程初始化了第一個VM, 并且預(yù)加載了framework和App所需要的通用資源。它會創(chuàng)建一個超級管理進(jìn)程SystemServer進(jìn)程,SystemServer進(jìn)程會啟動所有系統(tǒng)的核心服務(wù),如包管理服務(wù)PackageManagerService和應(yīng)用程序組件管理服務(wù)ActivityManagerService,硬件相關(guān)的Service等。然后它開啟一個Socket接口來監(jiān)聽請求, 根據(jù)請求fork新的VM來管理新的App進(jìn)程. 一旦收到新的請求, Zygote會基于自身預(yù)先加載的VM來fork一個新的VM創(chuàng)建一個新的進(jìn)程。需要啟動一個Android應(yīng)用程序時,ActivityManagerService會通過Socket進(jìn)程間通信機(jī)制,通知Zygote進(jìn)程為這個應(yīng)用程序創(chuàng)建一個新的進(jìn)程。

具體啟動細(xì)節(jié):每一個App應(yīng)用都是由ActivityManagerService通過Socket與Zygote進(jìn)程進(jìn)行通信,ActivityManagerService調(diào)用startProcessLocked()方法來創(chuàng)建新的進(jìn)程, 該方法會通過前面講到的socket通道傳遞參數(shù)給Zygote進(jìn)程。Zygote會fork一個子進(jìn)程出來作為這個即將要啟動的應(yīng)用程序的進(jìn)程, 并調(diào)用ZygoteInit.main()方法來實例化ActivityThread對象并最終返回新進(jìn)程的pid。接下來要做的就是將進(jìn)程和指定的Application綁定起來. 這個是通過ActivityThread對象中調(diào)用bindApplication()方法完成的. 該方法發(fā)送一個BINDAPPLICATION的消息到消息隊列中, 最終通過handleBindApplication()方法處理該消息. 然后調(diào)用makeApplication()方法來加載App的classes到內(nèi)存中。

經(jīng)過前兩個步驟之后, 系統(tǒng)已經(jīng)擁有了該application的進(jìn)程。后面的調(diào)用順序就是普通的從一個已經(jīng)存在的進(jìn)程中啟動一個新進(jìn)程的Activity了。ActivityManagerService會通過Binder機(jī)制通知ActivityThread去創(chuàng)建需要的Activity,實際調(diào)用方法是realStartActivity(), 它會調(diào)用application線程對象中的sheduleLaunchActivity()發(fā)送一個LAUNCHACTIVITY消息到消息隊列中, 通過handleLaunchActivity()來處理該消息。最后會輾轉(zhuǎn)到Instrumentation來創(chuàng)建Activity。

影響應(yīng)用進(jìn)程的因素

正常情況下一個android應(yīng)用只有一個進(jìn)程,進(jìn)程名就是應(yīng)用包名,android的application和四大組件都是在這個進(jìn)程。但可分別指定Application和四大組件運行的進(jìn)程,通過以下方式創(chuàng)建獨立進(jìn)程:

android:process=":name" 創(chuàng)建私有進(jìn)程,進(jìn)程名包名+":name"

android:process="com.cxzl.name" 創(chuàng)建公共進(jìn)程,進(jìn)程名com.cxzl.name

組件創(chuàng)建獨立進(jìn)程通常用來需要長期運行不被系統(tǒng)回收的功能,比如push功能。

除了android:process外,android:multiprocess也能夠影響進(jìn)程創(chuàng)建,multiprocess顧名思義就是多進(jìn)程的意思,這個參數(shù)只能在Activity和ContentProvider中定義。默認(rèn)是false,組件只會創(chuàng)建在申明它的app所在的進(jìn)程。multiprocess="true"允許組件創(chuàng)建多個實例,在哪個進(jìn)程中調(diào)用組件就會使用該進(jìn)程中的組件實例,并不會共用。

除了上述兩個直接影響進(jìn)程創(chuàng)建的配置外,還有一個配置能夠間接的影響到進(jìn)程的創(chuàng)建,它就是android:launchMode="singleInstance",Android的Activity有四種啟動方式,除了singleInstance以外的三種全部都是在同一個Activity棧里面,唯獨singeleInstance是創(chuàng)建獨立的棧,而且只有一份實例,這就導(dǎo)致它能夠影響到進(jìn)程創(chuàng)建:因為它與android:multiprocess="true"這個屬性相沖突。既然它是在獨立的棧,那么兩個應(yīng)用打開它,它屬于哪個應(yīng)用的棧?哪個應(yīng)用的進(jìn)程?如果組件指定了自己的進(jìn)程,又會是什么樣的情況?

帶著這些疑問我在網(wǎng)上搜了一下,沒有找到完整的資料,有的文章會根據(jù)某個點有一些分析,但不全面,而且不同文章觀點有沖突,對android:process,android:multiprocess,android:launchMode三個配置對一個組件所處的進(jìn)程的影響并沒有清晰結(jié)論。查看官方文檔,也只是單獨解釋這個三個配置的功能,并沒有說這些情況交叉在一起的影響。

進(jìn)程測試

所以我寫了兩個Demo來測試應(yīng)用中多進(jìn)程的情況.

一個主應(yīng)用(github地址https://github.com/cxzl/ProcessTestMain),包名com.cxzl.processtest,包含了以下內(nèi)容:

MainActivity類,啟動各個具體配置的Activity;

要啟動的Activity基類,用來打印進(jìn)程的pid,進(jìn)程名和Activity的hashcode;

在AndroidManifest配置出來的各個Activity,全部繼承第二條的基類。

一個輔助應(yīng)用(github地址https://github.com/cxzl/ProcessTestAssist),包名com.cxzl.processthirdstart,只包含一個類,就是MainActivity,用來啟動主應(yīng)用AndroidManifest配置出來的各個Activity。

用這兩個應(yīng)用單獨和交叉啟動各種配置的具體Activity,來比較各種因素會對進(jìn)程創(chuàng)建的影響。測試界面選取了幾張截圖:



下面的表格是具體的測試結(jié)果:


?先解釋一下這個表格:

最左邊一行是對Activity三種配置的數(shù)字編碼,因為用文字列舉這12種情況太復(fù)雜了,所以改成數(shù)字編碼。

第一個數(shù)字是android:launchMode,0是默認(rèn)情況,也就是不設(shè)置launchMode(Android默認(rèn)為standard),1是設(shè)置launchMode為singleInstance。

第二數(shù)字是android:multiprocess,0是默認(rèn)情況,也就是false,1是true。

第三個數(shù)字是android:process,0是默認(rèn)情況,也就是不設(shè)置process,默認(rèn)process就是應(yīng)用的包名com.cxzl.processtest,1是設(shè)置私有進(jìn)程,進(jìn)程名com.cxzl.processtest:private,2是設(shè)置公共進(jìn)程,進(jìn)程名com.cxzl.public。

舉個例子101就是1(launchMode="singleInstance")+0(multiprocess="false")+1(process:private)

最上面一行是啟動Activity的方式,分別是:

輔助應(yīng)用未運行,主應(yīng)用啟動自己的Activity;

輔助應(yīng)用已經(jīng)打開了主應(yīng)用的某個Activity,按home鍵,打開主應(yīng)用再去啟動這個Activity;

主應(yīng)用未運行,輔助應(yīng)用啟動主應(yīng)用的Activity;

主應(yīng)用已經(jīng)打開了自己的某個Activity,按home鍵,打開輔助應(yīng)用再去啟動這個Activity。

解釋完了下面來總結(jié)結(jié)論:

輔助應(yīng)用的進(jìn)程com.cxzl.processthirdstart出現(xiàn)次數(shù)為0,打破網(wǎng)上一些文章說配置了multiprocess="true",哪個應(yīng)用打開組件,組件就在哪個進(jìn)程的流言,實際上不論怎么配置:組件只存在于本應(yīng)用進(jìn)程或者本應(yīng)用process指定的進(jìn)程。

所有的情況下,設(shè)置了私有進(jìn)程或者公有進(jìn)程的表現(xiàn)都同步,都是要么是主應(yīng)用進(jìn)程,要么是各自的進(jìn)程,也就是說私有進(jìn)程和公有進(jìn)程在進(jìn)程創(chuàng)建上表現(xiàn)是相同的,只是進(jìn)程名不同。

所有編碼0結(jié)尾的行,啟動組件都是在主進(jìn)程,也就是說只要process沒有指定進(jìn)程,一定在主進(jìn)程。

對比B列和C列(都是啟動主應(yīng)用,C列多了第三方應(yīng)用干擾),只有111和112不同,也就是設(shè)置launchmodle為singInstance,multiprocess為true的情況下啟動組件,私有進(jìn)程和公有進(jìn)程會因為第三方應(yīng)用先打開了這個進(jìn)程,直接打開相同進(jìn)程,否則在主進(jìn)程中創(chuàng)建組件。

對比B列和D列(主應(yīng)用啟動和第三方應(yīng)用啟動),011,012和111,112不同,也就是multiprocess為true的情況,本應(yīng)用和第三方應(yīng)用打開同一個組件所在進(jìn)程不同,本應(yīng)用在主進(jìn)程,第三方應(yīng)用在組件自己聲明的進(jìn)程。

對比D列和E列(都是第三方應(yīng)用啟動,E列多了本應(yīng)用干擾),只有111和112不同,也就是設(shè)置launchmodle為singInstance,multiprocess為true的情況下啟動組件,私有進(jìn)程和公有進(jìn)程會因為本應(yīng)用先打開了這個進(jìn)程,直接打開相同進(jìn)程,否則在組件指定的進(jìn)程中創(chuàng)建組件。

對比001,002,101,102和011,012,111,112,會發(fā)現(xiàn)multiprocess為true會導(dǎo)致本應(yīng)用打開指定公有或者私有進(jìn)程的組件失效,還是出現(xiàn)在主進(jìn)程,但是第三方應(yīng)用打開不受影響。

對比000,001,002和100,101,102,發(fā)現(xiàn)是一模一樣,也就是說multiprocess為false的情況下,singleInstance不會影響組件所在進(jìn)程。

對比011,012和111,112,發(fā)現(xiàn)multiprocess為true的情況下,組件指定了私有或公有進(jìn)程,singInstance模式下,后運行的應(yīng)用會用先運行的應(yīng)用創(chuàng)建的進(jìn)程。

除了結(jié)論以外,還發(fā)現(xiàn)了兩個問題:

第三方應(yīng)用啟動multiprocess為true的組件,不開啟singleInstance的Activity連續(xù)打開了兩個頁面,進(jìn)程號相同但是hashcode不同。這個我還沒找出來原因,也沒發(fā)現(xiàn)網(wǎng)上有人討論這個問題,如果誰知道原因可以交流一下。

第三方應(yīng)用啟動singleInstance的activity,按android多任務(wù)鍵會出現(xiàn)兩個應(yīng)用,非singleInstance雖然創(chuàng)建了進(jìn)程,但是不會出現(xiàn)在任務(wù)列表。任務(wù)列表和進(jìn)程的關(guān)系是一個值得深入了解的事。

圖7

到這里這篇文章就結(jié)束了,希望能幫助大家理解android進(jìn)程機(jī)制,和多應(yīng)用多進(jìn)程之間的進(jìn)程關(guān)系。本系列第二篇文章正在準(zhǔn)備中:android進(jìn)程詳解(二):進(jìn)程與應(yīng)用生命周期的關(guān)系和進(jìn)程銷毀,敬請期待。

我的微信公眾號:程序之路,持續(xù)發(fā)布技術(shù)和成長文章,歡迎長按或者掃描二維碼關(guān)注


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容