筆記

第一章 Android 系統(tǒng)架構(gòu)

Android系統(tǒng)架構(gòu)分為五層魁亦,從上到下依次是:

  • 應(yīng)用層
  • 應(yīng)用框架層
  • 系統(tǒng)運(yùn)行庫(kù)層
  • 硬件抽象層
  • Linux內(nèi)核層

五層內(nèi)容:

①應(yīng)用層

主要是系統(tǒng)內(nèi)置的一些程序和非系統(tǒng)級(jí)的應(yīng)用程序都屬于應(yīng)用層落追。
比如:Activity Manager/Package Manager/Resource Manager/Window Manager/View System

②應(yīng)用框架層

提供開(kāi)發(fā)程序所需要的API,平常開(kāi)發(fā)調(diào)用的api都是這層的毛嫉。也叫Java Framewordk.

③系統(tǒng)運(yùn)行庫(kù)層

分為兩部分:C/C++程序庫(kù) & Android運(yùn)行時(shí)庫(kù)践啄。
C/C++程序庫(kù):是android各組件使用的,可以通過(guò)應(yīng)用程序框架給開(kāi)發(fā)者使用橄霉。比如SQLite就在其中嫌松。(現(xiàn)在市面上android數(shù)據(jù)庫(kù)框架雖然多沪曙,但是絕大多數(shù)都是SQLite的二次封裝,所以與其用框架萎羔,還不如把SQLite玩精了)
Android運(yùn)行時(shí)庫(kù):分為核心庫(kù)和ART液走。Android5.0的時(shí)候已經(jīng)用ART將Dalvik替換掉了。Dalvik的應(yīng)用每次運(yùn)行時(shí)贾陷,字節(jié)碼都需要通過(guò)即時(shí)編譯器(Just In Time,JIT)轉(zhuǎn)換為機(jī)器碼缘眶,這會(huì)使應(yīng)用的運(yùn)行效率降低。而在ART中髓废,系統(tǒng)在安裝應(yīng)用時(shí)會(huì)進(jìn)行一次預(yù)編譯(Ahead Of Time,AOT),將字節(jié)碼預(yù)先編譯成機(jī)器碼并存儲(chǔ)到本地巷懈,這樣每次運(yùn)行時(shí)就不需要執(zhí)行編譯了,運(yùn)行效率大大提升慌洪。

④硬件抽象層

位于操作系統(tǒng)內(nèi)核與硬件電路之間的接口層顶燕。目的在于將硬件抽象化。

⑤Linux內(nèi)核層

Android核心服務(wù)基于Linux內(nèi)核冈爹,在此基礎(chǔ)上添加了部分Android專(zhuān)用的驅(qū)動(dòng)涌攻。

在線(xiàn)閱讀源碼

Androidxref

第二章 Android系統(tǒng)啟動(dòng)

init進(jìn)程是Android系統(tǒng)中用戶(hù)控件的第一個(gè)進(jìn)程,進(jìn)程號(hào)為1犯助。極其重要癣漆,比如創(chuàng)建Zygote和屬性服務(wù)

init進(jìn)程啟動(dòng)過(guò)程

啟動(dòng)過(guò)程:當(dāng)我們按下啟動(dòng)電源時(shí),系統(tǒng)啟動(dòng)后會(huì)加載引導(dǎo)程序(BootLoader)剂买,引導(dǎo)程序又啟動(dòng)Linux內(nèi)核,在Linux內(nèi)核加載完成后癌蓖,就會(huì)在系統(tǒng)文件中尋找init.rc文件瞬哼,并啟動(dòng)init進(jìn)程。
init啟動(dòng)流程需要分析C++程序代碼租副,因本人沒(méi)有C++基礎(chǔ)坐慰,暫且略過(guò),只看一下init進(jìn)程中都做了什么東西用僧。
init進(jìn)程工作總結(jié):
(1)創(chuàng)建和掛載啟動(dòng)所需的文件目錄
(2)初始化和啟動(dòng)屬性服務(wù)
(3)解析init.rc配置文件并啟動(dòng)Zygote進(jìn)程

Zygote簡(jiǎn)述

在Android系統(tǒng)中结胀,DVM(Dalvik)和ART赞咙、應(yīng)用程序進(jìn)程以及運(yùn)行系統(tǒng)的關(guān)鍵服務(wù)的SystemServer進(jìn)程都是由Zygote進(jìn)程創(chuàng)建的,故我們也稱(chēng)其為孵化器糟港。
它通過(guò)fork(復(fù)制進(jìn)程)的形式來(lái)創(chuàng)建應(yīng)用程序進(jìn)程和SystemServer,因?yàn)橥ㄟ^(guò)fork來(lái)創(chuàng)建的攀操,所以這些進(jìn)程里會(huì)有一個(gè)DVM或者ART的副本。

Zygote啟動(dòng)流程

Zygote進(jìn)程會(huì)啟動(dòng)兩個(gè)秸抚,一個(gè)名稱(chēng)為zygote,執(zhí)行程序?yàn)閍pp_process32,作為主模式速和;一個(gè)名稱(chēng)為zygote_secondary,執(zhí)行程序?yàn)閍pp_process64,作為輔模式。


image.png

在app_main.cpp的main()方法中剥汤,如果zygote參數(shù)為true颠放,就說(shuō)明當(dāng)前運(yùn)行在Zygote進(jìn)程中,那么就會(huì)調(diào)用AppRuntime的start函數(shù)吭敢。這個(gè)start函數(shù)中就是Zygote啟動(dòng)的核心所在:


image.png

image.png

先是創(chuàng)建了Java虛擬機(jī)碰凶,接著為Java虛擬機(jī)注冊(cè)JNI方法。
接著通過(guò)JNI調(diào)用ZygoteInit的main方法鹿驼。這里要用JNI的原因:
因?yàn)閆ygoteInit的main方法是由Java語(yǔ)音編寫(xiě)的痒留,當(dāng)前的運(yùn)行邏輯是在Native中,這就需要通過(guò)JNI里來(lái)調(diào)用Java.這么一來(lái)蠢沿,Zygote就從Native層進(jìn)入到了Java框架層伸头。可以說(shuō)舷蟀,Zygote開(kāi)創(chuàng)了Java層恤磷。
接著我們進(jìn)入到ZygoteInit中的main方法,也就是啟動(dòng)Zygote的java層:

image.png

在ZygoteInit的main方法中野宜,同樣做了幾件十分重要的工作:
①創(chuàng)建了一個(gè)Server端的Socket,sockName為"zygote"
②預(yù)加載類(lèi)和資源
③啟動(dòng)SystemServer進(jìn)程(毫無(wú)疑問(wèn)扫步,是由Zygote進(jìn)程fork出來(lái)的)
④等待AMS請(qǐng)求創(chuàng)建新的應(yīng)用程序進(jìn)程(又是一個(gè)死循環(huán))
在Zygote進(jìn)程將SystemServer進(jìn)程啟動(dòng)后,就會(huì)在這個(gè)服務(wù)端的Socket上等待AMS請(qǐng)求Zygote進(jìn)程來(lái)創(chuàng)建新的應(yīng)用程序進(jìn)程匈子。

Zygote進(jìn)程啟動(dòng)總結(jié):
主要工作如下:
①創(chuàng)建AppRuntime并調(diào)用其start方法河胎,啟動(dòng)Zygote進(jìn)程
②創(chuàng)建Java虛擬機(jī)并為Java虛擬機(jī)注冊(cè)JNI方法
③通過(guò)JNI調(diào)用ZygoteInit的main函數(shù)進(jìn)入 Zygote的Java框架層
④創(chuàng)建服務(wù)端Socket,并等待AMS的請(qǐng)求來(lái)創(chuàng)建新的應(yīng)用程序進(jìn)程
⑤啟動(dòng)SystemServer進(jìn)程

SystemServer處理過(guò)程

SystemServer進(jìn)程主要用于創(chuàng)建系統(tǒng)服務(wù),我們熟知的AMS/WMS和PMS都是由它來(lái)創(chuàng)建的虎敦!掌握SystemServer進(jìn)程如何啟動(dòng)游岳,以及它在啟動(dòng)時(shí)做了什么工作十分必要。

image.png

在ZygoteInit中調(diào)用handleSystemServerProcess()方法來(lái)啟動(dòng)SystemServer進(jìn)程其徙。
image.png

image.png

在①處創(chuàng)建了PathClassLoader.
在②處調(diào)用的zygoteInit方法內(nèi)部胚迫,會(huì)調(diào)用

ZygoteInit.nativeZygoteInit();

從方法名就可以看出,調(diào)用的是native方法唾那。這個(gè)方法就是為了啟動(dòng)Binder線(xiàn)程池可以去看對(duì)應(yīng)的JNI文件访锻,因?yàn)楸救藢?duì)JNI不熟,所以后續(xù)再來(lái)完善內(nèi)部方法分析。啟動(dòng)了Binder線(xiàn)程池后期犬,SystemServer進(jìn)程就可以使用Binder與其他進(jìn)程進(jìn)行通信了河哑。
在nativeZygoteInit()方法下的applicationInit方法

image.png

內(nèi)部會(huì)通過(guò)反射來(lái)創(chuàng)建SystemServer,并調(diào)用其main方法 :
image.png

image.png

image.png

①創(chuàng)建消息Looper
②加載了動(dòng)態(tài)so庫(kù)libandroid_servers.so
③創(chuàng)建系統(tǒng)的Context
④啟動(dòng)引導(dǎo)、核心龟虎、其他服務(wù)

總而言之昧港,SystemServer進(jìn)程主要做了如下三件事:
**
①啟動(dòng)Binder線(xiàn)程池犬第,這樣就可以與其他進(jìn)程進(jìn)行通信
②創(chuàng)建SystemServiceManager,其用于對(duì)系統(tǒng)的服務(wù)進(jìn)行創(chuàng)建、啟動(dòng)和生命周期管理
③啟動(dòng)各種系統(tǒng)服務(wù)(AMS/PMS/WMS等核心服務(wù)由此創(chuàng)建!u馄簟G锩Α)
**

Launcher啟動(dòng)過(guò)程

上面學(xué)習(xí)了init進(jìn)程悬包、Zygote進(jìn)程和SystemServer進(jìn)程裁厅,系統(tǒng)啟動(dòng)的最后一步就是我們的Launcher進(jìn)程了。
啟動(dòng)一個(gè)應(yīng)用程序來(lái)顯示系統(tǒng)中已經(jīng)安裝的應(yīng)用程序這個(gè)應(yīng)用程序就叫做launcher垂券。
Launcher在啟動(dòng)過(guò)程中會(huì)請(qǐng)求PackageManagerService花盐,PMS返回應(yīng)用程序信息,Launcher負(fù)責(zé)展示這些應(yīng)用程序圖標(biāo)菇爪。
Launcher主要作用有兩點(diǎn):
**
①作為Android系統(tǒng)的啟動(dòng)器算芯,用于啟動(dòng)應(yīng)用程序。
②作為Android系統(tǒng)的桌面凳宙,用于顯示和管理應(yīng)用程序的快捷圖標(biāo)及其他桌面組件熙揍。
**

image.png

Launcher入口為AMS的systemReady()方法,它在SystemServer的startOtherServices中被調(diào)用氏涩。
怎么說(shuō)呢届囚,Launcher啟動(dòng)流程后續(xù)補(bǔ)充...這塊不好整理

Android系統(tǒng)啟動(dòng)流程【從按下開(kāi)機(jī)鍵開(kāi)始,Android系統(tǒng)都做了什么是尖?】

1.啟動(dòng)電源以及系統(tǒng)啟動(dòng)
當(dāng)電源按下時(shí)意系,加載引導(dǎo)程序BootLoader到RAM,然后執(zhí)行饺汹。BootLoader是一個(gè)小程序蛔添,主要作用就是將系統(tǒng)OS拉起來(lái)。
2.Linux內(nèi)核啟動(dòng)
內(nèi)核完成系統(tǒng)設(shè)置后兜辞,會(huì)在系統(tǒng)文件中尋找init.rc文件迎瞧,啟動(dòng)init進(jìn)程。
3.init進(jìn)程啟動(dòng)
初始化和啟動(dòng)屬性服務(wù)弦疮,啟動(dòng)Zygote進(jìn)程夹攒。
4.Zygote進(jìn)程啟動(dòng)
創(chuàng)建Java虛擬機(jī)并為Java虛擬機(jī)注冊(cè)JNI方法,創(chuàng)建服務(wù)端Socket,啟動(dòng)SystemServer進(jìn)程胁塞。
5.SystemServer進(jìn)程啟動(dòng)
啟動(dòng)Binder線(xiàn)程池和SystemServiceManager,并且啟動(dòng)各種系統(tǒng)服務(wù)。(PMS/WMS/AMS等)
6.Launcher啟動(dòng)
被System進(jìn)程啟動(dòng)的AMS會(huì)啟動(dòng)Launcher,Launcher啟動(dòng)后會(huì)將已安裝的快捷圖標(biāo)顯示到界面上啸罢。
流程圖如下:

image.png

第三章 應(yīng)用程序進(jìn)程啟動(dòng)過(guò)程

上一章講解了Android系統(tǒng)如何啟動(dòng)的编检,這一章我們就來(lái)看一下"應(yīng)用程序進(jìn)程啟動(dòng)流程",注意不是"應(yīng)用程序啟動(dòng)過(guò)程"扰才。這個(gè)知識(shí)點(diǎn)雖然沒(méi)有后面提及的多允懂,但也是大廠(chǎng)面試常客了衩匣,不要和后者混淆就好蕾总。

