寫給Android App開發人員看的Android底層知識(2)

(五)AMS

如果站在四大組件的角度來看,AMS就是Binder中的Server。

AMS全稱是ActivityManagerService,看字面意思是管理Activity的,但其實四大組件都歸它管。估計是Android底層開發人員先寫了ActivityManagerService用來管理Activity,后來寫Service、Receiver、CP的時候發現代碼都差不多,于是就全都用ActivityManagerService,但是卻忘記改名字了——我也是猜的,純屬八卦。

由此而說到了插件化,我記得16年和Lody、張勇、林光亮一起吃夜宵的時候,我當時問了困惑已久的兩個問題:

1)App的安裝過程,為什么不把apk解壓縮到本地,這樣讀取圖片就不用每次從apk包中讀取了——這個問題,我們放到PMS那一節再詳細說。

2)為什么Hook永遠是在Binder Client端,也就是四大組件這邊,而不是在AMS那一側進行Hook。

這里要說清楚第二個問題。就拿Android剪切板舉例吧。前面說過,這也是個Binder服務。

AMS要負責和所有App的四大組件進行通信,也真夠他忙的。如果在一個App中,在AMS層面把剪切板功能給篡改了,那會導致Android系統所有的剪切板功能被篡改——這就是病毒了,如果是這樣的話,Android系統早就死翹翹了。所以Android系統不允許我們這么做。

我們只能在AMS的另一側,Client端,也就是四大組件這邊做篡改,這樣即使我們把剪切板功能篡改了,也只影響篡改代碼所在的App,在別的App中,剪切板功能還是正常的。

關于AMS我們就說這么多,下面介紹四大組件時,會反復提到四大組件和AMS的跨進程通信。

(六)Activity 第1講

對于做App的開發人員而言,Activity是四大組件中用的最多的,也是最復雜的,我這里只講Activity的啟動和通信原理。還有一些相關的概念,比如說View、Looper、Intent、Resource,我以后另起章節來介紹。

注:我對四大組件的分析,都是基于羅升陽的那本分析Android底層的書,我把其中羅列的大部分代碼都刪掉了,只保留那些對App開發人員有用的一些代碼片段和一些關鍵類名,并融入了我對四大組件的理解。

1)首先要搞清,App是怎么啟動的。

在手機屏幕上點擊某個App的Icon,假設就是斗魚App吧,這個App的首頁(或引導頁)就出現在我們面前了。這個看似簡單的操作,背后經歷了Activity和AMS的反反復復的通信過程。

首先要搞清楚,在手機屏幕上點擊App的icon快捷圖標,此時手機屏幕就是一個Activity,而這個Activity所在的App,業界稱之為Launcher。Launcher是手機系統廠商提供的,類似小米華為這樣的手機,比拼的就是誰的Launcher絢麗和人性化。

Launcher這個App,其實和我們做的各類應用類App沒有什么不同,我們大家用過華為、小米之類的手機,預裝App以及我們下載的各種App,都顯示在Launcher上,每個App表現為一個Icon。Icon多了可以分頁,可以分組,此外,Launcher也會發起網絡請求,調用天氣的數據,顯示在屏幕上,所謂的人性化界面。

還記得我們在開發一款App時,在Manifest文件中是怎么定義默認啟動Activity的么?如下所示:

而Launcher中為每個App的icon提供了啟動這個App所需要的Intent信息,如下所示(比如說斗魚的包名是):

action:android.intent.action.MAIN

category: android.intent.category.LAUNCHER

cmp: 斗魚的包名+ 首頁Activity名

這些信息是App安裝(或Android系統啟動)的時候,PackageManagerService從斗魚的apk包的manifest文件中讀取到的。

所以點擊icon就啟動了斗魚App中的首頁。

2)啟動App哪有那么簡單

前面介紹的只是App啟動的一個最簡單的概述。

仔細看,我們會發現,Launcher和斗魚是兩個不同的App,他們位于不同的進程中,它們之間的通信是通過Binder完成的——這時候AMS出場了。

仍然以啟動斗魚App為例子,整體流程是:

1. Launcher通知AMS,要啟動斗魚App,而且指定要啟動斗魚的哪個頁面(也就是首頁)。
?2. AMS通知Launcher,好了我知道了,沒你什么事了,同時,把要啟動的首頁記下來。
?3. Launcher當前頁面進入Paused狀態,然后通知AMS,我睡了,你可以去找斗魚App了。
?4. AMS檢查斗魚App是否已經啟動了。是,則喚起斗魚App即可。否,就要啟動一個新的進程。AMS在新進程中創建一個ActivityThread對象,啟動其中的main函數。
?5. 斗魚App啟動后,通知AMS,說我啟動好了。
?6. AMS翻出之前在第二步存的值,告訴斗魚App,啟動哪個頁面。
?7. 斗魚App啟動首頁,創建Application,創建Context并與首頁Activity關聯。然后調用首頁Activity的onCreate函數。

至此啟動流程完成,分成兩部分,1-3步,Launcher和AMS相互通信,而后面幾步,斗魚App和AMS相互通信。

