Framework源碼面試之a(chǎn)ctivity啟動流程
引言
今天在電腦上翻出了很久之前整理筆記Framework
源碼面試,Flutter
,以及一部分面試專題。拿出來溫習(xí)一下。
今天先講Framework
源碼篇:
1.Framework
源碼面試:Activity
啟動流程
2.Framework
源碼面試:Binder
面試
3.Framework
源碼面試:Handler
面試
4.Framework
源碼面試:事件分發(fā)機制
5.Framework
源碼面試:onMeasure
測量原理
6.Framework
源碼面試:Android
屏幕刷新機制
Framework源碼面試:activity啟動流程
面試的時候,面試官經(jīng)常同你隨便侃侃Activity
的啟動模式,但Activity
啟動牽扯的知識點其實很多,并非能單單用四個啟動模式就能概括的,
默認的啟動模式的表現(xiàn)會隨著Intent Flag
的設(shè)置而改變,因此侃Activity
啟動模式大多走流程裝逼,最多結(jié)合項目遇到的問題,隨便刁難一下面試者,并不太容易把控,也許最后,面試官跟面試者的答案都是錯了,
比如在Service
中必須通過設(shè)置FLAG_ACTIVITY_NEW_TASK
才能啟動Activity
,這個時候啟動Activit
會有什么樣的表現(xiàn)呢?就這一個問題,答案就要分好幾個場景:
Activity
的taskAffinity
屬性的Task
棧是否存在- 如果存在,要看
Activity
是否存已經(jīng)存在于該Task
- 如果已經(jīng)存在于該
taskAffinity
的Task
,要看其是不是其rootActivity
- 如果是其
rootActivity
,還要看啟動該Activity
的Intent
是否跟當前intent
相等
不同場景,所表現(xiàn)的行為都會有所不同,再比如singleInstance
屬性,如果設(shè)置了,大家都知道只有一個實例,將來再啟動會復(fù)用,但是如果使用Intent.FLAG_ACTIVITY_CLEAR_TASK
來啟動,仍然會重建,并非完全遵守singleInstance
的說明,還有不同Flag
在疊加使用時候也會有不同的表現(xiàn),單一而論Activity
啟動模式其實是很難的。本文也僅僅是涉及部分啟動模式及Flag
,更多組合跟場景要自己看源碼或者實驗來解決了。
1.1面試連環(huán)炮之說說 Android 的四種啟動模式
standard
這是 Activity
的默認啟動模式,每次激活 Activity
的時候都會創(chuàng)建一個新的 Activity
實例,并放入任務(wù)棧中。
使用場景:基本絕大多數(shù)地方都可以用。
singleTop
這可能也是非常常用的 launchMode
了。如果在任務(wù)的棧頂正好存有該 Activity
的實例,則會通過調(diào)用 onNewIntent()
方法進行重用,否則就會同 standard 模式一樣,創(chuàng)建新的實例并放入棧頂。即便棧中已經(jīng)存在了該 Activity 的實例,也會創(chuàng)建新的實例,即:A -> B ->A,此時棧內(nèi)為 A -> B -> A,但 A -> B ->B ,此時棧內(nèi)為 A -> B。一句話概述就是:當且僅當啟動的 Activity
和上一個 Activity
一致的時候才會通過調(diào)用 onNewIntent()
方法重用 Activity
。
使用場景:資訊閱讀類 APP 的內(nèi)容界面。
singleTask
這個 launchMode
專門用于解決上面 singleTop
的另外一種情況,只要棧中已經(jīng)存在了該 Activity
的實例,就會直接調(diào)用 onNewIntent()
方法來實現(xiàn)重用實例。重用時,直接讓該 Activity
的實例回到棧頂,并且移除之前它上面的所有 Activity
實例。如果棧中不存在這樣的實例,則和 standard
模式相同。即: A ->B -> C -> D -> B,此時棧內(nèi)變成了 A -> B。而 A -> B -> C,棧內(nèi)還是 A -> B -> C。
使用場景:瀏覽器的主頁面,或者大部分 APP 的主頁面。
singleInstance
在一個新棧中創(chuàng)建該 Activity
的實例,并讓多個應(yīng)用共享該棧中的該 Activity
實例。一旦該模式的 Activity
實例已經(jīng)存在于某個棧中,任何應(yīng)用再激活該 Activity
時都會重用該棧中的實例,是的,依然是調(diào)用 onNewIntent()
方法。其效果相當于多個應(yīng)用共享一個應(yīng)用,不管是誰激活,該 Activity 都會進入同一個應(yīng)用中。但值得引起注意的是:singleInstance
不要用于中間頁面,如果用戶中間頁面,跳轉(zhuǎn)會出現(xiàn)很難受的問題。 這個在實際開發(fā)中我暫未遇到過,不過 Android 系統(tǒng)的來電頁面,多次來電均是使用的同一個 Activity
。
四種模式的背書式理解記憶講完了,你認為這樣就結(jié)束了嗎?
對,我也一度是這樣認為的。
1.2.面試連環(huán)炮之說說 Intent標簽起什么作用呢? 簡單說一說
我們除了需要知道在 AndroidManifest.xml
里面設(shè)置 android:launchMode
屬性,我們還需要了解下面這幾個Intent
標簽的用法。
在 Android 中,我們除了在清單文件 AndroidManifest.xml
中配置 launchMode
,當然可以用 Intent
標簽說事兒。啟動 Activity
,我們需要傳遞一個 Intent
,完全可以通過設(shè)置 Intent.setFlags(int flags)
來設(shè)置啟動的 Activity
的啟動模式。
需要注意的是:通過代碼來設(shè)置 Activity
的啟動模式的方式,優(yōu)先級比清單文件設(shè)置更高。
FLAG_ACTIVITY_NEW_TASK
這個標識會使新啟動的Activity
獨立創(chuàng)建一個Task
。FLAG_ACTIVITY_CLEAR_TOP
這個標識會使新啟動的Activity
檢查是否存在于Task
中,如果存在則清除其之上的Activity
,使它獲得焦點,并不重新實例化一個Activity
,一般結(jié)合FLAG_ACTIVITY_NEW_TASK
一起使用。FLAG_ACTIVITY_SINGLE_TOP
等同于在launcherMode
屬性設(shè)置為singleTop
。
1.2.Android 的啟動原理,他的流程是什么樣的
總的流程圖:
1.2.1.進程A與AMS的交互過程
此處以跨進程啟動Activity
分析一下源碼流程:
①A調(diào)用startActivity
時,需要與AMS交互,此時需要需要獲取到AMS的代理對象Binder也就是上圖的AMP,
通過ActivityManagerNative.getDefault()
獲得,并調(diào)用AMP的startActivity
方法,然后會通過mRemote.transact
方法進行Binder通信,在AMS的onTransact
方法里面會獲取到請求的Activity
參數(shù)信息:
mRemote.transact(START_ACTIVITY_TRANSACTION,data,reply,0); @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags){ switch(code){ case START_ACTIVITY_TRANSACTION:{ startActivity(app,callingPackage,intent,...) } } }
②AMS里面的startActivity
方法最主要會去調(diào)用startSpecificActivityLocked
函數(shù),在此函數(shù)里面會去判斷目標進程是否已經(jīng)存在,并且目標向AMS注冊過它自己的ApplicationThread
也就是上圖ATP代理對象,如果這兩個條件都滿足會去調(diào)用realStartActivityLocked
方法,這個方法我們后面再看。如果上述條件不滿足時,會去調(diào)用mService.startProcessLocked(r.processName,...)
方法啟動進程。
startProcessLocked
方法首先調(diào)用Process.start("android.app.ActivityThread",)
方法會向Zygote
發(fā)送一個啟動進程的請求,并告知Zygote
進程啟動之后,加載ActivityThread
這個類的入口main
函數(shù),啟動完成后返回進程的pid,并向AMS的Handler發(fā)送一個延遲消息,為的是要求目標進程啟動后,10秒鐘內(nèi)需要向AMS報告,不然的話AMS就會清除目標進程的相關(guān)信息。Process.start
方法會去調(diào)用startViaZygote(processClass,)
函數(shù),這個函數(shù)主要做了兩件事,一件就是打開通往Zygote
的Socket
,第二件事就是通過Socket
發(fā)送啟動進程參數(shù)。Zygote
端主要邏輯是在runOnce
函數(shù),函數(shù)內(nèi)調(diào)用Zygote.forkAndSpecialize(...)
創(chuàng)建子進程,創(chuàng)建完成之后就分別在父進程和子進程里面做各自的事情.
父進程通過hanleParentProc(pid)
把子進程的pid
通過Socket
發(fā)送給AMS
子進程調(diào)用handleChildProc
函數(shù),做一些通用的初始化,比如啟用Binder
機制;執(zhí)行應(yīng)用程序的入口函數(shù),也就是ActivityThread
的Main
函數(shù).
ActivityThread
的main
函數(shù),里面會創(chuàng)建一個ActivityThread
對象,并調(diào)用thread.attach(false)
,為的是向AMS報到,上面第一條里面有提到。attach
方法里面,其實是一個跨進程的調(diào)用,首先通過
IActivityManager mgr = ActivityManagerNative.getDefault();
獲取到AMS的Binder代理對象,然后調(diào)用
mgr.attachApplication(mAppThread);
mAppThread
是應(yīng)用端的一個Binder對象ApplicationThread
,也就是最上面一張圖的ATP,這樣AMS端就可以調(diào)用應(yīng)用端了。
attachApplication
方法里面,最主要有兩個方法,一個是通過傳入的ApplicationThread
對象,調(diào)用bindApplication
初始化Application
對象,另一個就是通過
mStactSupervisor.attachApplicationLoacked(app);
初始化掛起的Activity
對象。
- 在
attachApplicationLoacked
函數(shù)里,會調(diào)用
ActivityRecord hr = stack.topRunningActivityLocked(null);
? 其中要明白AMS里面有兩個棧,一個是Launch
桌面棧,一個就是非桌面棧mFocusedStack
,此處的stack
就是mFocusedStack
,它會將棧頂?shù)?code>ActivityRecord返回出來,我們的目標Activity
早就放置在了棧頂,只是一直沒有初始化。然后調(diào)用方法,來啟動Activity
? 如果我們不是啟動另外一個進程,而是同一進程,那么這第二大部分就不會存在了,而是直接調(diào)用realStartActivityLocked
方法。
realStartActivityLocked(hr,app,true,true);
寫到這里是不是有很多碼牛的小伙伴們已經(jīng)堅持不下去了。還剩最后幾個步驟
① realStartActivityLocked
函數(shù)會調(diào)用app.thread.scheduleLaunchActivity(new Intent(r.intent),...)
;也就是通過之前注冊的Binder對象ATP,調(diào)用scheduleLaunchActivity
函數(shù),在scheduleLaunchActivity
函數(shù)里面:
ActivityClientRecord r = new ActivityClientRecord(); ... sendMessage(H.LAUNCH_ACTIVITY,r);
封裝了一個ActivityClientRecord
消息,然后丟到主線程的Handler(mH)里。
②在主線程里面
final ActivityClientRecord r = (ActivityClientRecord)msg.obj ; r.packageInfo = getPackageInfoNoCheck(...); handleLaunchActivity(r,null);
getPackageInfoNoCheck
函數(shù)主要是用來生成一個LoadedApk
對象,它用來保存我們的apk信息,因為后面我們需要一個ClassLoader
去加載Apk里面的Activity
類,所以這里提前準備好。
③handleLaunchActivity
里面分為兩個部分,一個是performLaunchActivity
函數(shù),一個是handleResumeActivity
函數(shù)。
performLaunchActivity
Activity activity = mInstrumentation.newActivity(...); //返回之前創(chuàng)建好的 Application app = r.packageInfo.makeApplication(false,mInstrumentation); //生成ContextImpl Context appContext = createBaseContextForActivity(r,activity); //給activity綁定上下文和一些初始化的工作,如createPhoneWindow activity.attach(appContext,...); mInstrumentation.callActivityOnCreate(activity,r.state); //生命周期的OnCreate activity.performStart(); //生命周期的OnStart return activity
④ handleResumeActivity
:
-> r.activity.performResume() -> mInstrumentation.callActivityOnResume(this); -> activity.onResume()
以上就是Framework源碼面試之a(chǎn)ctivity啟動流程的詳細內(nèi)容,更多關(guān)于Framework activity啟動流程的資料請關(guān)注腳本之家其它相關(guān)文章!