應(yīng)用程序進(jìn)程簡(jiǎn)介:
AMS在啟動(dòng)應(yīng)用程序之前會(huì)檢查這個(gè)應(yīng)用程序所需的進(jìn)程是否存在,如果沒(méi)有就會(huì)請(qǐng)求Zygote進(jìn)程啟動(dòng)所需的應(yīng)用程序進(jìn)程琅捏。之前說(shuō)過(guò)在Zygote的Java層生百,也就是ZygoteInit中會(huì)創(chuàng)建一個(gè)服務(wù)端的Socket,這個(gè)Socket柄延,就是用來(lái)等待AMS向Zygote請(qǐng)求創(chuàng)建應(yīng)用程序進(jìn)程蚀浆。Zygote進(jìn)程通過(guò)fork自身創(chuàng)建應(yīng)用程序進(jìn)程,同時(shí)創(chuàng)建Binder線(xiàn)程池和消息循環(huán)搜吧。這樣我們的應(yīng)用程序就可以使用Binder進(jìn)行進(jìn)程間通信了市俊。
應(yīng)用程序進(jìn)程啟動(dòng)主要分為兩部分:
①AMS發(fā)送啟動(dòng)應(yīng)用程序進(jìn)程請(qǐng)求
②Zygote接收請(qǐng)求并創(chuàng)建應(yīng)用程序進(jìn)程

AMS發(fā)送啟動(dòng)應(yīng)用程序進(jìn)程請(qǐng)求

image.png

AMS 通過(guò)調(diào)用startProcessLocked方法向Zygote進(jìn)程發(fā)送請(qǐng)求,


image.png

image.png

image.png

拿到用戶(hù)id(uid)和用戶(hù)組ID(gid)滤奈,調(diào)用Process.start方法摆昧。接著會(huì)調(diào)用ZygoteProcess里的start方法,ZygoteProcess用于保持與Zygote進(jìn)程的通信狀態(tài)蜒程。

image.png

start方法里最終會(huì)會(huì)走ZygoteProcess里的startViaZygote(~)方法中绅你,可以看到這個(gè)方法的注釋?zhuān)和ㄟ^(guò)zygote機(jī)制創(chuàng)建一個(gè)新的進(jìn)程!可以看出來(lái)搞糕,此方法就是我們應(yīng)用程序創(chuàng)建的核心流程所在了勇吊。
argsForZygote字符串列表會(huì)存儲(chǔ)應(yīng)用進(jìn)程的啟動(dòng)參數(shù)。
從上圖還可以看出有個(gè)位運(yùn)算實(shí)際運(yùn)用的場(chǎng)景:控制權(quán)限窍仰。
可以通過(guò)與汉规、或運(yùn)算來(lái)控制權(quán)限,在權(quán)限較多的情況下驹吮,通過(guò)位運(yùn)算來(lái)控制權(quán)限十分方便针史。這里提供一個(gè)通過(guò)位運(yùn)算來(lái)控制權(quán)限的工具類(lèi):

public class NewPermission {
    //通過(guò)二進(jìn)制位來(lái)判斷權(quán)限,比如1111碟狞,說(shuō)明所有權(quán)限都有啄枕;1110,只有除了查詢(xún)之外的權(quán)限族沃;
    //1010,只有刪除和插入權(quán)限频祝,so on...

    public static final int ALLOW_SELECT = 1 << 0; // 0001
    public static final int ALLOW_INSERT = 1 << 1; // 0010
    public static final int ALLOW_UPDATE = 1 << 2; // 0100
    public static final int ALLOW_DELETE = 1 << 3; // 1000
    //存儲(chǔ)目前權(quán)限狀態(tài)
    private int flag;

    public int getCurrentPermission(){
        return flag;
    }

    public void resetPermission(int permission) {
        flag = permission;
    }

    //添加一項(xiàng)或者多項(xiàng)權(quán)限
    public void enable(int permission) {
        flag |= permission;
    }

    //移除一項(xiàng)或者多項(xiàng)權(quán)限
    public void disable(int permission) {
        flag &= ~permission;
    }

    //是否有用某項(xiàng)權(quán)限
    public boolean hasPermission(int permission) {
        return (flag & permission) == permission;
    }

    //是否禁用了某些權(quán)限
    public boolean hasNotPermission(int permission) {
        return (flag & permission) == 0;
    }

    //是否禁擁有某些權(quán)限
    public boolean isOnlyAllow(int permission) {
        return flag == permission;
    }


}

使用:

image.png

也可以像源碼中一樣通過(guò)與運(yùn)算泌参,來(lái)判斷是否擁有某種權(quán)限,十分的方便常空!
言歸正傳:
image.png

image.png

openZygoteSocketIfNeeded方法中沽一,注釋①處,AMS會(huì)與Zygote建立Socket連接漓糙。此方法返回的ZygoteState是ZygoteProcess的靜態(tài)內(nèi)部類(lèi)铣缠,用于表示與Zygote進(jìn)程通信的狀態(tài)。
如果通過(guò)zygote主模式建立的socket與啟動(dòng)應(yīng)用程序所需的ABI不一致昆禽,就會(huì)啟動(dòng)輔模式來(lái)進(jìn)行AMS和Zygote的socket連接蝗蛙。都失敗,就會(huì)拋出ZygoteStartFailedException異常醉鳖。
到這里捡硅,實(shí)際上我們的第一步就完成了,也就是AMS向Zygote發(fā)送啟動(dòng)應(yīng)用程序進(jìn)程請(qǐng)求辐棒。

Zygote接受請(qǐng)求并創(chuàng)建應(yīng)用程序進(jìn)程

image.png

時(shí)序圖如上病曾。
我們回到之前說(shuō)的Zygote進(jìn)程創(chuàng)建的時(shí)間點(diǎn),在Zygote的Java層漾根,也就是ZygoteInit的main方法中曾創(chuàng)建了一個(gè)服務(wù)端的Socket:
image.png

① 創(chuàng)建了一個(gè)Server端的Socket泰涂,socketName的值為"zygote"
②預(yù)加載類(lèi)和資源
開(kāi)啟死循環(huán)等待AMS請(qǐng)求
image.png

可以看到while(true)這個(gè)Socket會(huì)一直運(yùn)行等待新的請(qǐng)求。同時(shí)我們也可以看到辐怕,當(dāng)有新的連接建立時(shí)逼蒙,會(huì)調(diào)用ZygoteConnection的runOnce方法來(lái)處理AMS發(fā)過(guò)來(lái)的請(qǐng)求數(shù)據(jù)
image.png

獲取應(yīng)用程序進(jìn)程的啟動(dòng)參數(shù)寄疏,如果參數(shù)為空是牢,直接關(guān)閉本次socket連接。

image.png

①處調(diào)用Zygote.forkAndSpecialize方法陕截,傳入用戶(hù)的uid驳棱、用戶(hù)組id,創(chuàng)建應(yīng)用程序進(jìn)程农曲,并返回進(jìn)程id(pid).此方法主要就是通過(guò)fork當(dāng)前進(jìn)程來(lái)創(chuàng)建一個(gè)子進(jìn)程的社搅。
②處,pid等于0就表明應(yīng)用程序進(jìn)程創(chuàng)建成功了乳规!接著會(huì)調(diào)用handleChildProc方法來(lái)處理我們的應(yīng)用程序進(jìn)程形葬。 如注釋所以,如果pid<0暮的,則說(shuō)明創(chuàng)建失敗了笙以。
handleChildProc方法中會(huì)調(diào)用ZygoteInit.zygoteInit()方法,如下圖:

image.png

注釋1處冻辩,也就是ZygoteInit.nativeZygoteInit(),會(huì)在新創(chuàng)建的應(yīng)用程序中創(chuàng)建Binder線(xiàn)程池猖腕。
注釋2處拆祈,調(diào)用RuntimeInit的applicationInit()方法,顧名思義谈息,此方法用于在新的應(yīng)用進(jìn)程中創(chuàng)建Application.此方法會(huì)調(diào)用invokeStaticMain方法:
c

image.png

image.png

通過(guò)反射獲得了android.app.ActivityThread(①),并獲取ActivityThread的main方法(②)缘屹。拋出異常被ZygoteInit的main方法捕獲后會(huì)調(diào)用invoke ActivityThread的 main方法凛剥,這樣應(yīng)用程序進(jìn)程就創(chuàng)建完成了并且運(yùn)行了主線(xiàn)程的管理類(lèi)ActivityThread.

Binder線(xiàn)程池啟動(dòng)過(guò)程

前面說(shuō)過(guò)ZygoteInit的zygoteInit方法中會(huì)調(diào)用nativeZygoteInit()方法來(lái)創(chuàng)建Binder線(xiàn)程池


image.png

可以看出這個(gè)一個(gè)JNI方法,會(huì)調(diào)用到ProcessState.cpp中的startThreadPool方法來(lái)創(chuàng)建Binder線(xiàn)程池:


image.png

image.png

重點(diǎn)來(lái)了:
支持Binder通信的進(jìn)程中都有一個(gè)ProcessState類(lèi)侠仇,它里面有個(gè)mThreadPoolStarted變量,用來(lái)表示Binder線(xiàn)程池是否已經(jīng)被啟動(dòng)過(guò)犁珠,默認(rèn)值為false逻炊。在每次調(diào)用startThreadPool函數(shù)時(shí),都會(huì)檢查這個(gè)標(biāo)記犁享,確定一個(gè)應(yīng)用程序進(jìn)程的Binder線(xiàn)程池只會(huì)被啟動(dòng)一次余素。如果未被啟動(dòng),就會(huì)調(diào)用spawnPooledThread函數(shù)來(lái)創(chuàng)建線(xiàn)程池中的第一個(gè)線(xiàn)程炊昆,也就是線(xiàn)程池的主線(xiàn)程桨吊。
Binder線(xiàn)程為一個(gè)PoolThread,run方法中會(huì)調(diào)用IPCThreadState的joinThreadPool函數(shù),將當(dāng)前線(xiàn)程也就是Binder主線(xiàn)程注冊(cè)到Binder驅(qū)動(dòng)程序中凤巨,這樣我們創(chuàng)建的線(xiàn)程就加入了Binder線(xiàn)程池中视乐,新創(chuàng)建的應(yīng)用程序進(jìn)程就支持Binder進(jìn)程通信了。

消息循環(huán)創(chuàng)建過(guò)程

應(yīng)用程序進(jìn)程啟動(dòng)后會(huì)創(chuàng)建消息循環(huán)敢茁。前面我們講Zygote進(jìn)程創(chuàng)建應(yīng)用程序進(jìn)程時(shí)佑淀,最后會(huì)調(diào)用ActivityThread的main方法,創(chuàng)建應(yīng)用程序的主線(xiàn)程彰檬。消息循環(huán)的創(chuàng)建就在此main方法中:

image.png

首先調(diào)用Looper.prepareMainLooper()方法伸刃,創(chuàng)建主線(xiàn)程Looper.之后thread.getHandler()會(huì)創(chuàng)建主線(xiàn)程H類(lèi),這是ActivityThread的內(nèi)部類(lèi)逢倍,用于處理主線(xiàn)程的消息循環(huán)捧颅!最后調(diào)用Looper.loop()方法開(kāi)始工作,進(jìn)入消息循環(huán)较雕。

四大組件的工作過(guò)程(activity等啟動(dòng)流程)

本章十分十分十分重要碉哑!是整個(gè)Android知識(shí)體系的核心內(nèi)容之一,對(duì)于理解和掌握整個(gè)Android知識(shí)體系起著重大的作用@砂省谭梗!同時(shí)本章也是理解插件化原理的必知點(diǎn)。
Activity的啟動(dòng)流程分為兩種:
1.根Activity的啟動(dòng)過(guò)程
2.普通Activity的啟動(dòng)流程宛蚓,也就是從一個(gè)Activity跳到另外一個(gè)Activity
我們要分析的就是根Activity的啟動(dòng)流程激捏。

根Activity的啟動(dòng)流程

根Activity的啟動(dòng)流程娱局,分為三個(gè)部分:
①分別是Launcher請(qǐng)求AMS過(guò)程
②AMS到ApplicationThread的調(diào)用過(guò)程
③ActivityThread啟動(dòng)Activity

1.Launcher請(qǐng)求AMS過(guò)程
P82頁(yè)時(shí)序圖

image.png

當(dāng)我們點(diǎn)擊應(yīng)用程序的快捷圖標(biāo)時(shí)修赞,就會(huì)調(diào)用Launcher的startActivitySafely方法
將FLAG設(shè)置為Intent.FLAG_ACTIVITY_NEW_TASK杈帐,這樣根Activity會(huì)在新的任務(wù)棧中啟動(dòng)僵控。

startActivity會(huì)調(diào)用startActivityForResult方法,接著會(huì)調(diào)用Instrumentation的execStartActivity。Instrumentation主要用來(lái)監(jiān)控應(yīng)用程序和系統(tǒng)的交互图柏。
image.png

image.png

首先調(diào)用ActivityManage的getService方法獲取到AMS的代理對(duì)象:ActivityManagerProxy序六。這里的單例對(duì)象創(chuàng)建的過(guò)程中使用的是AIDL技術(shù),這是8.0新增的蚤吹,8.0之前用的是類(lèi)似AIDL的形式例诀。總之execStartActivity方法就是通過(guò)AIDL拿到AMS,調(diào)用其startActivity方法裁着。
2.AMS到ApplicationThread的調(diào)用過(guò)程
P86時(shí)序圖

image.png