這會牽扯一堆類進來,列舉如下,在接下來的分析中,我們都會遇到:

  • Instrumentation
  • ActivityThread
  • H
  • LoadedApk
  • AMS
  • ActivityManagerNative和ActivityManagerProxy
  • ApplicationThread和ApplicationThreadProxy

第1階段 Launcher通知AMS

這是我根據老羅那本書中對Activity的分析,自己手繪的UML圖,一共七張,也就是Activity啟動所經歷的七個階段。建議各位讀者也親自手繪一遍,從而就加深理解。

  • 第1步、第2步

從上圖中我們看到,點擊Launcher上的斗魚App的icon快捷圖標,這時會調用Launcher的startActivitySafely方法,其實還是會調用Activity的startActivity方法,intent中帶著要啟動斗魚App所需要的關鍵信息,如下所示:

action = "android.intent.action.MAIN"

category = "android.intent.category.LAUNCHER"

cmp = "com.douyu.activity.MainActivity"

第3行代碼是我猜的,就是斗魚App在Mainfest文件中指定為首頁的那個Activity。這樣,我們終于明白,為什么在Mainfest中,給首頁指定action和category了。在app的安裝過程中,會把這個信息"記錄"在Launcher的斗魚啟動快捷圖標中。關于App的安裝過程,我會在后面的文章詳細介紹。

startActivity這個方法,如果我們看它的實現,會發現它調來調去,經過一系列startActivity的重載方法,最后會走到startActivityForResult方法。

我們知道startActivityForResult需要兩個參數,一個是intent,另一個是code,這里code是-1,表示Launcher才不關心斗魚的App是否啟動成功了呢。

第3步: startActivityForResult

Activity內部會保持一個對Instrumentation的引用,但凡是做過App單元測試的同學,對這個類都很熟悉,稱之為儀表盤。

在startActivityForResult方法的實現中,會調用Instrumentation的execStartActivity方法。

看到這里,我們發現有個mMainThread變量,這是一個ActivityThread類型的變量。

這個家伙的來頭可不小。

ActivityThread,就是主線程,也就是UI線程,它是在App啟動時創建的,它代表了App應用程序。

啥?ActivityThread代表了App應用程序,那Application類豈不是被架空了?其實,Application對我們App開發人員來說也許很重要,但是在Android系統中還真的沒那么重要,他就是個上下文。Activity不是有個Context上下文嗎?Application就是整個ActivityThread的上下文。

ActivityThread則沒有那么簡單了。它里面有main函數。

我們知道大部分程序都有main函數,比如java、C#,遠了不說,iPhone App用到的Objective-C,也有main函數,那么Android的main函數藏在哪里?就在ActivityThread中,如下所示,代碼太多,我只截取了一部分

又有人會問?不是說誰寫的程序,誰就要提供main函數,作為入口嗎?但Android App卻不是這樣的。Android App的main函數,在ActivityThread里面,而這個類是Android系統提供的底層類,不是我們提供的。

所以這就是Andoid有趣的地方。Android App的入口是Mainifest中定義默認啟動Activity。這是由Android AMS與四大組件的通信機制決定的。

最近在看吳越版的西游記,就發現這個西天取經為啥用了十幾年啊?因為這師徒四個取經路上愛管閑事,所以耽擱了很久。我這篇文章也是如此,經常講著講著就跑題了,再這么寫下去,不知道要寫到啥時候,所以我們一路向西,徑直往前走,再遇到奇怪的類,先不要理它。

在回到代碼來,這里要傳遞2個很重要的參數:

  • 通過ActivityThread的getApplicationThread方法取到一個Binder對象,它的類型為ApplicationThread,它代表著Launcher所在的App進程。
  • mToken,這也是個Binder對象,它代表了Launcher這個Activity,這里也通過Instrumentation傳給AMS,AMS一查電話簿,就知道是誰向AMS發起請求了。

這兩個參數是伏筆,傳遞給AMS,以后AMS想反過來通知Launcher,就能通過這兩個參數,找到Launcher。

第4步,Instrumentation的execStartActivity方法

Instrumentation絕對是Adnroid測試團隊的最愛,因為它可以幫我們啟動Activity。

回到我們的App啟動過程來,在Instrumentation的execStartActivity方法中,

我理解這就是一個透傳,Activity把數據借助Instrumentation,傳遞給ActivityManagerNative,沒太多有趣的內容,就不多講了。

第5步:AMN的getDefault方法

ActivityManagerNative,簡稱AMN。這個類后面會反復用到。

AMN通過getDefault方法,從ServiceManager中取得一個名為activity的對象,然后把它包裝成一個ActivityManagerProxy對象(簡稱AMP),AMP就是AMS的代理對象。

備注1:ServiceManager是一個容器類。

備注2: AMN的getDefault方法返回類型為IActivityManager,而不是AMP。IActivityManager是一個實現了IInterface的接口,里面定義了四大組件所有的生命周期。

AMN和AMP都實現了IActivityManager接口,AMS繼承自AMN(好亂),那么對照著前面AIDL的UML,就不難理解了:

第6步,AMP的startActivity方法

看到這里,你會發現AMP的startActivity方法,和AIDL的Proxy方法,是一模一樣的,寫入數據到另一個進程,也就是AMS,然后等待AMS返回結果。

至此,第一階段的工作就做完了。

后續流程請參加下一篇文章。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容