進(jìn)程介紹
先強(qiáng)化兩個(gè)進(jìn)程相關(guān)的概念
進(jìn)程:進(jìn)程是一個(gè)具有一定獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合的一次運(yùn)行活動(dòng),它是操作系統(tǒng)動(dòng)態(tài)執(zhí)行的基本單元,是系統(tǒng)進(jìn)行資源分配的基本單位悦污,在面向線程設(shè)計(jì)的計(jì)算機(jī)結(jié)構(gòu)中是線程的容器代芜。
fork:fork函數(shù)UNIX及類UNIX系統(tǒng)中的分叉函數(shù)策州,一個(gè)現(xiàn)有進(jìn)程可以調(diào)用fork函數(shù)創(chuàng)建一個(gè)新進(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)程持有的是上述存儲(chǔ)空間的“副本”霜幼,這意味著父子進(jìn)程間不共享這些存儲(chǔ)空間。
Android進(jìn)程機(jī)制
下面介紹一下android的進(jìn)程機(jī)制誉尖,Android進(jìn)程實(shí)際上是Linux(類UNIX系統(tǒng))進(jìn)程罪既,在Linux中所有的進(jìn)程都是init進(jìn)程的子孫進(jìn)程,也就是說铡恕,所有的進(jìn)程都是直接或者間接地由init進(jìn)程fork出來的琢感。
在android系統(tǒng)中也有一個(gè)初始進(jìn)程:Zygote進(jìn)程,是在系統(tǒng)啟動(dòng)的過程探熔,由init進(jìn)程創(chuàng)建的驹针,啟動(dòng)Zygote之后, init進(jìn)程會(huì)啟動(dòng)runtime進(jìn)程。Zygote進(jìn)程負(fù)責(zé)后續(xù)Android應(yīng)用程序框架層的其它進(jìn)程的創(chuàng)建和啟動(dòng)工作诀艰。Zygote進(jìn)程初始化了第一個(gè)VM, 并且預(yù)加載了framework和App所需要的通用資源柬甥。它會(huì)創(chuàng)建一個(gè)超級(jí)管理進(jìn)程SystemServer進(jìn)程,SystemServer進(jìn)程會(huì)啟動(dòng)所有系統(tǒng)的核心服務(wù)其垄,如包管理服務(wù)PackageManagerService和應(yīng)用程序組件管理服務(wù)ActivityManagerService苛蒲,硬件相關(guān)的Service等。然后它開啟一個(gè)Socket接口來監(jiān)聽請(qǐng)求, 根據(jù)請(qǐng)求fork新的VM來管理新的App進(jìn)程. 一旦收到新的請(qǐng)求, Zygote會(huì)基于自身預(yù)先加載的VM來fork一個(gè)新的VM創(chuàng)建一個(gè)新的進(jìn)程绿满。需要啟動(dòng)一個(gè)Android應(yīng)用程序時(shí)臂外,ActivityManagerService會(huì)通過Socket進(jìn)程間通信機(jī)制,通知Zygote進(jìn)程為這個(gè)應(yīng)用程序創(chuàng)建一個(gè)新的進(jìn)程喇颁。
具體啟動(dòng)細(xì)節(jié):每一個(gè)App應(yīng)用都是由ActivityManagerService通過Socket與Zygote進(jìn)程進(jìn)行通信漏健,ActivityManagerService調(diào)用startProcessLocked()方法來創(chuàng)建新的進(jìn)程, 該方法會(huì)通過前面講到的socket通道傳遞參數(shù)給Zygote進(jìn)程。Zygote會(huì)fork一個(gè)子進(jìn)程出來作為這個(gè)即將要啟動(dòng)的應(yīng)用程序的進(jìn)程, 并調(diào)用ZygoteInit.main()方法來實(shí)例化ActivityThread對(duì)象并最終返回新進(jìn)程的pid橘霎。接下來要做的就是將進(jìn)程和指定的Application綁定起來. 這個(gè)是通過ActivityThread對(duì)象中調(diào)用bindApplication()方法完成的. 該方法發(fā)送一個(gè)BINDAPPLICATION的消息到消息隊(duì)列中, 最終通過handleBindApplication()方法處理該消息. 然后調(diào)用makeApplication()方法來加載App的classes到內(nèi)存中蔫浆。
經(jīng)過前兩個(gè)步驟之后, 系統(tǒng)已經(jīng)擁有了該application的進(jìn)程。后面的調(diào)用順序就是普通的從一個(gè)已經(jīng)存在的進(jìn)程中啟動(dòng)一個(gè)新進(jìn)程的Activity了茎毁。ActivityManagerService會(huì)通過Binder機(jī)制通知ActivityThread去創(chuàng)建需要的Activity克懊,實(shí)際調(diào)用方法是realStartActivity(), 它會(huì)調(diào)用application線程對(duì)象中的sheduleLaunchActivity()發(fā)送一個(gè)LAUNCHACTIVITY消息到消息隊(duì)列中, 通過handleLaunchActivity()來處理該消息。最后會(huì)輾轉(zhuǎn)到Instrumentation來創(chuàng)建Activity七蜘。
影響應(yīng)用進(jìn)程的因素
正常情況下一個(gè)android應(yīng)用只有一個(gè)進(jìn)程谭溉,進(jìn)程名就是應(yīng)用包名,android的application和四大組件都是在這個(gè)進(jìn)程橡卤。但可分別指定Application和四大組件運(yùn)行的進(jìn)程扮念,通過以下方式創(chuàng)建獨(dú)立進(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)建獨(dú)立進(jìn)程通常用來需要長期運(yùn)行不被系統(tǒng)回收的功能柜与,比如push功能。
除了android:process外嵌灰,android:multiprocess也能夠影響進(jìn)程創(chuàng)建弄匕,multiprocess顧名思義就是多進(jìn)程的意思测暗,這個(gè)參數(shù)只能在Activity和ContentProvider中定義垛孔。默認(rèn)是false,組件只會(huì)創(chuàng)建在申明它的app所在的進(jìn)程番刊。multiprocess="true"允許組件創(chuàng)建多個(gè)實(shí)例驹溃,在哪個(gè)進(jìn)程中調(diào)用組件就會(huì)使用該進(jìn)程中的組件實(shí)例城丧,并不會(huì)共用。
除了上述兩個(gè)直接影響進(jìn)程創(chuàng)建的配置外豌鹤,還有一個(gè)配置能夠間接的影響到進(jìn)程的創(chuàng)建亡哄,它就是android:launchMode="singleInstance",Android的Activity有四種啟動(dòng)方式布疙,除了singleInstance以外的三種全部都是在同一個(gè)Activity棧里面蚊惯,唯獨(dú)singeleInstance是創(chuàng)建獨(dú)立的棧,而且只有一份實(shí)例灵临,這就導(dǎo)致它能夠影響到進(jìn)程創(chuàng)建:因?yàn)樗candroid:multiprocess="true"這個(gè)屬性相沖突拣挪。既然它是在獨(dú)立的棧,那么兩個(gè)應(yīng)用打開它俱诸,它屬于哪個(gè)應(yīng)用的棧菠劝?哪個(gè)應(yīng)用的進(jìn)程?如果組件指定了自己的進(jìn)程睁搭,又會(huì)是什么樣的情況赶诊?
帶著這些疑問我在網(wǎng)上搜了一下,沒有找到完整的資料园骆,有的文章會(huì)根據(jù)某個(gè)點(diǎn)有一些分析舔痪,但不全面,而且不同文章觀點(diǎn)有沖突锌唾,對(duì)android:process锄码,android:multiprocess夺英,android:launchMode三個(gè)配置對(duì)一個(gè)組件所處的進(jìn)程的影響并沒有清晰結(jié)論。查看官方文檔滋捶,也只是單獨(dú)解釋這個(gè)三個(gè)配置的功能痛悯,并沒有說這些情況交叉在一起的影響。
進(jìn)程測(cè)試
所以我寫了兩個(gè)Demo來測(cè)試應(yīng)用中多進(jìn)程的情況.
一個(gè)主應(yīng)用(github地址https://github.com/cxzl/ProcessTestMain)重窟,包名com.cxzl.processtest载萌,包含了以下內(nèi)容:
MainActivity類,啟動(dòng)各個(gè)具體配置的Activity巡扇;
要啟動(dòng)的Activity基類扭仁,用來打印進(jìn)程的pid,進(jìn)程名和Activity的hashcode厅翔;
在AndroidManifest配置出來的各個(gè)Activity,全部繼承第二條的基類乖坠。
一個(gè)輔助應(yīng)用(github地址https://github.com/cxzl/ProcessTestAssist),包名com.cxzl.processthirdstart刀闷,只包含一個(gè)類瓤帚,就是MainActivity,用來啟動(dòng)主應(yīng)用AndroidManifest配置出來的各個(gè)Activity涩赢。
用這兩個(gè)應(yīng)用單獨(dú)和交叉啟動(dòng)各種配置的具體Activity戈次,來比較各種因素會(huì)對(duì)進(jìn)程創(chuàng)建的影響。測(cè)試界面選取了幾張截圖:
下面的表格是具體的測(cè)試結(jié)果:
?先解釋一下這個(gè)表格:
最左邊一行是對(duì)Activity三種配置的數(shù)字編碼筒扒,因?yàn)橛梦淖至信e這12種情況太復(fù)雜了怯邪,所以改成數(shù)字編碼。
第一個(gè)數(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武氓。
第三個(gè)數(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权逗。
舉個(gè)例子101就是1(launchMode="singleInstance")+0(multiprocess="false")+1(process:private)
最上面一行是啟動(dòng)Activity的方式美尸,分別是:
輔助應(yīng)用未運(yùn)行冤议,主應(yīng)用啟動(dòng)自己的Activity;
輔助應(yīng)用已經(jīng)打開了主應(yīng)用的某個(gè)Activity师坎,按home鍵恕酸,打開主應(yīng)用再去啟動(dòng)這個(gè)Activity;
主應(yīng)用未運(yùn)行屹耐,輔助應(yīng)用啟動(dòng)主應(yīng)用的Activity;
主應(yīng)用已經(jīng)打開了自己的某個(gè)Activity椿猎,按home鍵惶岭,打開輔助應(yīng)用再去啟動(dòng)這個(gè)Activity。
解釋完了下面來總結(jié)結(jié)論:
輔助應(yīng)用的進(jìn)程com.cxzl.processthirdstart出現(xiàn)次數(shù)為0犯眠,打破網(wǎng)上一些文章說配置了multiprocess="true",哪個(gè)應(yīng)用打開組件按灶,組件就在哪個(gè)進(jìn)程的流言,實(shí)際上不論怎么配置:組件只存在于本應(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é)尾的行韭赘,啟動(dòng)組件都是在主進(jìn)程,也就是說只要process沒有指定進(jìn)程势就,一定在主進(jìn)程泉瞻。
對(duì)比B列和C列(都是啟動(dòng)主應(yīng)用,C列多了第三方應(yīng)用干擾)苞冯,只有111和112不同袖牙,也就是設(shè)置launchmodle為singInstance,multiprocess為true的情況下啟動(dòng)組件舅锄,私有進(jìn)程和公有進(jìn)程會(huì)因?yàn)榈谌綉?yīng)用先打開了這個(gè)進(jìn)程鞭达,直接打開相同進(jìn)程,否則在主進(jìn)程中創(chuàng)建組件皇忿。
對(duì)比B列和D列(主應(yīng)用啟動(dòng)和第三方應(yīng)用啟動(dòng))碉怔,011,012和111禁添,112不同撮胧,也就是multiprocess為true的情況,本應(yīng)用和第三方應(yīng)用打開同一個(gè)組件所在進(jìn)程不同老翘,本應(yīng)用在主進(jìn)程芹啥,第三方應(yīng)用在組件自己聲明的進(jìn)程锻离。
對(duì)比D列和E列(都是第三方應(yīng)用啟動(dòng),E列多了本應(yīng)用干擾)墓怀,只有111和112不同汽纠,也就是設(shè)置launchmodle為singInstance,multiprocess為true的情況下啟動(dòng)組件傀履,私有進(jìn)程和公有進(jìn)程會(huì)因?yàn)楸緫?yīng)用先打開了這個(gè)進(jìn)程虱朵,直接打開相同進(jìn)程,否則在組件指定的進(jìn)程中創(chuàng)建組件钓账。
對(duì)比001碴犬,002,101梆暮,102和011服协,012,111啦粹,112偿荷,會(huì)發(fā)現(xiàn)multiprocess為true會(huì)導(dǎo)致本應(yīng)用打開指定公有或者私有進(jìn)程的組件失效,還是出現(xiàn)在主進(jìn)程唠椭,但是第三方應(yīng)用打開不受影響跳纳。
對(duì)比000,001贪嫂,002和100棒旗,101,102撩荣,發(fā)現(xiàn)是一模一樣铣揉,也就是說multiprocess為false的情況下,singleInstance不會(huì)影響組件所在進(jìn)程餐曹。
對(duì)比011逛拱,012和111,112台猴,發(fā)現(xiàn)multiprocess為true的情況下朽合,組件指定了私有或公有進(jìn)程,singInstance模式下饱狂,后運(yùn)行的應(yīng)用會(huì)用先運(yùn)行的應(yīng)用創(chuàng)建的進(jìn)程曹步。
除了結(jié)論以外,還發(fā)現(xiàn)了兩個(gè)問題:
第三方應(yīng)用啟動(dòng)multiprocess為true的組件休讳,不開啟singleInstance的Activity連續(xù)打開了兩個(gè)頁面讲婚,進(jìn)程號(hào)相同但是hashcode不同。這個(gè)我還沒找出來原因俊柔,也沒發(fā)現(xiàn)網(wǎng)上有人討論這個(gè)問題筹麸,如果誰知道原因可以交流一下活合。
第三方應(yīng)用啟動(dòng)singleInstance的activity,按android多任務(wù)鍵會(huì)出現(xiàn)兩個(gè)應(yīng)用物赶,非singleInstance雖然創(chuàng)建了進(jìn)程白指,但是不會(huì)出現(xiàn)在任務(wù)列表。任務(wù)列表和進(jìn)程的關(guān)系是一個(gè)值得深入了解的事酵紫。
圖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)程銷毀橄唬,敬請(qǐng)期待。
我的微信公眾號(hào):程序之路鹉动,持續(xù)發(fā)布技術(shù)和成長文章轧坎,歡迎長按或者掃描二維碼關(guān)注