ActivityManagerService中的startActivity方法會(huì)調(diào)用startActivityAsUser,這個(gè)方法會(huì)獲得調(diào)用者的UserId繁涂,通過(guò)此ID來(lái)確定調(diào)用者的權(quán)限。在此之前的方法會(huì)判斷調(diào)用者進(jìn)程是否被隔離二驰,兩者都可能會(huì)拋出SecurityException.
然后會(huì)調(diào)用ActivityStarter里的startActivity方法扔罪。[ActivityStarter是7.0新增的類(lèi),是加載Activity的控制類(lèi)桶雀。此類(lèi)會(huì)收集所有的邏輯來(lái)決定如何將Intent和Flags轉(zhuǎn)換為Activity]

image.png

image.png

此方法中矿酵,通過(guò)mService.getRecordForAppLocked得到Launcher進(jìn)程的ProcessRecord【ProcessRecord用于描述進(jìn)程信息】,如果進(jìn)程不存在就會(huì)將狀態(tài)置為START_PERMISSION_DENIED.
接著會(huì)創(chuàng)建即將要啟動(dòng)的Activity的描述類(lèi)ActivityRecord矗积。startActivity方法調(diào)用了startActivityUnchecked方法
image.png

其內(nèi)部會(huì)創(chuàng)建TaskRecord,用來(lái)描述一個(gè)Activity任務(wù)棧全肮,這個(gè)任務(wù)棧是一個(gè)假想的模型,并不真實(shí)存在漠魏。
image.png

在創(chuàng)建好兩個(gè)Record之后倔矾,在ActivityStack中判斷當(dāng)前要啟動(dòng)Activity對(duì)應(yīng)的ActivityRecord是否為空,如果不為空或者Activity的狀態(tài)不為resumed,就會(huì)走到ActivityStackSupervisor的startSpecificActivityLocked方法中去:
image.png

此方法中會(huì)獲取即將啟動(dòng)的Activity所在的應(yīng)用程序進(jìn)程柱锹,并判斷要啟動(dòng)的Activity所在的應(yīng)用程序進(jìn)程是否已經(jīng)運(yùn)行哪自,如果已經(jīng)運(yùn)行就會(huì)調(diào)用realStartActivityLocked方法
image.png

image.png

此方法調(diào)用的app.thread.scheduleLauncheActivity方法中的app.thread指的是IApplicationThread,它的實(shí)現(xiàn)是ActivityThread的內(nèi)部類(lèi)ApplicationThread,以上的代碼都運(yùn)行在AMS所在的進(jìn)程中禁熏,也就是SystemServer進(jìn)程中壤巷。

image.png

ApplicationThread繼承了IApplicationThread.Stub[Binder機(jī)制],可以看出AMS就是通過(guò)Binder機(jī)制來(lái)與應(yīng)用程序進(jìn)行通信。ApplicationThread就是AMS所在進(jìn)程(SystemServer )與應(yīng)用程序進(jìn)程的通信橋梁瞧毙。

ActivityThread啟動(dòng)Activity的過(guò)程

時(shí)序圖

1417629-50a4962ca419692e.png

上面我們走到了ApplicationThread的scheduleLaunchActivity里胧华,ApplicationThread是ActivityThread的內(nèi)部類(lèi)。
image.png

此方法主要就是將啟動(dòng)Activity的參數(shù)封裝成ActivityClientRecord.發(fā)送LAUNCH_ACTIVITY消息給H類(lèi)宙彪,此H類(lèi)是ActivityThread的內(nèi)部類(lèi)并繼承Handler,它是應(yīng)用程序進(jìn)程中主線(xiàn)程的消息管理類(lèi)矩动。因?yàn)锳pplicationThread是一個(gè)Binder,它的調(diào)用邏輯運(yùn)行在Binder線(xiàn)程池中,所以我們需要將H代碼的邏輯切換到主線(xiàn)程中释漆。
友情提示悲没,9.0已經(jīng)去除了H類(lèi)。
image.png

H類(lèi)中接收到LAUNCH_ACTIVITY消息后男图,解析參數(shù):將LoadedApk類(lèi)型的對(duì)象賦值給ActivityClientRecord的成員變量packageInfo.應(yīng)用程序進(jìn)程要啟動(dòng)Activity時(shí)需要將該Activity所屬的APK加載進(jìn)來(lái)示姿,而LoadedApk就是用來(lái)描述已加載的APK文件的甜橱。
接著調(diào)用handleLaunchActivity方法
image.png

此方法中的performLaunchActivity用來(lái)啟動(dòng)Activity。handleResumeActivity用來(lái)將Activity的狀態(tài)置為Resume.如果activity為null栈戳,就會(huì)通知AMS停止啟動(dòng)Activity.
看一下啟動(dòng)Activity的核心方法:performLaunchActivity.這個(gè)方法十分重要岂傲,我們一步一步來(lái):
image.png

**
①獲取ActivityInfo,存儲(chǔ)AndroidManifes設(shè)置的Activity和Receiver節(jié)點(diǎn)信息
②獲取APK文件的描述類(lèi)LoadedApk.
③獲取要啟動(dòng)Activity的ComponentName類(lèi)。在此類(lèi)中存儲(chǔ)了Activity的包名和類(lèi)名子檀。
④創(chuàng)建Activity的上下文環(huán)境镊掖。
⑤根據(jù)ComponentName中存儲(chǔ)的Activity類(lèi)名,用類(lèi)加載器來(lái)創(chuàng)建Activity實(shí)例命锄。
**
image.png

image.png

image.png

image.png

**
⑥makeApplication創(chuàng)建Application,內(nèi)部會(huì)調(diào)用Application的onCreate
⑦創(chuàng)建Window對(duì)象[PhoneWindow]并與Activity自身進(jìn)行關(guān)聯(lián)
⑧最后調(diào)用Instrumentation的callActivityOnCreate方法來(lái)啟動(dòng)Activity堰乔!(此方法中會(huì)調(diào)用performCreate方法,內(nèi)部會(huì)調(diào)用Activity的onCreate方法脐恩。)
**
至此,簡(jiǎn)單分析根Activity的啟動(dòng)流程已經(jīng)OK了侦讨。
還有一些疑惑點(diǎn):// TODO

①比如清單文件里的各種屬性值是怎么存到ActivityInfo里驶冒,每個(gè)字段值對(duì)應(yīng)的邏輯是如何處理的,主Activity那個(gè)字段怎么判斷的韵卤;
②Activity除了onCreate之外的其他生命周期如何調(diào)用
后續(xù)補(bǔ)上骗污。

根Activity啟動(dòng)過(guò)程中涉及的進(jìn)程

根Activity啟動(dòng)過(guò)程中會(huì)涉及4個(gè)進(jìn)程:
Zygote進(jìn)程、Launcher進(jìn)程沈条、AMS所在進(jìn)程(SystemServer進(jìn)程)需忿、應(yīng)用程序進(jìn)程(主進(jìn)程)
根Activity啟動(dòng)涉及進(jìn)程圖

image.png

image.png

首先Launcher進(jìn)程向AMS請(qǐng)求創(chuàng)建根Activity,AMS會(huì)判斷根Activity所需的應(yīng)用程序進(jìn)程是否存在并啟動(dòng),如果不存在就會(huì)請(qǐng)求Zygote進(jìn)程創(chuàng)建應(yīng)用程序進(jìn)程蜡歹。應(yīng)用程序進(jìn)程啟動(dòng)后,AMS會(huì)請(qǐng)求創(chuàng)建應(yīng)用程序進(jìn)程并啟動(dòng)根Activity.其中AMS與Zygote采用的是Socket通信屋厘。1和4,也就是Launcher進(jìn)程和AMS進(jìn)程通信以及AMS進(jìn)程與應(yīng)用進(jìn)程通信采用的是Binder通信月而。

如果是普通Activity啟動(dòng)汗洒,涉及到的進(jìn)程就兩個(gè):AMS所在進(jìn)程和應(yīng)用程序進(jìn)程。

Service的啟動(dòng)過(guò)程

service的啟動(dòng)過(guò)程主要分為兩個(gè)部分:
**
①ContextImpl到ActivityManagerService的調(diào)用過(guò)程
②ActivityThread啟動(dòng)Service
**

ContextImpl到AMS的調(diào)用過(guò)程

image.png

要啟動(dòng)service父款,我們會(huì)調(diào)用startService方法溢谤,它在ContextWrapper中實(shí)現(xiàn):
image.png

這里的mBase,實(shí)際上就是我們ActivityThread的內(nèi)部類(lèi)H調(diào)用的performLaunchActivity里創(chuàng)建的:
image.png

image.png

在Activity的attach方法中將ContextImpl賦值給ContextWrapper的成員變量mBase.mBase指向的就是ContexImpl憨攒。也就是說(shuō)startService的實(shí)際邏輯在ContextImpl的startService方法中世杀!
image.png

調(diào)用的是AMS的startService方法
AMS的startService方法中調(diào)用ActiveService的startServiceLocked方法。
image.png

此方法會(huì)查找是否有與參數(shù)service對(duì)應(yīng)的ServiceRecord肝集,如果沒(méi)有找到則調(diào)用PackageManagerService去獲取參數(shù)service對(duì)應(yīng)的Service信息瞻坝,并將其封裝到ServiceRecord中,最后將ServiceRecord封裝為ServiceLookupResult返回包晰。
再拿到ServiceRecord后湿镀,在方法最底部會(huì)調(diào)用startServiceInnerLocked方法:
內(nèi)部會(huì)接著調(diào)用bringUpServiceLocked方法
image.png

①處就是用來(lái)描述描述Service想要在哪個(gè)進(jìn)程中運(yùn)行炕吸,默認(rèn)是當(dāng)前進(jìn)程∶愠眨可以在清單文件中設(shè)置android:process屬性來(lái)開(kāi)啟一個(gè)新的進(jìn)程赫模。
②處查詢(xún)是否存在一個(gè)與Service對(duì)應(yīng)的ProcessRecord類(lèi)型的對(duì)象app,ProcessRecord主要用來(lái)描述運(yùn)行的應(yīng)用程序進(jìn)程信息蒸矛。
此方法就是用來(lái)判斷運(yùn)行Service的應(yīng)用程序進(jìn)程是否存在瀑罗,如果不存在則調(diào)用AMS的startProcessLocked方法來(lái)創(chuàng)建對(duì)應(yīng)進(jìn)程。如果存在則調(diào)用realStartServiceLocked方法來(lái)啟動(dòng)Service.如下圖所示
image.png

image.png

image.png

內(nèi)部會(huì)調(diào)用ApplicationThread的scheduleCreateService()方法
image.png

將參數(shù)封裝成CreateServiceData,發(fā)送事件給H類(lèi)雏掠;跟Activity的啟動(dòng)流程類(lèi)似了斩祭,所以其實(shí)現(xiàn)還是在H類(lèi)中的handleMessage方法中:
image.png

調(diào)用handleCreateService()方法:
image.png

獲取APK文件的描述類(lèi)LoadedApk,從而獲取到類(lèi)加載器,創(chuàng)建Service實(shí)例乡话。創(chuàng)建Service的上下文環(huán)境ContextImpl對(duì)象摧玫,作為參數(shù)傳入attach方法中來(lái)初始化Service,接著調(diào)用Service的onCreate方法绑青,這樣Service就啟動(dòng)了诬像。
Service啟動(dòng)流程大概是這樣的,下面我們看一下Service的綁定過(guò)程闸婴。

Service的綁定過(guò)程

綁定Service的過(guò)程比啟動(dòng)Service的過(guò)程更復(fù)雜一些坏挠。
綁定Service分為兩個(gè)部分:
1.ContextImpl到AMS的調(diào)用過(guò)程
2.Service的綁定過(guò)程

ContextImpl到AMS的調(diào)用過(guò)程

bindService里的實(shí)現(xiàn)也是在ContextImpl里,其內(nèi)部調(diào)用了bindServiceCommo方法:

image.png

此方法中將ServiceConnection封裝成IServiceConnection,實(shí)現(xiàn)了Binder機(jī)制邪乍,這樣service的綁定過(guò)程就支持了跨進(jìn)程降狠。接著調(diào)用AMS的bindService方法。
接下來(lái)會(huì)調(diào)用bindServiceLocked-->bringUpServiceLocked方法-->realStartServiceLocked方法
最終由ActivityThread來(lái)調(diào)用Service的onCreate方法啟動(dòng)Service庇楞,說(shuō)明bindService里也會(huì)啟動(dòng)Service.
image.png

AppBindRecord中intent的received存儲(chǔ)了當(dāng)前應(yīng)用程序進(jìn)程已經(jīng)接收到綁定Service時(shí)返回的Binder榜配。這樣應(yīng)用程序進(jìn)程就可以通過(guò)Binder來(lái)獲取要綁定的Service的訪(fǎng)問(wèn)接口。(AppBindRecord維護(hù)Service與應(yīng)用程序進(jìn)程之間的關(guān)聯(lián)姐刁。)
TODO//為了趕進(jìn)程芥牌,Service這塊我們只撿一些核心的說(shuō),準(zhǔn)備一個(gè)月投簡(jiǎn)歷聂使,這本書(shū)不能細(xì)讀了壁拉。
接著會(huì)調(diào)用到ApplicationThread里的scheduleBindService方法,其內(nèi)部會(huì)將Service的信息封裝成BindServiceData對(duì)象發(fā)送BIND_SERVICE事件到H類(lèi)中柏靶,在handleMessage里調(diào)用handleBindSerivce方法,根據(jù)BindServiceData里的rebind方法來(lái)判斷是否已經(jīng)綁定過(guò)Service
image.png

如果未綁定的話(huà)弃理,會(huì)調(diào)用AMS的publishService方法。接著調(diào)用ServiceDispatcher.InnerConnection里的connect方法屎蜓,此方法內(nèi)部會(huì)調(diào)用H類(lèi)的post方法將RunConnection對(duì)象的內(nèi)容運(yùn)行在主線(xiàn)程痘昌。RunConnection實(shí)現(xiàn)了Runnable接口,其內(nèi)部的doConnect方法調(diào)用了ServiceConnection里的onServiceConnected方法。這樣ServiceConnection接口類(lèi)的onServiceConnected方法就會(huì)被執(zhí)行辆苔,Service的綁定過(guò)程也就完成了算灸。

廣播的注冊(cè)、發(fā)送和接收過(guò)程

TODO

ContentProvider的啟動(dòng)過(guò)程

第五章 理解上下文Context

Context的關(guān)聯(lián)類(lèi)

Context使用場(chǎng)景:
1.使用Context調(diào)用方法驻啤,比如啟動(dòng)Activity菲驴、訪(fǎng)問(wèn)資源、調(diào)用系統(tǒng)級(jí)服務(wù)
2.調(diào)用方法時(shí)傳入Context骑冗,比如彈出Toast赊瞬、創(chuàng)建Dialog
Activity/Service/Application都間接的繼承自Context,所以我們熟知的計(jì)算一個(gè)項(xiàng)目中多少個(gè)Context = Activity個(gè)數(shù)+Service個(gè)數(shù)+1(Application)
Context是一個(gè)抽象類(lèi)贼涩,其實(shí)現(xiàn)是ContextImpl巧涧。與Context相關(guān)聯(lián)的類(lèi),除了ContextImpl,還有ContextWrapper/ContextThemeWrapper/Activity等遥倦,其繼承關(guān)系如下:


ContextImpl使用了裝飾模式谤绳,ContextWrapper是其裝飾類(lèi),其作用是方法傳遞谊迄,其中幾乎所有的方法都是ContextImpl里定義好的闷供。 ContextThemeWrapper、Service和Application都繼承自ContextWrapper统诺,因?yàn)锳ctivity需要主題,所以繼承了ContextThemeWrapper疑俭。
Context的關(guān)聯(lián)類(lèi)采用了裝飾模式粮呢,主要有一下幾個(gè)優(yōu)點(diǎn):
1.使用者,比如Service能夠能方便的使用Context
2.如果ContextImpl發(fā)生了變化钞艇,它的裝飾類(lèi)ContextWrapper不需要做任何修改啄寡。
3.通過(guò)組合而非繼承的方式,擴(kuò)展了ContextImpl的功能哩照。在運(yùn)行時(shí)選擇不同的裝飾類(lèi)挺物、實(shí)現(xiàn)不同的功能,解耦飘弧。

Application Context的創(chuàng)建過(guò)程[也就是Application創(chuàng)建過(guò)程]

要看Context的創(chuàng)建過(guò)程识藤,那么首選肯定是Application Context。
Activity在啟動(dòng)首個(gè)Activity的時(shí)候次伶,會(huì)調(diào)用ActivityThread內(nèi)部類(lèi)ApplicationThread里的scheduleLaunchActivity方法痴昧。該方法內(nèi)部會(huì)向H類(lèi)發(fā)送LAUNCH_ACTIVITY類(lèi)型的消息,目的就是為了將啟動(dòng)Activity的邏輯放在主線(xiàn)程中的消息隊(duì)列中冠王。接下來(lái)得處理邏輯也肯定就是H類(lèi)中handleMessage里的LAUNCH_ACTIVITY分支了赶撰。

image.png

接著調(diào)用handleLaunchActivity方法,其內(nèi)部調(diào)用的performLaunchActivity的方法中有一行代碼:
image.png

調(diào)用LoadedApk中的makeApplication方法來(lái)創(chuàng)建Application
image.png

接著調(diào)用ContextImpl的createAppContext方法來(lái)創(chuàng)建ContextImpl,接著在ActivityThread里Instrumentation的newApplication方法中傳入ClassLoader和我們剛創(chuàng)建好的ContextImpl,創(chuàng)建Application對(duì)象豪娜,接著app.setOuterContext中傳入我們創(chuàng)建好的Application餐胀。這樣ContextImpl中也會(huì)包含Application的引用。
newAppliaction中通過(guò)反射來(lái)創(chuàng)建Application,并調(diào)用了Application的attach方法瘤载,將ContextImpl傳進(jìn)去否灾,最后返回新創(chuàng)建的Application嗽仪。attach方法的作用就是使Application可以使用Context的方法婉称,這樣Application才可以用來(lái)代表Application Context。

Application Context的創(chuàng)建流程如上所述邪蛔。

Application Context的獲取過(guò)程

我們通過(guò)getApplicationContext方法來(lái)獲得Application Context:

image.png

這里的mBase其實(shí)就是ContextImpl,但是此文件屬于保護(hù)文件溃蔫,在as中看不到:
image.png

只能下載下來(lái)源碼健提,或者在線(xiàn)上看了
image.png

所以getApplicationContext實(shí)際就是調(diào)用ContextImpl里的getApplicationContext,此方法中如果LoadedApk類(lèi)型的mPackageInfo不為空伟叛,則調(diào)用LoadedApk的getAppliaction私痹,否則調(diào)用ActivityThread里的同名方法。因?yàn)閼?yīng)用程序已經(jīng)啟動(dòng)统刮,LoadedApk不為空紊遵,所以最后取的還是LoadedApk里的getApplication。就是之前LoadedApk的makeAppliaction創(chuàng)建出來(lái)的Application侥蒙。

Activity的Context創(chuàng)建過(guò)程

Activity的Context的創(chuàng)建過(guò)程就是在啟動(dòng)過(guò)程中的一部分:

image.png

Activity啟動(dòng)過(guò)程中到了應(yīng)用程序主進(jìn)程時(shí)暗膜,會(huì)調(diào)用ActivityThread的內(nèi)部類(lèi)ApplicationThread的scheduleLaunchActivity,發(fā)LAUNCH_ACTIVITY消息給H類(lèi)鞭衩,再其handleMessage方法中調(diào)用ActivityThread的handleLaunchActivity,接著就是之前說(shuō)的performLaunchActivity了学搜,這里面進(jìn)行了很多操作:
image.png

調(diào)用Instrumentation的newActivity方法來(lái)創(chuàng)建Activity。
通過(guò)createBaseContextForActivity方法來(lái)創(chuàng)建Activity的ContextImpl.其內(nèi)部會(huì)調(diào)用ContextImpl的createActivityContext论衍。

image.png

接著調(diào)用ContextImpl的setOuterContext方法將剛才創(chuàng)建的Activity實(shí)例賦值到ContextImpl的成員變量中去瑞佩,這樣ContextImpl也能訪(fǎng)問(wèn)Activity的變量了。
image.png

接著調(diào)用Instrumentation的callActivityOnCreate方法來(lái)調(diào)用Activity的onCreate方法坯台。
回到performLaunchActivity方法中炬丸,看一下Activity的attach方法執(zhí)行了哪些重要邏輯:
image.png

image.png

在attach方法中創(chuàng)建了PhoneWindow,并為其設(shè)置WindowManager蜒蕾。這樣Activity就可以通過(guò)getWindowManager來(lái)獲取WindowManager稠炬。
總而言之:
在啟動(dòng)Activity的過(guò)程中創(chuàng)建ContextImpl,并賦值給ContextWrapper的成員變量mBase滥搭。Activity繼承自ContextWrapper的子類(lèi)ContextThemeWrapper酸纲,這樣Activity中就可以使用Context中定義的方法了。

Service的Context創(chuàng)建過(guò)程

Service的Context創(chuàng)建過(guò)程與Activity的Context創(chuàng)建過(guò)程類(lèi)似瑟匆,是在Service的啟動(dòng)過(guò)程中被創(chuàng)建的闽坡。之前在分析Service的創(chuàng)建過(guò)程中栽惶,ActivityThread的內(nèi)部類(lèi)ApplicationThread會(huì)調(diào)用scheduleCreateService方法來(lái)啟動(dòng)Service,此方法內(nèi)部會(huì)向H類(lèi)發(fā)送CREATE_SERVICE類(lèi)型的消息疾嗅,在其handleMessage中對(duì)此類(lèi)型的消息進(jìn)行處理外厂,調(diào)用ActivityThread的handleCreateService方法:


image.png

其內(nèi)部會(huì)調(diào)用ContextImpl的createAppCnotext方法創(chuàng)建ContextImpl,并將其傳入service的attach方法中:


image.png

將ContextImpl賦值給ContextWrapper的Context類(lèi)型的mBase代承,這樣在ContextWrapper中就可以使用Context的方法汁蝶,而Service繼承自ContextWrapper,同樣可以使用Context方法论悴。Service和ContextImpl相互持有掖棉,可以互相調(diào)用了。

第六章 理解ActivityManagerService

AMS處理的邏輯多復(fù)雜膀估,并不是孤軍奮戰(zhàn)幔亥,而是有一些類(lèi)和它并肩作戰(zhàn),這些類(lèi)會(huì)幫助AMS完成相關(guān)邏輯察纯,AMS和這些共同奮戰(zhàn)的類(lèi)就稱(chēng)為AMS家族帕棉。7.0和8.0相關(guān)處理有很大區(qū)別,我們主要看8.0饼记。

8.0 AMS家族

我們以Activity啟動(dòng)過(guò)程為例香伴,在Activity的啟動(dòng)過(guò)程中會(huì)調(diào)用到Instrumentation的execStartActivity方法:

image.png

其中會(huì)調(diào)用到ActivityManager的getService方法:
image.png

getService方法調(diào)用了IActivityManagerSingleton的get方法,IActivityManagerSingleton是一個(gè)Singleton類(lèi)具则。在Singleton的create方法中即纲,采用AIDL.獲取到IBinder類(lèi)型的AMS引用。然后轉(zhuǎn)換成IActivityManager類(lèi)型的對(duì)象博肋。IActivityManager.java類(lèi)是有AIDL工具在編譯時(shí)自動(dòng)生成的崇裁,AMS只需要繼承IActivityManger.Stub類(lèi)并實(shí)現(xiàn)對(duì)應(yīng)的方法就可以了。IActivityManager就是AMS的本地代理束昵。
image.png

一句話(huà)總結(jié):
ActivityManager的getService方法會(huì)得到IActivityManager,AMS只需要繼承IActivityManger.Stub類(lèi)葛峻,就可以和ActivityManger實(shí)現(xiàn)進(jìn)程間通信了锹雏。

AMS的啟動(dòng)過(guò)程

AMS的啟動(dòng)是在SystemServer進(jìn)程中啟動(dòng)的,我們直接從SystemServer的main方法開(kāi)始講起术奖。

image.png

image.png

之前講過(guò)SystemServer的run方法礁遵,這里我們?cè)倏匆幌隆?br> 首先通過(guò)System.loadLibrary("android_servers")加載動(dòng)態(tài)庫(kù)libandroid_servers.so.接下來(lái)創(chuàng)建SystemServiceManager,此類(lèi)會(huì)對(duì)系統(tǒng)的服務(wù)進(jìn)行創(chuàng)建、啟動(dòng)和生命周期管理采记。startBootstrapServices方法會(huì)用我們上面創(chuàng)建的SystemServiceManager啟動(dòng)ActivityManagerService/PowerManagerService/PackageManagerService等服務(wù)佣耐。 startCoreServices方法則啟動(dòng)了BatteryService/DropBoxManagerService/UsageStatsService和WebViewUpdateService.
startOtherService則啟動(dòng)了CameraService、AlarmMangerService等服務(wù)唧龄。
官方把系統(tǒng)服務(wù)分為了三種類(lèi)型兼砖,分別是引導(dǎo)服務(wù)、核心服務(wù)和其他服務(wù)。
我們主要看引導(dǎo)服務(wù)AMS是如何啟動(dòng)的讽挟,也就是startBootstrapServices方法:
image.png

image.png

其內(nèi)部調(diào)用了SystemServiceManger的startService方法懒叛,傳了一個(gè)SystemService類(lèi)型的參數(shù)ActivityManagerService.Lifecycle.class,并調(diào)用此參數(shù)的onStart方法來(lái)啟動(dòng)service對(duì)象耽梅。這個(gè)service對(duì)象也就是Lifecyle里的具體實(shí)現(xiàn)薛窥,可以看一下:

image.png

在Lifecycle的構(gòu)造方法中創(chuàng)建了AMS實(shí)例。當(dāng)調(diào)用SystemService類(lèi)型的service的onStart方法時(shí)眼姐,實(shí)際上就是調(diào)用了AMS的start方法诅迷。此類(lèi)中g(shù)etService方法返回的就是AMS實(shí)例。所以我們最初的SystemServer的startBootstrapServices里的
mSystemServiceManager.startSerivce(ActivityManagerService.Lifecycle.class).getService()實(shí)際上得到的就是AMS實(shí)例众旗。

AMS與應(yīng)用程序進(jìn)程

這里做一下簡(jiǎn)單總結(jié)罢杉,代碼就不跟了哈,之前也講過(guò)了逝钥。
① 啟動(dòng)應(yīng)用程序進(jìn)程時(shí)AMS會(huì)檢查這個(gè)應(yīng)用程序需要的應(yīng)用程序進(jìn)程是否存在屑那。
②如果需要的應(yīng)用程序進(jìn)程不存在,AMS就會(huì)請(qǐng)求Zygote進(jìn)程創(chuàng)建需要的應(yīng)用程序進(jìn)程艘款。

AMS重要的數(shù)據(jù)結(jié)構(gòu)

image.png

AMS涉及到了很多數(shù)據(jù)結(jié)構(gòu)持际,我們主要看一下ActivityRecord/TaskRecord/ActivityStack這些是Activty任務(wù)棧模型的基礎(chǔ)。
1.ActivityRecord
用來(lái)描述一個(gè)Activity哗咆,其內(nèi)部記錄了Activity的所有信息蜘欲,它是在Activity啟動(dòng)時(shí)創(chuàng)創(chuàng)建的。具體是在ActivityStarter的startActivity方法里晌柬。
其存儲(chǔ)的信息里包括:AMS的引用姥份、AndroidManifest節(jié)點(diǎn)信息、Activity狀態(tài)年碘、Acitivty資源信息和Activity進(jìn)程相關(guān)信息澈歉。以及該ActivityRecord所在的TaskRecord!

2.TaskRecord
TaskRecord用來(lái)描述一個(gè)Activity任務(wù)棧屿衅,其內(nèi)部存儲(chǔ)了任務(wù)棧的所有信息埃难。包括任務(wù)棧的唯一標(biāo)識(shí)符、任務(wù)棧中的Activity記錄和AMS的引用等涤久。需要注意的是其中含有ActivityStack涡尘。也就是當(dāng)前Activity任務(wù)棧所屬的ActivityStack.
3.ActivityStack
此類(lèi)是一個(gè)管理類(lèi),用來(lái)管理系統(tǒng)的所有Activity响迂。其內(nèi)部維護(hù)了Activity的所有狀態(tài)考抄、特殊狀態(tài)的Activity以及和Activity相關(guān)的列表等數(shù)據(jù)。ActivityStack是由ActivityStackSupervisor來(lái)進(jìn)行管理的蔗彤,而ActivityStackSupervisor在AMS的構(gòu)造方法中被創(chuàng)建川梅。

image.png

image.png

ActivityStackSupervisor中有多種ActivityStack實(shí)例疯兼!ActivityStack并不是只有一個(gè)!
image.png

其中mHomeStack用來(lái)存儲(chǔ)Launcher App的所有Activity挑势。mFocusedStack表示當(dāng)前正在接收輸入或啟動(dòng)下一個(gè)Activity的所有Activity镇防。mLastFocusedStack表示此前接收輸入的所有Activity。

①在ActivityStack中通過(guò)枚舉存儲(chǔ)了Activity的所有的狀態(tài)潮饱。也就是ActivittState来氧。
②ActivityStack中定義了一些特殊狀態(tài)的activity

image.png

比如正在暫停的activity、上一個(gè)已經(jīng)暫停的activity香拉,最近一次沒(méi)有歷史記錄的activity啦扬。
這些特殊狀態(tài)都是ActivityRecord類(lèi)型的。
③在ActivityStack中維護(hù)了很多ArrayList凫碌,這些ArrayList中的元素類(lèi)型主要有ActivityRecord和TaskRecord.
ActivityStack維護(hù)了元素類(lèi)型為T(mén)askRecord的列表扑毡,這樣ActivityStack和TaskRecord就有了關(guān)聯(lián)。TaskRecord中又有ActivityStack盛险。

Activity棧管理

1.Activity任務(wù)棧模型
Activity任務(wù)棧并不是憑空想象的瞄摊,它是由多種數(shù)據(jù)結(jié)構(gòu)共同組成的。之前說(shuō)的ActivityRecord苦掘、TaskRecord和ActivityStack换帜,它們就是Activity任務(wù)棧模型的重要組成部分。

image.png

ActivityRecord用來(lái)記錄一個(gè)Activity的所有信息鹤啡,TaskRecord中包含了一個(gè)或多個(gè)ActivityRecord惯驼。TaskRecord用來(lái)表示Activity的任務(wù)棧,用來(lái)管理?xiàng)V械腁ctivityRecord递瑰,ActivityStack又包含了一個(gè)或多個(gè)TaskRecord祟牲。它是TaskRecord的管理者。Activity棧管理就是建立在Activity任務(wù)棧模型之上的抖部。有了棧管理说贝,我們可以對(duì)應(yīng)用程序進(jìn)行操作,應(yīng)用可以復(fù)用自身應(yīng)用中以及其他應(yīng)用的Activity慎颗,節(jié)省了資源狂丝。
2.Launch Mode (啟動(dòng)模式)
四種啟動(dòng)模式:
①standard:默認(rèn)模式,每次啟動(dòng)Activity都會(huì)創(chuàng)建一個(gè)新的Activity實(shí)例哗总。
②singleTop:棧頂復(fù)用.如果要啟動(dòng)的Activity在棧頂,直接復(fù)用倍试,并調(diào)用onNewIntent方法讯屈。如果要啟動(dòng)的Activity不在棧頂,則會(huì)重新創(chuàng)建Activity的實(shí)例县习。
③singleTask:棧內(nèi)復(fù)用涮母。如果Activity已經(jīng)存在于它想要?dú)w屬的棧內(nèi)谆趾,不會(huì)重新創(chuàng)建實(shí)例,而是將棧內(nèi)該Activity上的所有Activity出棧叛本,同時(shí)調(diào)用該Activity的onNewIntent方法沪蓬。如果要啟動(dòng)的Activity不存在于它想在的棧內(nèi)中,并且該棧存在来候,則會(huì)重新創(chuàng)建該Activity的實(shí)例跷叉。如果要啟動(dòng)的Activity想要?dú)w屬的棧不存在,則先創(chuàng)建新棧营搅,然后創(chuàng)建該Activity實(shí)例并壓入到新棧中云挟。
④singleInstance: 啟動(dòng)Activity時(shí),首先創(chuàng)建一個(gè)新棧转质,然后創(chuàng)建該Activity實(shí)例并壓入新棧中园欣。
3.Intent的FLAG
在Intent中定義了很多FLAG,其中有幾個(gè)Flag也可以設(shè)定Activity的啟動(dòng)方式.如果Launch Mode和FLAG設(shè)定的Activity的啟動(dòng)方式有沖突,則以FLAG設(shè)定的為準(zhǔn)休蟹。
FLAG_ACTIVITY_SINGLE_TOP:和Launch Mode中的singleTop效果一樣
FLAG_ACTIVITY_NEW_TASK:和Launch Mode中的singleTask效果一行
FLAG_ACTIVITY_CLEAR_TOP:Launch Mode沒(méi)有與此對(duì)應(yīng)的模式沸枯,如果要啟動(dòng)的Activity已經(jīng)存在于棧中,則將所有位于它上面的Activity出棧赂弓。singleTask默認(rèn)有此標(biāo)記位的效果绑榴。
除了這三個(gè),還有很多:
FLAG_ACTIVITY_NO_HISTORY
FLAG_ACTIVITY_MULTIPLE_TASK
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
FLAG_ACTIVITY_BROUGHT_TO_FRONT
FLAG_ACTIVITY_CLEAR_TASK:這個(gè)需要和FLAG_ACTIVITY_NEWt_TASK一同使用才有效果拣展,用于清除與啟動(dòng)的Activity相關(guān)棧的所有其他 Activity彭沼。

taskAffinity ?备埃?姓惑??

我們可以在AndroidManifest.xml中設(shè)置android:taskAffinity,用來(lái)指定Activity希望歸屬的棧按脚,在默認(rèn)情況下于毙,同一個(gè)應(yīng)用程序的所有的Activity都有著相同的taskAffinity。taskAffinity在下面兩種情況中會(huì)產(chǎn)生效果:
1.taskAffinity與FLAG_ACTIVITY_NEW_TASK或者singleTask配合辅搬。如果新啟動(dòng)的Activity的taskAffinity和棧的taskAffinity相同則加入到該棧中唯沮;如果不同,就會(huì)創(chuàng)建新棧堪遂。
2.taskAffinity與allowTaskReparenting配合介蛉。如果allowTaskReparenting為true,說(shuō)明Activity具有轉(zhuǎn)移的能力溶褪。

第七章 理解WindowManager

為了更好地理解WMS币旧,我們需要先了解WindowManager的相關(guān)知識(shí)。

Window猿妈、WindowManager和WMS

Window是一個(gè)抽象類(lèi)吹菱,具體實(shí)現(xiàn)是PhoneWindow,對(duì)View進(jìn)行管理巍虫。
WindowManager是一個(gè)接口類(lèi),繼承自接口ViewManager,用來(lái)管理Window,實(shí)現(xiàn)類(lèi)是WindowManagerImpl.


image.png

image.png

如果需要對(duì)Window(View)進(jìn)行添加鳍刷、更新和刪除操作就可以使用WindowManager占遥。WindowManager會(huì)將具體的工作交給WMS來(lái)處理。WindowManager和WMS通過(guò)Binder來(lái)進(jìn)行跨進(jìn)程通信输瓜!WindowManager所提供的功能最終都會(huì)由WMS進(jìn)行處理瓦胎!
Window的實(shí)體其實(shí)也是View。

WindowManager的管理類(lèi)

上面說(shuō)過(guò)前痘,WindowManager是一個(gè)接口類(lèi)凛捏,繼承自接口ViewManager。ViewManager中定義了三個(gè)方法芹缔,分別用來(lái)添加坯癣、更新和刪除View.:

image.png

Window是一個(gè)抽象類(lèi),其具體實(shí)現(xiàn)是PhoneWindow最欠。 在Activity啟動(dòng)過(guò)程中會(huì)調(diào)用ActivityThread的performLaunchActivity方法苛萎,performLaunchActivity方法中又會(huì)調(diào)用Activity的attach方法本橙,PhoneWindow就是在Activity的PhoneWindow中創(chuàng)建的:
image.png

image.png

image.png

image.png

如上圖所示,在Activity的attach方法中創(chuàng)建了PhoneWindow,并調(diào)用setWindowManager方法浅妆,此方法實(shí)現(xiàn)在父類(lèi)Window中:
image.png

如果傳進(jìn)來(lái)的WindowManager為空丑婿,會(huì)調(diào)用Context.getSystemService方法朽们,并傳入服務(wù)的名稱(chēng)Context.WINDOW_SERVICE.具體實(shí)現(xiàn)也就在ContextImpl中了:
image.png

內(nèi)部會(huì)調(diào)用SystemServiceRegistry的getSystemServiceName方法:
image.png

從一個(gè)名為SYSTEM_SERVICE_FETCHERS的HashMap中獲取對(duì)應(yīng)名字的Service的烁。
在SystemServiceRegistry的靜態(tài)代碼塊中,會(huì)多次調(diào)用registerService將系統(tǒng)服務(wù)的名字和對(duì)應(yīng)的實(shí)現(xiàn)類(lèi)存到此HashMap中迟赃。
image.png

image.png

這里可以看到創(chuàng)建一個(gè)WindowManagerImpl陪拘。
接著回到setWindowManager中,獲取到WindowManager實(shí)現(xiàn)類(lèi)后纤壁,調(diào)用其createLocalWindowManager方法左刽,將當(dāng)前window作為參數(shù)傳入。這樣WindowManagerImpl中也就持有了Window.可以對(duì)Window進(jìn)行各種操作酌媒。
比如調(diào)用addView方法:
image.png

image.png

調(diào)用的就是WindowManagerImpl的addView方法欠痴,而WindowManagerImpl作為WindowManager的實(shí)現(xiàn)類(lèi),其實(shí)沒(méi)有實(shí)現(xiàn)什么功能秒咨,而是將功能委托給了WindowManagerGlobal喇辽,這里用到的是橋接模式。
這里WindowManagerGlobal是一個(gè)單例雨席,一個(gè)進(jìn)程中只有一個(gè)實(shí)例茵臭。

通過(guò)上面分析,簡(jiǎn)單的整理一下:


image.png

PhoneWindow繼承自Window,Window通過(guò)setWindowManager方法與WindowManager發(fā)生關(guān)聯(lián)。WindowManager繼承自接口ViewManager,WindowManagerImpl是WindowManager接口的實(shí)現(xiàn)類(lèi)旦委,但是具體的功能都會(huì)委托給WindowManagerGlobal,這里用到的是橋接模式雏亚。

Window的屬性

WMS是Window的最終管理者缨硝,Window好比是員工,WMS是老板罢低。為了方便管理員工查辩,老板制定的規(guī)章制度就是Window的屬性,它們被定義在WindowManager的內(nèi)部類(lèi)LayoutParams中网持。其中與實(shí)際開(kāi)發(fā)最密切的有3種:
Type(Window的類(lèi)型)
Flag(Window的標(biāo)志)
SoftInputMode(軟鍵盤(pán)相關(guān)模式)

Window分為3大類(lèi)型:
Application Window(應(yīng)用程序窗口)
Sub Window(子窗口)
System Window(系統(tǒng)窗口)

Window類(lèi)型及顯示次序

1.應(yīng)用程序窗口:
應(yīng)用程序窗口的Type值范圍為1~99.
2.子窗口
顧名思義宜岛,不能獨(dú)立存在,必須依附在其他窗口上才行功舀,PopupWindow就屬于子窗口萍倡。Type值范圍在1000~1999之間。
3.系統(tǒng)窗口
Toast/輸入法窗口/系統(tǒng)音量窗口/系統(tǒng)錯(cuò)誤窗口辟汰,都屬于系統(tǒng)窗口列敲。Type值范圍為2000~2999之間。
4.窗口顯示次序帖汞。
一般情況下戴而,Type值越大則Z-Oder排序越靠前,就越靠近用戶(hù)翩蘸。實(shí)際情況比這要復(fù)雜所意,比如多個(gè)窗口的Type相同時(shí),WMS會(huì)結(jié)合各種情況給出最終的排序催首。

Window的標(biāo)志

Window的標(biāo)志就是Flag,用于控制Window的顯示扶踊,定義在WindowManager的內(nèi)部類(lèi)LayoutParams,一共有20多個(gè)。這里說(shuō)幾個(gè)常用的翅帜。

FLAG_KEEP_SCREEN_ON:只要窗口可見(jiàn)姻檀,屏幕就會(huì)一直亮著。
FLAG_FULLSCREEN: 隱藏所有的屏幕裝飾窗口涝滴,比如在游戲绣版,播放器中的全屏顯示。
FLAG_SHOW_WHEN_LOCKED:窗口可以在鎖屏的窗口之上顯示歼疮。
FLAG_IGNORE_CHEEK_PRESSES:當(dāng)用戶(hù)的臉貼近屏幕時(shí)(比如打電話(huà))杂抽,不會(huì)去響應(yīng)此事件。

getWindow()是Activity里的方法韩脏。Window的Flag有3種方法:
①Window的addFlags方法:

Window mWindow = getWindow();
mWindow.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

②Window的setFlags方法(addFlags方法內(nèi)部也會(huì)調(diào)用setFlags方法)
③給LayoutParams設(shè)置Flag缩麸,并通過(guò)WindowManager的addView方法進(jìn)行添加。

WindowManger.LayoutParams params = new WindowManager.LayoutParams();
params.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN;
WindowManager mWindowManager = (WindowManager) getSystemServices(Context.WINDOW_SERVICE);
TextView mTextView = new TextView(this);
mWindowManager.addView(mTextView,params);
軟鍵盤(pán)相關(guān)模式

有六種赡矢,這里只提兩種最常用的:
①SOFT_INPUT_ADJUST_RESIZE:當(dāng)軟鍵盤(pán)彈出時(shí)杭朱,窗口會(huì)調(diào)整大小
②SOFT_INPUT_ADJUST_PAN:當(dāng)軟鍵盤(pán)彈出時(shí)阅仔,窗口不需要調(diào)整大小,要確保輸入焦點(diǎn)是可見(jiàn)的弧械。

軟鍵盤(pán)的模式可以在清單文件中標(biāo)明八酒,也可以在代碼中手動(dòng)添加.
1.在清單文件中添加:
android:windowSoftInputMode
2.在代碼中添加
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)

Window的操作

Window的添加、更新和刪除的操作刃唐,統(tǒng)稱(chēng)為Window的操作羞迷。對(duì)Window的操作分為兩部分:
1.WindowManager處理部分
2.WMS處理部分
對(duì)于Window的操作,最終都會(huì)交由WMS來(lái)進(jìn)行處理画饥。

本節(jié)就是簡(jiǎn)單說(shuō)一下WindowManager的處理部分衔瓮,我們主要看窗口的添加過(guò)程:
可以以狀態(tài)欄StatusBar為例。
在StatusBarWindowManager的add方法里:

image.png

調(diào)用了WindowManager的addView方法抖甘,最終會(huì)調(diào)用到WindowManagerGlobal的addView方法:
image.png

在此方法里會(huì)創(chuàng)建ViewRootImpl,添加窗口這一操作是通過(guò)ViewRootImpl來(lái)進(jìn)行的热鞍。
ViewRootImpl身負(fù)了很多職責(zé):
①View樹(shù)的根并管理View樹(shù)
②觸發(fā)View的測(cè)量、布局和繪制
③輸入事件的中轉(zhuǎn)站
④管理Surface
⑤負(fù)責(zé)和WMS進(jìn)行進(jìn)程間通信

可以看到单山,每一項(xiàng)都十分重要0帧!C准椤V缃印!
接下來(lái)就可以查看ViewRootImpl的setView方法了:
image.png

image.png

可以看到這里面是個(gè)同步代碼塊悴晰,setView方法中會(huì)調(diào)用IWindowSession的addToDisplay方法慢睡,這個(gè)一個(gè)Binder對(duì)象,是Client端的代理铡溪,用于進(jìn)程間通信漂辐。Server端的實(shí)現(xiàn)為Session。之前的邏輯都是本地進(jìn)程的棕硫,而Session的addToDisplay方法則運(yùn)行在WMS所在的進(jìn)程(SystemServer進(jìn)程)中髓涯。

image.png

在addToDisplay方法中調(diào)用WMS的addWindow方法,WMS會(huì)為這個(gè)添加的窗口分配Surface,并確定窗口顯示次序哈扮。負(fù)責(zé)顯示界面的是畫(huà)布Surface,而不是窗口本身纬纪。WMS會(huì)將所管理的Surface交由SurfaceFlinger處理,SurfaceFlinger會(huì)將這些Surface混合并繪制到屏幕上滑肉。

Activity的添加過(guò)程

在Activity啟動(dòng)時(shí)包各,最后會(huì)調(diào)用到ActivityThread里的handleResumeActivity方法,此方法里:


image.png

會(huì)得到ViewManager類(lèi)型的wm對(duì)象靶庙,調(diào)用addView方法问畅,也就是WindowManager的addView方法,之前說(shuō)過(guò)WindowManager繼承ViewManager。其實(shí)現(xiàn)就是在WindowManagerImpl中實(shí)現(xiàn)的。最后還是會(huì)調(diào)用WindowManagerGlobal中去护姆。之后的流程就是我們前面分析的那些了矾端。

Window的更新過(guò)程

更新過(guò)程與添加過(guò)程類(lèi)似。需要調(diào)用ViewManager的updateViewLayout方法卵皂,實(shí)現(xiàn)在WindowManagerImpl里须床,具體處理還是WindowManagerGlobal.(這個(gè)橋接模式搞得Global同學(xué)好累)里的updateViewLayout:


image.png

根據(jù)索引拿到ViewRootImpl對(duì)象,調(diào)用其setLayoutParams方法更新參數(shù)渐裂。


image.png

在setLayoutParams方法的最后會(huì)調(diào)用scheduleTraversals方法,這個(gè)方法最終會(huì)調(diào)用到ViewRootImpl里的performTraversals()方法:
image.png

800多行的方法...emm...做個(gè)簡(jiǎn)單的總結(jié)吧
最終會(huì)調(diào)用WMS的relayoutWindow方法钠惩。同時(shí)調(diào)用了performMeasure柒凉、performLayout、performDraw方法篓跛,它們的內(nèi)部又會(huì)調(diào)用View的measure膝捞、layout和draw方法,這樣就完成了View的工作流程愧沟。在此方法中更新了Window視圖蔬咬,又執(zhí)行了Window中的View的工作流程,這樣就完成了Window的更新沐寺!刪除過(guò)程留到下一章再講林艘。

理解WindowManagerService

主要講解WMS的職責(zé)、WMS的創(chuàng)建過(guò)程混坞、WMS的重要成員以及Window的添加和刪除過(guò)程狐援。

WMS的職責(zé)

1.窗口管理
WMS是窗口的管理者,它負(fù)責(zé)窗口的啟動(dòng)究孕、添加和刪除啥酱,另外窗口的大小和層級(jí)也是由WMS進(jìn)行管理的。
2.窗口動(dòng)畫(huà)
窗口間進(jìn)行切換時(shí)厨诸,使用窗口動(dòng)畫(huà)可以顯得更炫酷一些镶殷。窗口動(dòng)畫(huà)由WMS的動(dòng)畫(huà)子系統(tǒng)來(lái)負(fù)責(zé),也就是WindowAnimator.
3.輸入系統(tǒng)的中轉(zhuǎn)站
通過(guò)對(duì)窗口的觸摸從而產(chǎn)生觸摸事件微酬,InputManagerService(IMS)會(huì)對(duì)觸摸事件進(jìn)行處理绘趋。
4.Surface管理
窗口并不具有繪制的功能,因此每個(gè)窗口都需要一塊Surface來(lái)供自己繪制得封,為每個(gè)窗口分配Surface是由WMS來(lái)完成的埋心。

本章主要講窗口管理。

WMS的創(chuàng)建過(guò)程

WMS是在SystemServer進(jìn)程中創(chuàng)建的忙上。

image.png

image.png

在①處加載了so動(dòng)態(tài)庫(kù)libandroid_servers.so拷呆。在③處創(chuàng)建SystemServiceManager,它會(huì)對(duì)系統(tǒng)的服務(wù)進(jìn)行創(chuàng)建、啟動(dòng)和生命周期管理茬斧。
image.png

最重要的是這三個(gè)方法:
startBootstrapServices:啟動(dòng)了ActivityManagerService腰懂、PowerManagerService、PackageManagerService等服務(wù)项秉。
startCoreServices:?jiǎn)?dòng)了BatteryService绣溜、WebViewUpdateService等。
startOtherService:?jiǎn)?dòng)了CameraService娄蔼、AlarmManagerService等怖喻。
這些服務(wù)的父類(lèi)都是SystemService
這三個(gè)方法對(duì)應(yīng)三種服務(wù):引導(dǎo)服務(wù)、核心服務(wù)和其他服務(wù)岁诉。
其他服務(wù)是一些非緊要和不需要立即啟動(dòng)的服務(wù)锚沸。WMS就是其他服務(wù)中的一種(沒(méi)想到連個(gè)coreService都沒(méi)混到)
image.png

在startOtherServices中創(chuàng)建的其他服務(wù)大概有100多個(gè),上圖只借去了WMS相關(guān)的代碼涕癣。
通過(guò)調(diào)用WMS的main方法哗蜈,創(chuàng)建WMS,其中的一個(gè)參數(shù)是我們創(chuàng)建的InputManagerService坠韩,也就是IMS距潘。WMS的main方法是運(yùn)行在SystemServer的run方法中的,換句話(huà)說(shuō)就是運(yùn)行在"system_server"線(xiàn)程中的只搁。接著調(diào)用SystemService的addService方法將WMS和IMS注冊(cè)到ServiceManager中音比。這樣如果有客戶(hù)端想要使用WMS,就需要先去ServiceManager中查詢(xún)信息须蜗,然后根據(jù)信息與WMS所在的進(jìn)行建立通信通路硅确,客戶(hù)端就可以使用WMS了。接下來(lái)我們看一下WMS的main方法:
image.png

調(diào)用DisplayThread的getHandler方法獲取到該線(xiàn)程的handler對(duì)象明肮,毫無(wú)疑問(wèn)這個(gè)是一個(gè)HandlerThread的子類(lèi)菱农,是一個(gè)單例的前臺(tái)線(xiàn)程。在handler的runWithScissors方法中new了一個(gè)WindowManagerService柿估,說(shuō)明WMS的創(chuàng)建試運(yùn)行在android.display線(xiàn)程中的循未。
runWithScissors方法中,根據(jù)每個(gè)線(xiàn)程只有一個(gè)Loopper的原理來(lái)判斷當(dāng)前的線(xiàn)程也就是system_server線(xiàn)程是否是Handler所指向的線(xiàn)程秫舌,也就是android.display線(xiàn)程的妖。如果是,直接執(zhí)行run方法足陨;如果不是則調(diào)用BlockingRunnable的postAndWait方法嫂粟。很明顯,這里走的是postAndWait方法墨缘。
image.png

我們看一下postAndWait里具體的實(shí)現(xiàn):
image.png

image.png

調(diào)用handler的post方法星虹,將當(dāng)前的BlockingRunnable添加到Handler的任務(wù)隊(duì)列中零抬。接著通過(guò)判斷標(biāo)志位mDone,為false的時(shí)候宽涌,會(huì)一直調(diào)用wait方法使得當(dāng)前線(xiàn)程也就是system_server線(xiàn)程進(jìn)入等待狀態(tài)平夜。而mDone就是在BlockingRunnable的的run方法執(zhí)行完畢后,在finally中將mDone置為true卸亮,同時(shí)調(diào)用notifyAll方法喚醒處于等待狀態(tài)的線(xiàn)程忽妒。這樣下面的wait方法就會(huì)停止調(diào)用。簡(jiǎn)而言之兼贸,system_server線(xiàn)程等待的就是android.diaplay線(xiàn)程段直,一直等到android.display線(xiàn)程執(zhí)行完畢后,再執(zhí)行system_serve線(xiàn)程溶诞。這是因?yàn)閍ndroid.display線(xiàn)程內(nèi)部執(zhí)行了WMS的創(chuàng)建坷牛,而WMS的創(chuàng)建優(yōu)先級(jí)更高。WMS的創(chuàng)建就到此為止很澄。接下來(lái)看一下WMS的構(gòu)造方法里做了哪些工作:
image.png

image.png

image.png

保存了傳遞進(jìn)來(lái)的IMS,這樣WMS就持有了IMS的引用颜及。同時(shí)通過(guò)ActivityManger.getService()方法獲得了AMS的引用甩苛。在8.0的時(shí)候還會(huì)在構(gòu)造方法中直接調(diào)用initPolicy方法初始化窗口管理策略的接口類(lèi):WindowManagerPolicy(WMP),定義一個(gè)窗口策略所要遵守的通用規(guī)范俏站。但是在9.0中的構(gòu)造方法中并沒(méi)有調(diào)用此方法讯蒲,而是在所有與WMS相關(guān)聯(lián)的實(shí)體類(lèi)都創(chuàng)建好之后(例如AMS)才會(huì)調(diào)用:
image.png

通過(guò)通過(guò)addMonitor方法將WMS添加到WatchDog中,WatchDog每分鐘都會(huì)對(duì)被監(jiān)控的系統(tǒng)服務(wù)進(jìn)行檢查肄扎,如果被監(jiān)控的系統(tǒng)服務(wù)出現(xiàn)了死鎖墨林,則會(huì)殺死WatchDog所在進(jìn)程,也就是SystemServer進(jìn)程犯祠。
看一下initPolicy方法:
image.png

與WMS的main方法類(lèi)似旭等,執(zhí)行WMP的init方法,此方法運(yùn)行在android.ui線(xiàn)程中衡载,android.ui線(xiàn)程優(yōu)先級(jí)又比android.display優(yōu)先級(jí)高搔耕,所以android.display需要等android.ui線(xiàn)程執(zhí)行完畢后才能繼續(xù)執(zhí)行。WMS的啟動(dòng)過(guò)程中涉及到了三個(gè)線(xiàn)程:
system_server痰娱、android.display和android.ui弃榨。這三個(gè)線(xiàn)程的聯(lián)系如下:
QQ圖片20210330233232.jpg

1.首先在system_server線(xiàn)程中執(zhí)行SystemServer的startOtherService方法,在startOtherService方法中調(diào)用WMS的main方法梨睁,會(huì)創(chuàng)建WMS鲸睛,創(chuàng)建過(guò)程在android.display線(xiàn)程中,此線(xiàn)程優(yōu)先級(jí)比system_server優(yōu)先級(jí)高坡贺,所以system_server會(huì)等待android.display線(xiàn)程執(zhí)行完畢再執(zhí)行官辈。
2.在WMS的構(gòu)造方法中調(diào)用WMS的initPolicy方法箱舞,initPolicy方法會(huì)調(diào)用PMS(PhoneWindowManager)的init方法,此方法運(yùn)行在android.ui線(xiàn)程中钧萍,此線(xiàn)程優(yōu)先級(jí)比android.display優(yōu)先級(jí)高褐缠,所以android.display需要等待android.ui線(xiàn)程執(zhí)行完畢后再執(zhí)行。
3.PWM的init方法執(zhí)行結(jié)束后风瘦,繼續(xù)執(zhí)行android.display方法队魏,android.display線(xiàn)程執(zhí)行完畢就完成了WMS的創(chuàng)建。等待system_server線(xiàn)程被喚醒后繼續(xù)執(zhí)行WMS的main方法之后的邏輯万搔。

WMS的重要成員

image.png

1.mPolicy:WindowManagerPolicy
前面也說(shuō)了胡桨,WindowManagerPolicy是窗口管理策略的接口類(lèi),具體實(shí)現(xiàn)是PhoneWindowManager.
2.mSessions:ArraySet
mSessions是ArraySet類(lèi)型的變量瞬雹,元素類(lèi)型為Session蚊惯,它主要用于進(jìn)程間通信,其他的應(yīng)用程序進(jìn)程想要和WMS的進(jìn)程進(jìn)行通信就必須通過(guò)Session挚冤。并且每個(gè)應(yīng)用程序都會(huì)對(duì)應(yīng)一個(gè)Session利诺。
3.mWindowMap:WindowHashMap
key類(lèi)型是IBinder,value值類(lèi)型是WindowState胖缤。WindowState用來(lái)描述一個(gè)窗口尚镰。此hashMap就是用來(lái)保存WMS中各種窗口的集合。
4.mFinishedStartinga:ArrayList
5.mResizingWindows:ArrayList
6.mAnimator:WindowAnimator
用來(lái)管理窗口的動(dòng)畫(huà)以及特效動(dòng)畫(huà)哪廓。
7.mH:H
mH是H類(lèi)型的變量狗唉,系統(tǒng)的Handler類(lèi),用于將任務(wù)加入到主線(xiàn)程的消息隊(duì)列中涡真。這樣代碼邏輯就會(huì)在主線(xiàn)程中運(yùn)行分俯。
8.mInputManager:InputManagerService
mInputManager是InputManagerService類(lèi)型的變量,輸入系統(tǒng)的管理者哆料。IMS會(huì)對(duì)觸摸事件進(jìn)行管理缸剪,它會(huì)尋找一個(gè)最合適的窗口來(lái)處理觸摸反饋信息。

Window的添加過(guò)程(WMS處理部分)

Window的操作分為兩部分东亦,一部分是WindowManager處理部分橄登,另一部分是WMS處理部分;WindowManager處理部分之前說(shuō)過(guò)了讥此,我們主要看WMS處理的部分拢锹,在其addWindow方法中:


image.png

這個(gè)方法里邏輯比較重,但重要程度沒(méi)有那么高萄喳,暫不貼源碼卒稳,先做一些摘要:
addWindow方法會(huì)返回addWindow操作的各種狀態(tài)。主要做了以下4件事情:
①對(duì)所要添加的窗口進(jìn)行檢查他巨,在WindowManagerPolicy的checkAddPermission方法中進(jìn)行判斷充坑,如果窗口不滿(mǎn)足一些條件减江,就不會(huì)執(zhí)行下面的代碼。
②WindowToken相關(guān)處理捻爷,根據(jù)父窗口的rootType來(lái)判斷當(dāng)前窗口是否需要提供WindowToken辈灼,沒(méi)有提供的就不會(huì)執(zhí)行下面的邏輯;而有的窗口類(lèi)型需要WMS隱式創(chuàng)建WindowToken
③WindowState的創(chuàng)建和相關(guān)處理也榄,將WindowToken和WindowState相關(guān)聯(lián)巡莹。WindowState存有窗口的所有的狀態(tài)信息。
④創(chuàng)建和配置DisplayContent甜紫,完成窗口添加到系統(tǒng)前的準(zhǔn)備工作降宅。DisplayContent用來(lái)描述一塊屏幕,里面存著屏幕id囚霸、動(dòng)畫(huà)腰根、狀態(tài)等信息。


image.png

Window的刪除過(guò)程

和Window的創(chuàng)建與更新過(guò)程一樣拓型,要?jiǎng)h除Window需要先調(diào)用WindowManagerImpl的removeView方法额嘿,在removeView方法中又會(huì)調(diào)用WindowManagerGlobal的removeView方法。
還是做一些摘要:
①檢查線(xiàn)程的安全性劣挫,如果不正確就拋出異常岩睁。執(zhí)行ViewRootImpl的doDie方法來(lái)判斷刪除Window的線(xiàn)程是否是創(chuàng)建此Window的原始線(xiàn)程,因?yàn)橹挥袆?chuàng)建Window的原始線(xiàn)程才能夠操作Window揣云。
②從ViewRootImpl列表、布局參數(shù)列表和View列表中刪除和Window對(duì)應(yīng)的元素
③判斷是否可以執(zhí)行刪除操作冰啃,如果不能就推遲刪除操作邓夕,比如當(dāng)前Window在執(zhí)行動(dòng)畫(huà),就等動(dòng)畫(huà)執(zhí)行完成后再執(zhí)行刪除操作
④執(zhí)行刪除操作阎毅,清理和釋放與Window相關(guān)的一切資源焚刚。

JNI原理

暫且跳過(guò)

Java虛擬機(jī)

概述

我們常說(shuō)的JDK(Java Development Kit)包含了Java語(yǔ)言、Java虛擬機(jī)和Java API類(lèi)庫(kù)這三個(gè)部分扇调,是Java程序開(kāi)發(fā)的最小環(huán)境矿咕。而JRE(Java Runtime Environment)包含了Java API中的Java SE API子集和Java虛擬機(jī)這兩部分,是Java程序運(yùn)行的標(biāo)準(zhǔn)環(huán)境狼钮。
Java虛擬機(jī)是一個(gè)家族碳柱,實(shí)際上有很多類(lèi)型的Java虛擬機(jī),有幾個(gè)主流的:
1.HotSpot VM : Oracle JDK和 OpenJDK中自帶的虛擬機(jī)熬芜,是最主流的和使用范圍最廣的Java虛擬機(jī)莲镣。一般不做特殊介紹的話(huà),我們所說(shuō)的虛擬機(jī)就是這一款涎拉,當(dāng)下屬于Oracle公司瑞侮。
2.J9 VM:IBM開(kāi)發(fā)的虛擬機(jī)的圆,目前是其主力發(fā)展的Java虛擬機(jī)。
3.Zing VM :以O(shè)racle的HotSpot VM為基礎(chǔ)半火,改進(jìn)了許多影響延遲的細(xì)節(jié)越妈。
Android中的Dalvik和ART虛擬機(jī)并不屬于Java虛擬機(jī)

Java虛擬機(jī)執(zhí)行流程

0FCDFDF6DFA46E12762975EDDED7BBF5.jpg

如上圖,java虛擬機(jī)執(zhí)行流程分為兩大部分:
①編譯時(shí)環(huán)境
②運(yùn)行時(shí)環(huán)境
當(dāng)一個(gè)Java編譯器編譯后會(huì)生成Class文件钮糖。Java虛擬機(jī)與Java語(yǔ)言并沒(méi)有必然聯(lián)系梅掠,它只與特定的二進(jìn)制文件:Class文件相關(guān)。因此無(wú)論任何語(yǔ)言只要能夠編譯成Class文件藐鹤,就可以被Java虛擬機(jī)識(shí)別并執(zhí)行瓤檐。
比如Java、Kotlin娱节、Groovy等挠蛉。

Java虛擬機(jī)結(jié)構(gòu)

Java虛擬機(jī)結(jié)構(gòu)包括運(yùn)行時(shí)數(shù)據(jù)區(qū)域、執(zhí)行引擎肄满、本地庫(kù)接口和本地方法庫(kù)谴古。
Class文件格式
Java文件被編譯后生成了Class文件,這種二進(jìn)制格式文件不依賴(lài)于特定的硬件和操作系統(tǒng)稠歉。每一個(gè)Class文件中都對(duì)應(yīng)著唯一的類(lèi)或者接口的定義信息掰担,但是類(lèi)或者接口并一定定義在文件中,比如類(lèi)和接口可以通過(guò)類(lèi)加載器來(lái)直接生成怒炸。

類(lèi)的生命周期

一個(gè)Java文件被加載到Java虛擬機(jī)內(nèi)存中到從內(nèi)存中卸載的過(guò)程被稱(chēng)為類(lèi)的生命周期带饱。類(lèi)的生命周期包括的階段分別是:加載、鏈接阅羹、初始化勺疼、使用和卸載。其中鏈接分為三個(gè)部分:驗(yàn)證捏鱼、準(zhǔn)備和解析执庐。因此類(lèi)的生命周期分為7個(gè)階段。而從廣義來(lái)上來(lái)导梆,類(lèi)的加載包含了類(lèi)的生命周期的5個(gè)階段:加載轨淌、鏈接(驗(yàn)證、準(zhǔn)備看尼、初始化)递鹉、初始化。其五個(gè)階段的工作如下:
(1)加載:查找并加載Class文件
(2)鏈接:包括驗(yàn)證藏斩、準(zhǔn)備和解析
驗(yàn)證:確保被導(dǎo)入類(lèi)型的正確性
準(zhǔn)備:為類(lèi)的靜態(tài)字段分配字段梳虽,并使用默認(rèn)值初始化這些字段
解析:虛擬機(jī)將常量池內(nèi)的符號(hào)引用替換為直接引用。
(3)初始化:將類(lèi)變量初始化為正確初始值灾茁。

類(lèi)加載子系統(tǒng)

類(lèi)加載子系統(tǒng)通過(guò)多種類(lèi)加載器來(lái)查找和加載Class文件到Java虛擬機(jī)中窜觉,Java虛擬機(jī)有兩種類(lèi)加載器:系統(tǒng)加載器和自定義加載器谷炸。
系統(tǒng)加載器分為三種:
①Bootstrap ClassLoader(引導(dǎo)類(lèi)加載器)
用C/C++實(shí)現(xiàn)的加載器,用于加載執(zhí)行的JDK的核心類(lèi)庫(kù)禀挫。比如java.lang,java.uti等旬陡。Java虛擬機(jī)的啟動(dòng)就是通過(guò)引導(dǎo)類(lèi)加載器來(lái)創(chuàng)建一個(gè)初始類(lèi)完成的。因?yàn)榇思虞d器是使用與平臺(tái)無(wú)關(guān)的C/C++語(yǔ)言實(shí)現(xiàn)的语婴,所以java訪(fǎng)問(wèn)不到描孟。
②Extensions ClassLoader(擴(kuò)展類(lèi)加載器)
加載java的擴(kuò)展類(lèi),比如java.ext.dir下的砰左。
③Application ClassLoader(應(yīng)用程序類(lèi)加載器)
又稱(chēng)System ClassLoader匿醒,這個(gè)類(lèi)加載器可以通過(guò)ClassLoader的getSystemClassLoader方法獲取到。繼承自java.lang.ClassLoader缠导。

運(yùn)行時(shí)數(shù)據(jù)區(qū)域(【Java內(nèi)存】)

很多人將java內(nèi)存區(qū)域分為堆內(nèi)存(Heap)和棧內(nèi)存(Stack),這種方法不夠準(zhǔn)確廉羔。
根據(jù)《Java虛擬機(jī)規(guī)范(Java SE7版)》,將內(nèi)存區(qū)域分為以下幾個(gè)部分:
程序計(jì)數(shù)器僻造、Java虛擬機(jī)棧憋他、本地方法棧、Java堆和方法區(qū)髓削,下面一一介紹:
1.程序計(jì)數(shù)器
程序計(jì)數(shù)器也叫作PC寄存器竹挡,是一塊較小的內(nèi)存空間。Java虛擬機(jī)的多線(xiàn)程是通過(guò)輪流切換并分配處理器執(zhí)行時(shí)間的方式來(lái)實(shí)現(xiàn)的立膛,在一個(gè)確定的時(shí)刻只有一個(gè)處理器執(zhí)行一條線(xiàn)程中的指令揪罕。為了在線(xiàn)程切換后能恢復(fù)到正確的執(zhí)行位置。每個(gè)線(xiàn)程都會(huì)有一個(gè)獨(dú)立的程序計(jì)數(shù)器宝泵。程序計(jì)數(shù)器是線(xiàn)程私有的好啰。
也是java虛擬機(jī)規(guī)范中唯一沒(méi)有規(guī)定任何OutOfMemoryError情況的數(shù)據(jù)區(qū)域。
2.Java虛擬機(jī)棧
每一條Java虛擬機(jī)線(xiàn)程都有一個(gè)線(xiàn)程私有的Java虛擬機(jī)棧鲁猩。它的生命周期與線(xiàn)程相同,與線(xiàn)程是同時(shí)創(chuàng)建的罢坝。存儲(chǔ)的內(nèi)容是方法調(diào)用的狀態(tài)(就是方法的內(nèi)容)廓握,包括局部變量、參數(shù)嘁酿、返回值以及運(yùn)算的中間結(jié)果等隙券。
3.本地方法棧
與虛擬機(jī)棧類(lèi)似,只不過(guò)本地方法棧是用來(lái)支持Native方法的闹司。
4.Java堆
線(xiàn)程共享的運(yùn)行時(shí)內(nèi)存區(qū)域娱仔,占用區(qū)域最大。Java堆用來(lái)存放對(duì)象實(shí)例游桩,幾乎所有的對(duì)象實(shí)例都會(huì)在這里分配內(nèi)存牲迫。是GC機(jī)制管理的主要部分耐朴,也被稱(chēng)為GC堆。Java堆的容量可以是固定的盹憎,也可以動(dòng)態(tài)擴(kuò)展筛峭。
堆分為三個(gè)部分:
①年輕代:又被劃分為Eden區(qū)和Survivor(From Survivor、ToSurvivor),空間分配比例為8:1:1.
②老年代
5.方法區(qū)
方法區(qū)是線(xiàn)程共享的運(yùn)行時(shí)內(nèi)存區(qū)域陪每,用來(lái)存儲(chǔ)已經(jīng)被java虛擬機(jī)加載的類(lèi)的結(jié)構(gòu)信息影晓,包括運(yùn)行時(shí)常量池、字段和方法信息檩禾、靜態(tài)變量等信息挂签。注意【運(yùn)行時(shí)】

對(duì)象的創(chuàng)建

創(chuàng)建一個(gè)對(duì)象會(huì)進(jìn)行如下操作:
①判斷對(duì)象對(duì)應(yīng)的類(lèi)是否加載、鏈接和初始化
②為對(duì)象分配內(nèi)存
根據(jù)java堆是否完整盼产,分為兩種方式:指針碰撞(內(nèi)存是規(guī)整的)饵婆、空閑列表(內(nèi)存不規(guī)整)
③處理并發(fā)安全問(wèn)題
④初始化分配到的內(nèi)存空間
將分配到的內(nèi)存信息,除了對(duì)象頭之外都初始化為零
⑤設(shè)置對(duì)象的對(duì)象頭
⑥執(zhí)行init方法進(jìn)行初始化
執(zhí)行init方法辆飘,初始化對(duì)象的成員變量啦辐、調(diào)用類(lèi)的構(gòu)造方法,這樣一個(gè)對(duì)象就被創(chuàng)建出來(lái)了蜈项。

對(duì)象的堆內(nèi)存布局

emm...

oop-klass模型

用來(lái)描述java對(duì)象實(shí)例的一種模型芹关。剩下的就emm...

垃圾標(biāo)記算法

目前有兩種垃圾標(biāo)記算法:引用計(jì)數(shù)法和根搜索算法(可達(dá)路徑法)
java中有四種引用:強(qiáng)引用、軟引用紧卒、弱引用和虛引用侥衬。
1.強(qiáng)引用:新建一個(gè)對(duì)象時(shí)就創(chuàng)建了一個(gè)具有強(qiáng)引用的對(duì)象,也就是new出來(lái)的對(duì)象跑芳,這種引用的對(duì)象轴总,垃圾收集器絕不會(huì)回收它。
2.軟引用:當(dāng)內(nèi)存不夠時(shí)博个,會(huì)回收這些對(duì)象的內(nèi)存怀樟。對(duì)應(yīng)SoftReference。
3.弱引用:比軟引用具有更短的生命周期盆佣,垃圾回收器一旦發(fā)現(xiàn)了只具有弱引用的對(duì)象往堡,不管當(dāng)前內(nèi)存是否足夠,都會(huì)回收它的內(nèi)存共耍,對(duì)應(yīng)WeakReference.
4.虛引用:虛引用并不會(huì)決定對(duì)象的生命周期虑灰,如果一個(gè)對(duì)象僅持有虛引用,這就和沒(méi)有任何引用一樣痹兜,在任何時(shí)候都可能被垃圾收集器回收穆咐。對(duì)應(yīng)PhantomReference類(lèi)。
兩種標(biāo)記算法:
1.引用計(jì)數(shù)法
其基本思想就是每個(gè)對(duì)象都有一個(gè)引用計(jì)數(shù)器,當(dāng)對(duì)象在某處被引用的時(shí)候对湃,它的引用計(jì)數(shù)器就加1崖叫,引用失效時(shí)就減1.當(dāng)值為0時(shí),該對(duì)象就不能被使用熟尉,變成了垃圾归露。
目前主流的Java虛擬機(jī)沒(méi)有選擇引用計(jì)數(shù)算法來(lái)標(biāo)記垃圾,是因?yàn)闆](méi)有解決對(duì)象之間相互循環(huán)引用的問(wèn)題斤儿。

2.根搜索算法
這個(gè)算法的基本思想就是選定一些對(duì)象作為GC Roots剧包,并組成根對(duì)象集合,然后以這些GC Roots的對(duì)象作為起始點(diǎn)往果,向下搜索疆液,如果目標(biāo)對(duì)象到GC Roots是連著的,我們稱(chēng)該目標(biāo)對(duì)象是可達(dá)的陕贮,如果目標(biāo)對(duì)象不可達(dá)則說(shuō)明目標(biāo)對(duì)象時(shí)可以被回收的對(duì)象堕油。解決了計(jì)數(shù)算法無(wú)法解決的問(wèn)題:已經(jīng)死亡的對(duì)象因?yàn)橄嗷ヒ枚荒鼙换厥铡?在Java中,可以作為GC Roots的對(duì)象主要有以下幾種:
①Java棧中引用的對(duì)象
②本地方法棧中JNI引用的對(duì)象
③方法區(qū)中運(yùn)行時(shí)常量池引用的對(duì)象
④方法區(qū)中靜態(tài)屬性引用的對(duì)象
⑤運(yùn)行中的線(xiàn)程
⑥由引導(dǎo)類(lèi)加載器加載的對(duì)象
⑦GC控制的對(duì)象
被標(biāo)記為不可達(dá)的對(duì)象會(huì)立即被垃圾回收器回收嗎肮之?

Java對(duì)象在虛擬機(jī)中的生命周期

java對(duì)象被類(lèi)加載器加載到虛擬機(jī)中后掉缺,在java虛擬機(jī)中有7個(gè)階段:
①創(chuàng)建階段
②應(yīng)用階段
③不可見(jiàn)階段
在程序中找不到對(duì)象的任何強(qiáng)引用,但仍可能被特殊的強(qiáng)引用GC Roots持有著戈擒,比如對(duì)象被本地方法棧中JNI引用或被運(yùn)行中的線(xiàn)程引用等眶明。
④不可達(dá)階段
垃圾回收器發(fā)現(xiàn)對(duì)象不可達(dá)
⑤收集階段
⑥終結(jié)階段
等待垃圾收集器回收該對(duì)象空間。
⑦對(duì)象空間重新分配階段

垃圾回收算法

1.標(biāo)記-清除算法
分為兩個(gè)階段:
標(biāo)記階段:標(biāo)記出可以回收的對(duì)象
清除階段:回收被標(biāo)記的對(duì)象所占用的空間
缺點(diǎn):標(biāo)記和清除的效率都不高筐高,容易產(chǎn)生大量不連續(xù)的內(nèi)存碎片搜囱。
2.復(fù)制算法
將內(nèi)存空間劃為兩個(gè)相等的區(qū)域,每次只使用其中一個(gè)區(qū)域柑土。在垃圾收集時(shí)蜀肘,遍歷當(dāng)前使用的區(qū)域,將存活的對(duì)象復(fù)制到另外一個(gè)區(qū)域中稽屏,最后將當(dāng)前使用的區(qū)域的可回收的對(duì)象進(jìn)行回收扮宠。
缺點(diǎn):使用內(nèi)存為原來(lái)的一半。如果存活對(duì)象很少狐榔,復(fù)制算法的效率就很高坛增,但絕大多數(shù)對(duì)象的生命周期都很短,并且這些生命周期很短的對(duì)象都存于新生代中荒叼,所以復(fù)制算法被廣泛應(yīng)用于新生代中轿偎。

  1. 標(biāo)記-壓縮算法
    與標(biāo)記-清除算法不同的是典鸡,在標(biāo)記可回收的對(duì)象后將所有存活的對(duì)象壓縮到內(nèi)存的一端被廓,使它們緊湊地排列在一起,然后對(duì)邊界以外的內(nèi)存進(jìn)行回收萝玷。解決了內(nèi)存碎片的問(wèn)題嫁乘,被廣泛應(yīng)用于老年代中昆婿。
分代收集算法

java堆區(qū)中大部分的對(duì)象生命周期很短,少部分對(duì)象生命周期很長(zhǎng)蜓斧。應(yīng)該對(duì)不同生命周期的對(duì)象采取不同的收集策略仓蛆,根據(jù)生命周期長(zhǎng)短將他們放到不同的內(nèi)存區(qū)域,并在不同的區(qū)域采用不同的收集方法挎春,這就是分代的概念】锤恚現(xiàn)在主流的Java虛擬機(jī)的垃圾收集器都是采用的分代收集算法。Java堆區(qū)基于分代的概念直奋,分為新生代和老年代能庆。其中新生代再細(xì)分為Eden空間、From Survivor空間和 To Survivor空間脚线。Eden空間中覺(jué)大多數(shù)對(duì)象生命周期很短搁胆,所以新生代的空間劃分并不是均勻的,HotSpot虛擬機(jī)默認(rèn)Eden空間和兩個(gè)Survivor空間的所占的比例為8:1.
分代收集中垃圾收集的類(lèi)型分為兩種:
①新生代垃圾收集
②老年代垃圾收集邮绿,通常情況下會(huì)伴隨至少一次的新生代垃圾收集渠旁,它的收集頻率較低,耗時(shí)較長(zhǎng)船逮。

當(dāng)執(zhí)行一次新生代垃圾收集時(shí)顾腊,Eden空間的存活對(duì)象會(huì)被復(fù)制到To Survivor空間,并且之前經(jīng)過(guò)一次新生代垃圾收集并在From Survivor空間存活的仍年輕的對(duì)象也會(huì)復(fù)制到To Survivor空間傻唾。有兩種情況Eden空間和From Survivor空間存活的對(duì)象不會(huì)被復(fù)制到To Survivor空間投慈,而是晉升到老年代。一種是存活的對(duì)象的分代年齡超過(guò)-XX:MaxTenuringThreshold所指定閾值冠骄。另一種是To Survivor空間容量達(dá)到閾值伪煤。當(dāng)所有存活的對(duì)象都會(huì)復(fù)制到To Survivor空間或晉升到老年代,也就意味著Eden空間和From Survivor空間剩下的都是可回收的對(duì)象凛辣。這個(gè)時(shí)候GC執(zhí)行新生代垃圾收集抱既,Eden空間和From Survivor空間都會(huì)被清空,新生代中存活的對(duì)象都在To Survivor中扁誓。接下來(lái)From Survivor和To Survivor空間互換位置防泵,每次Survivor空間互換都要保證To Survivor是空的,這就是復(fù)制算法在新生代中的應(yīng)用蝗敢。
老年代則會(huì)采用標(biāo)記-壓縮算法或者標(biāo)記-清除算法焦履。(畢竟收集頻率低,耗時(shí)長(zhǎng))

Dalvik和ART

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末眨层,一起剝皮案震驚了整個(gè)濱河市疏旨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖咏瑟,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拂到,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡码泞,警方通過(guò)查閱死者的電腦和手機(jī)兄旬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)余寥,“玉大人领铐,你說(shuō)我怎么就攤上這事∷蜗希” “怎么了罐孝?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)肥缔。 經(jīng)常有香客問(wèn)我莲兢,道長(zhǎng),這世上最難降的妖魔是什么续膳? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任改艇,我火速辦了婚禮,結(jié)果婚禮上坟岔,老公的妹妹穿的比我還像新娘谒兄。我一直安慰自己,他們只是感情好社付,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布承疲。 她就那樣靜靜地躺著,像睡著了一般鸥咖。 火紅的嫁衣襯著肌膚如雪燕鸽。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,749評(píng)論 1 289
  • 那天啼辣,我揣著相機(jī)與錄音啊研,去河邊找鬼。 笑死鸥拧,一個(gè)胖子當(dāng)著我的面吹牛党远,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播富弦,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼沟娱,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了腕柜?” 一聲冷哼從身側(cè)響起济似,我...
    開(kāi)封第一講書(shū)人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤柳爽,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后碱屁,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蛾找,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年娩脾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片打毛。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡柿赊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出幻枉,到底是詐尸還是另有隱情碰声,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布熬甫,位于F島的核電站胰挑,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏椿肩。R本人自食惡果不足惜瞻颂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望郑象。 院中可真熱鬧贡这,春花似錦、人聲如沸厂榛。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)击奶。三九已至辈双,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間柜砾,已是汗流浹背辐马。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留局义,地道東北人喜爷。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像萄唇,于是被迫代替她去往敵國(guó)和親檩帐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容