Q:從這篇文章你能學到什么伍俘?
A:這篇文章從桌面上點開一個APP圖標開始分析,一直到里面各個view也就是控件顯示到我們眼睛的整個過程勉躺。讓你了解整個APP啟動流程和View繪制流程癌瘾。看完你肯定會愛上它的饵溅,Believe Me妨退!
開始思考:當我們點擊手機桌面上的一個軟件時,從點擊到完整顯示這個APP整個過程中發(fā)生了什么蜕企?咬荷??
先忍受一下枯燥轻掩,理論知識還是得先在腦海中留下印象的
一幸乒、從Activity啟動流程中理論知識入門
砸門先來了解幾個關(guān)鍵的名詞,下面會用到哦
1.Launcher--桌面系統(tǒng)APP
??Launcher其實就是一個app唇牧,從功能上說罕扎,是對手機上其他app的一個管理和啟動聚唐,從代碼上說比其他app多了個屬性,就是在AndroidManifest.xml文件中多了個“<categoryandroid:name="android.intent.category.HOME" />”屬性腔召,這個屬性就是在啟動系統(tǒng)或者按Home鍵時會過濾這個屬性杆查,如果系統(tǒng)中只有一個應用具有這個屬性,就會直接跳轉(zhuǎn)到這個界面臀蛛,也就是這個launcher亲桦,如果有多個,會彈出選擇框讓用戶選擇并且提示用戶是否選擇默認設(shè)置浊仆。
??(其實Launcher就是我們玩手機時按Home鍵時跳轉(zhuǎn)到桌面一個道理客峭,說白了,Launcher就是我們的桌面氧卧,他就是個系統(tǒng)的App)我們點擊的每個程序圖標就是他的一個item桃笙,會觸發(fā)onclick事件,接收事件然后Launcher會打開我們的應用沙绝。
2.Zygote--一個進程
??zygote意為“受精卵“搏明。Android是基于Linux系統(tǒng)的,而在Linux中闪檬,所有的進程都是由init進程直接或者是間接fork出來的星著,zygote進程也不例外。在Android系統(tǒng)里面粗悯,zygote是一個進程的名字虚循。Android是基于Linux System的,當你的手機開機的時候样傍,Linux的內(nèi)核加載完成之后就會啟動一個叫“init“的進程横缔。在Linux System里面,所有的進程都是由init進程fork出來的衫哥,我們的zygote進程也不例外茎刚。我們都知道,每一個App其實都是一個單獨的dalvik虛擬機和一個單獨的進程撤逢。
??所以當系統(tǒng)里面的第一個zygote進程運行之后膛锭,在這之后再開啟App,就相當于開啟一個新的進程蚊荣。而為了實現(xiàn)資源共用和更快的啟動速度初狰,Android系統(tǒng)開啟新進程的方式,是通過fork第一個zygote進程實現(xiàn)的互例。所以說奢入,除了第一個zygote進程,其他應用所在的進程都是zygote的子進程媳叨。這下你明白為什么這個進程叫“受精卵”了吧俊马?因為就像是一個受精卵一樣丁存,它能快速的分裂,并且產(chǎn)生遺傳物質(zhì)一樣的細胞柴我!
3.SystemServer--一個系統(tǒng)服務進程管家(管家管著很多個服務)
SystemServer也是一個進程,而且是由zygote進程fork出來的扩然。
知道了SystemServer的本質(zhì)艘儒,我們對它就不算太陌生了,這個進程是Android Framework里面兩大非常重要的進程之一,另外一個進程就是上面的zygote進程夫偶。為什么說SystemServer非常重要呢界睁?因為系統(tǒng)里面重要的服務都是在這個進程里面開啟的,比如ActivityManagerService兵拢、PackageManagerService翻斟、WindowManagerService等等。
4.ActivityManagerService--一個服務(和四大組件相溝通的)
AMS是Android中最核心的服務之一说铃,主要負責系統(tǒng)中四大組件的啟動访惜、切換、調(diào)度及應用進程的管理和調(diào)度等工作腻扇,其職責與操作系統(tǒng)中的進程管理和調(diào)度模塊相類似债热,因此它在Android中非常重要,它本身也是一個Binder的實現(xiàn)類幼苛。
ActivityManagerService進行初始化的時機很明確窒篱,就是在SystemServer進程開啟的時候,就會初始化舶沿。
5.Instrumentation和ActivityThread(和Activity和Application相關(guān))
??每個Activity都持有Instrumentation對象的一個引用墙杯,但是整個進程只會存在一個Instrumentation對象。
??Instrumentation這個類里面的方法大多數(shù)和Application和Activity有關(guān)括荡,這個類就是完成對Application和Activity初始化和生命周期的工具類高镐。Instrumentation這個類很重要,對Activity生命周期方法的調(diào)用根本就離不開他一汽,他可以說是一個大管家避消。
??ActivityThread,就是UI線程召夹,應用的入口類岩喷,通過調(diào)用main方法,開啟消息循環(huán)隊列监憎。App和AMS是通過Binder傳遞信息的纱意,那么ActivityThread就是專門與AMS的外交工作的。
堅持,理論知識快沒了
二主胧、Activity啟動整體流程
1.基礎(chǔ)類介紹
1)ApplicationThread:
提供Binder通訊接口,AMS則通過代理調(diào)用此App進程的本地方法
2)ActivityManagerProxy
AMS服務在當前進程的代理類圾亏,負責與AMS通信类少。
3)ApplicationThreadProxy
ApplicationThread在AMS服務中的代理類叙身,負責與ApplicationThread通信。
2.整體流程圖
3.流程整體分析--(參考上圖分析)
1.點擊桌面App圖標硫狞,Launcher進程采用Binder IPC向system_server進程發(fā)起startActivity請求信轿;
2.system_server進程接收到請求后,向zygote進程發(fā)送創(chuàng)建進程的請求残吩;
3.Zygote進程fork出新的子進程财忽,即App進程;
4.App進程泣侮,通過Binder IPC向sytem_server進程發(fā)起attachApplication請求即彪;
5.AMS向ApplicationThreadProxy發(fā)送realStartActivityLocked請求。
6.system_server進程在收到請求后活尊,進行一系列準備工作后隶校,再通過binder IPC向App進程發(fā)送scheduleLaunchActivity請求;
7.App進程的binder線程(ApplicationThread)在收到請求后酬凳,通過handler向主線程發(fā)送LAUNCH_ACTIVITY消息惠况;
8.主線程在收到Message后,通過調(diào)用handleLaunchActivity創(chuàng)建目標Activity宁仔,并回調(diào)Activity.onCreate()等方法稠屠。
9.到此,App便正式啟動翎苫,開始進入Activity生命周期权埠,執(zhí)行完onCreate/onStart/onResume方法,UI渲染結(jié)束后便可以看到App的主界面煎谍。
總結(jié):
1.AMS是在System Server中的攘蔽,是其中的一個服務
2.APP進程和AMS之間通信都是通過Binder進行間接通信的
3.Zygote創(chuàng)建出APP進程之后,然后就是創(chuàng)建Application
4.接著創(chuàng)建Activity
總的就是這么個邏輯順序
在堅持堅持呐粘,我們看會源碼满俗,先知道大概流程在看源碼會輕松很多的,不看源碼很非常容易忘記的
4.Application是怎么創(chuàng)建出來的?
ActivityThread里面有個ApplicationThread內(nèi)部類作岖,這個內(nèi)部類繼承了IApplicationThread的內(nèi)部類Stub唆垃,所以最終調(diào)用的是ActivityThread的內(nèi)部類ApplicationThread里面的bindApplication方法,故我們找到這個方法:
//ActivityThread.ApplicationThread.java
@Override
public final void bindApplication(String processName, ApplicationInfo appInfo,
ProviderInfoList providerList, ComponentName instrumentationName,
ProfilerInfo profilerInfo, Bundle instrumentationArgs,
IInstrumentationWatcher instrumentationWatcher,
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableBinderTracking, boolean trackAllocation,
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
String buildSerial, AutofillOptions autofillOptions,
ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges) {
...
//1.將該方法的參數(shù)等信息封裝到AppBindData對象中
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providerList.getList();
data.instrumentationName = instrumentationName;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.instrumentationUiAutomationConnection = instrumentationUiConnection;
data.debugMode = debugMode;
data.enableBinderTracking = enableBinderTracking;
data.trackAllocation = trackAllocation;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
data.buildSerial = buildSerial;
data.autofillOptions = autofillOptions;
data.contentCaptureOptions = contentCaptureOptions;
data.disabledCompatChanges = disabledCompatChanges;
//2.通過Handler發(fā)送消息痘儡,并攜帶AppBindData參數(shù)
sendMessage(H.BIND_APPLICATION, data);
}
通過上面bindApplication方法的源碼可以看出辕万,在該方法里面對入?yún)⑦M行一個封裝,然后采用Handler機制攜帶AppBindData對象發(fā)送消息。通過sendMessage方法的第一個參數(shù)可以發(fā)現(xiàn)渐尿,接收消息的what標志是定義在一個H的類里面醉途,由此可以想到,最終處理這條消息的handleMessage方法應該也在這個H類里面砖茸,我們進入到這個H類里面隘擎,果然,很快就可以發(fā)現(xiàn)H類是Handler的子類渔彰,并且可以找到handleMessage方法嵌屎,在這里可以看到對BIND_APPLICATION的處理邏輯:
//ActivityThread.H.java
public void handleMessage(Message msg) {
...
switch (msg.what) {
//1.綁定application
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
//2.獲取傳過來的AppBindData實例
AppBindData data = (AppBindData)msg.obj;
//3.調(diào)用handleBindApplication方法處理邏輯
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
}
...
}
handleMessage接收到消息后,通過handleBindApplication方法來處理邏輯:
//ActivityThread.java
@UnsupportedAppUsage
private void handleBindApplication(AppBindData data) {
...
//1.創(chuàng)建ApplicationContext
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
...
//2.聲明Application
Application app;
...
//3.創(chuàng)建Application
app = data.info.makeApplication(data.restrictedBackupMode, null);
...
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
//從這里可以看出ContentProvider要先于application.onCreate執(zhí)行
installContentProviders(app, data.providers);
}
}
try {
//調(diào)用Application的onCreate方法
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
...
}
}
以上第1處的源碼為應用生成了上下文Context恍涂,即通過getApplicationContext()獲取到的ContextImpl,ContextImpl是Context的子類,然后通過data.info.makeApplication(data.restrictedBackupMode, null)方法創(chuàng)建app植榕,我們看一下這個方法:
//LoadedApk.java
@UnsupportedAppUsage
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
//1.如果不為空再沧,直接返回
return mApplication;
}
...
Application app = null;
//2.獲取Application的className
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
//3.如果獲取到的appClass為空,則使用默認Application的className
appClass = "android.app.Application";
}
try {
//4.獲取類加載器ClassLoader
final java.lang.ClassLoader cl = getClassLoader();
if (!mPackageName.equals("android")) {
...
initializeJavaContextClassLoader();
...
}
...
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
...
//5.通過反射創(chuàng)建Application
app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
...
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
}
通過上面的makeApplication方法源碼尊残,可以很清晰的知道炒瘸,Application是通過類加載器和反射創(chuàng)建的。上面第2步首先獲取Application的className顷扩,此處獲取的className即為我們項目中manifests文件中的application節(jié)點中name屬性定義的Application,然后在下面第3步對這個className進行判空婶芭,如果為空的話,就賦值為默認的className呵哨。緊接著獲取類加載器,然后在第5步通過類加載器和className利用反射創(chuàng)建Application纹坐。
到此Application就創(chuàng)建完成,然后我們回到前面ActivityThread.java類中的handleBindApplication方法绒障,創(chuàng)建完Application之后,調(diào)用了mInstrumentation.callApplicationOnCreate(app)方法庐镐,該方法源碼如下:
//Instrumentation.java
/**
* Perform calling of the application's {@link Application#onCreate}
* method. The default implementation simply calls through to that method.
*
* <p>Note: This method will be called immediately after {@link #onCreate(Bundle)}.
* Often instrumentation tests start their test thread in onCreate(); you
* need to be careful of races between these. (Well between it and
* everything else, but let's start here.)
*
* @param app The application being created.
*/
public void callApplicationOnCreate(Application app) {
app.onCreate();
}
簡潔明了揽乱,這個方法里面直接調(diào)用了Application的onCreate方法损拢,方法上面的注釋也說明的很清楚福压。
5.Activity到底是怎么創(chuàng)建出來的?
與Activity相關(guān)的類就是ActivityThread嚷那,ActivityThread的final H mH = new H()內(nèi)部類H繼承于Handler腐泻,通過handler消息機制,簡單說Handler機制用于同一個進程的線程間通信铆惑。Activity的生命周期都是依靠主線程的Looper.loop,當收到不同Message時則采用相應措施:在H.handleMessage(msg)方法中受裹,根據(jù)接收到不同的msg,執(zhí)行相應的生命周期
例如:
當msg.what == LAUNCH_ACTIVITY就是調(diào)用handleLaunchActivity方法啟動一個Activity照藻,在handleLaunchActivity中又調(diào)用了performLaunchActivity方法來創(chuàng)建一個Activity實例,完成Activity的啟動。 handleLaunchActivity源碼如下:
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
//...調(diào)用performLaunchActivity方法完成Activity的啟動
Activity a = performLaunchActivity(r, customIntent);
//...
}
H繼承與Handle,重寫了handleMessage的方法
public final class ActivityThread {
//...
final H mH = new H();
private class H extends Handler {
//...聲明的一些常量
public void handleMessage(Message msg) {
//...
switch (msg.what) {
//針對不同的常量,做不同的業(yè)務處理
case LAUNCH_ACTIVITY: {
//...啟動一個Activity
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
//...
} break;
case RELAUNCH_ACTIVITY: {
//...
handleRelaunchActivity(r);
//...
} break;
case PAUSE_ACTIVITY: {
//...
handlePauseActivity((IBinder) args.arg1, false,
(args.argi1 & USER_LEAVING) != 0, args.argi2,
(args.argi1 & DONT_REPORT) != 0, args.argi3);
maybeSnapshot();
//...
} break;
//...
}
//...
}
private void maybeSnapshot() {
//...這個方法主要統(tǒng)計snapshot
}
}
}
這個類主要作用就是根據(jù)不同的情況處理各種業(yè)務坤次,而且處理業(yè)務的方法一般是以handle開頭滑绒,handleXXX的格式疑故,如下:
handleActivityConfigurationChanged()
handleBindApplication()
handleBindService()
handleCancelVisibleBehind()
handleConfigurationChanged()
handleCreateService()
handleDestroyActivity()
handleDispatchPackageBroadcast()
handleLaunchActivity()
handleLowMemory()
handleMessage()
handleNewIntent()
handlePauseActivity()
handleReceiver()
handleRelaunchActivity()
handleResumeActivity()
handleSendResult()
handleServiceArgs()
handleStopActivity()
handleStopService()
而這些函數(shù)有的又會調(diào)用到如下的performXXX系列函數(shù)完成最終的事件處理:
performDestroyActivity()
performDestroyActivity()
performLaunchActivity()
performNewIntents()
performPauseActivity()
performPauseActivity()
performRestartActivity()
performResumeActivity()
performStopActivity()
performStopActivityInner()
performUserLeavingActivity()
//ActivityThread.java
@Override
public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent) {
...
//創(chuàng)建并啟動activity
final Activity a = performLaunchActivity(r, customIntent);
...
}
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//獲取activity信息
ActivityInfo aInfo = r.activityInfo;
...
//獲取component
ComponentName component = r.intent.getComponent();
//獲取上下文
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
//獲取ClassLoader
java.lang.ClassLoader cl = appContext.getClassLoader();
//通過ClassLoader加載activity软舌,再通過反射創(chuàng)建activity實例
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
...
} catch (Exception e) {
...
}
try {
//獲取Application
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
...
if (activity != null) {
...
// Activity resources must be initialized with the same loaders as the
// application context.
appContext.getResources().addLoaders(app.getResources().getLoaders().toArray(new ResourcesLoader[0]));
appContext.setOuterContext(activity);
//調(diào)用Activity的attach方法
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken);
...
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
//設(shè)置主題
activity.setTheme(theme);
}
...
//調(diào)用Activity的onCreate方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
...
r.activity = activity;
...
//設(shè)置Activity生命周期狀態(tài)為ON_CREATE
r.setState(ON_CREATE);
...
}
}
performLaunchActivity方法中首先通過ActivityClientRecord獲取activity信息恋脚,然后就是通過ClassLoader和反射創(chuàng)建activity實例焰手,接著再調(diào)用activity的attach方法船响,然后設(shè)置activity的主題啊等一些信息,最后再調(diào)用Activity的onCreate方法并將生命周期狀態(tài)記錄為ON_CREATE。
activity的attach方法調(diào)用
我們先來簡單看看activity.attach方法:
//Activity.java
@UnsupportedAppUsage
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
...
//創(chuàng)建PhoneWindow并賦值給mWindow
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(mWindowControllerCallback);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
...
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
...
}
attach方法里面主要是創(chuàng)建和配置PhoneWindow惊橱。在這里不細聊attach方法税朴。下面還會詳細介紹的
activity的onCreate方法調(diào)用
我們繼續(xù)看activity的onCreate方法的調(diào)用,接著上面mInstrumentation.callActivityOnCreate方法看:
//Instrumentation.java
public void callActivityOnCreate(Activity activity, Bundle icicle) {
//做一些準備工作
prePerformCreate(activity);
//真正執(zhí)行Activity的onCreate
activity.performCreate(icicle);
postPerformCreate(activity);
}
我們直接看activity.performCreate(icicle)方法宙枷,這個方法在Activity中:
//Activity.java
final void performCreate(Bundle icicle) {
//調(diào)用下面的重載方法
performCreate(icicle, null);
}
@UnsupportedAppUsage
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
dispatchActivityPreCreated(icicle);
mCanEnterPictureInPicture = true;
...
//此處真正調(diào)用了Activity的onCreate生命周期方法
if (persistentState != null) {
onCreate(icicle, persistentState);
} else {
onCreate(icicle);
}
}
至此Activity的創(chuàng)建和onCreate生命周期方法的調(diào)用結(jié)束了
總結(jié):
1.Application創(chuàng)建出來后掉房,會調(diào)用scheduleLaunchActivity()發(fā)送一個LAUNCH_ACTIVITY消息到消息隊列H類中
2.通過 handleLaunchActivity()來處理該消息
3.通過performLaunchActiivty()方法回調(diào)Activity的onCreate()方法和onStart()方法
4.后通過handleResumeActivity()方法,回調(diào)Activity的onResume()方法
5.最終顯示Activity界面慰丛。
6.源碼流程整體總結(jié)
1.創(chuàng)建進程
①先從Launcher的startActivity()方法卓囚,通過Binder通信蝇棉,調(diào)用ActivityManagerService的startActivity方法。
②一系列折騰邑跪,最后調(diào)用startProcessLocked()方法來創(chuàng)建新的進程。
③該方法會通過前面講到的socket通道傳遞參數(shù)給Zygote進程视粮。Zygote孵化自身瓤狐。調(diào)用ZygoteInit.main()方法來實例化ActivityThread對象并最終返回新進程的pid。
④調(diào)用ActivityThread.main()方法掰派,ActivityThread隨后依次調(diào)用Looper.prepareLoop()和Looper.loop()來開啟消息循環(huán)趟薄。
2.創(chuàng)建Application
①上面創(chuàng)建進程后腰湾,執(zhí)行ActivityThread.main()方法谎痢,隨后調(diào)用attach()方法峰鄙。
②將進程和指定的Application綁定起來。通過Binder IPC向sytem_server進程發(fā)起attachApplication請求雪侥。這個是通過上節(jié)的ActivityThread對象中調(diào)用bindApplication()方法完成的速缨。該方法發(fā)送一個BIND_APPLICATION的消息到消息隊列中, 最終通過handleBindApplication()方法處理該消息. 然后調(diào)用makeApplication()方法來加載App的classes到內(nèi)存中贮预。
3.創(chuàng)建Activity
經(jīng)過前兩個步驟之后, 系統(tǒng)已經(jīng)擁有了該application的進程贮缅。 后面的調(diào)用順序就是普通的從一個已經(jīng)存在的進程中啟動一個新進程的activity了榨咐。
實際調(diào)用方法是realStartActivity(), 它會調(diào)用application線程對象中的scheduleLaunchActivity()發(fā)送一個LAUNCH_ACTIVITY消息到消息隊列H類中,然后 通過 handleLaunchActivity()來處理該消息。在 handleLaunchActivity()通過performLaunchActiivty()方法回調(diào)Activity的onCreate()方法和onStart()方法谴供,然后通過handleResumeActivity()方法块茁,回調(diào)Activity的onResume()方法,最終顯示Activity界面桂肌。
神概括数焊,讓你記住這個流程
1.首先,手機上有個桌面(Launcher)崎场,桌面里面很多app佩耳,這些app也就是Launcher的孩子
2.然后孩子對系統(tǒng)服務管家(system_server)說我要開始啟動了,你準備好了嗎谭跨?
3.然后系統(tǒng)服務管家(system_server)開始通知其他人做好準備干厚,首先通知的是Zygote管家
4.Zygote管家接收到命令,收到螃宙,然后他自己就準備給這個app創(chuàng)建一個它的家蛮瞄,也就是屬于它自己獨有的APP進程
5.然后APP進程開始部署自己的家
6.APP進程對系統(tǒng)服務管家(system_server)說,我要先關(guān)聯(lián)APP的Application,
7.系統(tǒng)服務管家(system_server)做完一系列準備后通知APP進程的ApplicationThread小管家
8.Application已經(jīng)給你了谆扎,你自己處理了挂捅。
9.ApplicationThread小管家收到之后,Yes,sir!然后通知另外一個更小的管家ActivityThread
10.ActivityThread管家就是具體某個頁面Activity的小管家堂湖,然后他就比較忙了
11.接下來他就要完成onCreate,onstart,onResume等工作之后才能休息
12.終于闲先,一系列工作都完成了状土。這個APP有了自己的家,于是新家誕生了
13.喬遷新居伺糠,一家人有了一個溫馨的家蒙谓,夕陽西下,落下帷幕
醒醒醒醒退盯,美好的東西過去了彼乌,回歸現(xiàn)實哈,劃重點啦
到此為止渊迁,從桌面點擊APP到APP的Activity界面顯示的整個流程已經(jīng)分析完了慰照。但這只是把Activity顯示出來了,Activity里面還包含很多的控件琉朽,也就是View,這些View才是最終顯示給我們看到的毒租,Activity只是管理這些View的生命周期而已。
所以箱叁,你懂的墅垮,不要犯困,俺們在堅持堅持耕漱!接下來分析的是Activity之后自定義View是如何從繪制測量布局等一步步顯示到我們眼睛的算色!
三、View顯示過程中常見名詞解釋
不好意思螟够,又要帶你繼續(xù)帶你看那些難懂抽象的丑八怪了灾梦,你會不會討厭我啊,別妓笙,親親若河,我發(fā)誓,就下面一點丑八怪寞宫,后面都是你喜歡的哦萧福,你懂得?砸們堅持住啊辈赋,殺蚌耆獭!
1.Activity--這個在熟悉不過了吧
Activity并不負責視圖控制钥屈,它只是控制生命周期和處理事件悟民。真正控制視圖的是Window。一個Activity包含了一個Window焕蹄,Window才是真正代表一個窗口。Activity就像一個控制器阀溶,統(tǒng)籌視圖的添加與顯示腻脏,以及通過其他回調(diào)方法鸦泳,來與Window、以及View進行交互永品。
2.Window--一個包含很多視圖的容器
Window是視圖的承載器做鹰,內(nèi)部持有一個 DecorView,而這個DecorView才是 view 的根布局鼎姐。Window是一個抽象類钾麸,實際在Activity中持有的是其子類PhoneWindow。PhoneWindow中有個內(nèi)部類DecorView炕桨,通過創(chuàng)建DecorView來加載Activity中設(shè)置的布局R.layout.activity_main饭尝。Window 通過WindowManager將DecorView加載其中,并將DecorView交給ViewRoot献宫,進行視圖繪制以及其他交互钥平。
3.DecorView--根視圖,老大
DecorView是FrameLayout的子類姊途,它可以被認為是Android視圖樹的根節(jié)點視圖涉瘾。DecorView作為頂級View,一般情況下它內(nèi)部包含一個豎直方向的LinearLayout捷兰,在這個LinearLayout里面有上下三個部分立叛,上面是個ViewStub,延遲加載的視圖(應該是設(shè)置ActionBar贡茅,根據(jù)Theme設(shè)置)秘蛇,中間的是標題欄(根據(jù)Theme設(shè)置,有的布局沒有)友扰,下面的是內(nèi)容欄彤叉。 具體情況和Android版本及主體有關(guān),以其中一個布局為例村怪,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:fitsSystemWindows="true"
android:orientation="vertical">
<!-- Popout bar for action modes -->
<ViewStub
android:id="@+id/action_mode_bar_stub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:theme="?attr/actionBarTheme" />
<FrameLayout
style="?android:attr/windowTitleBackgroundStyle"
android:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize">
<TextView
android:id="@android:id/title"
style="?android:attr/windowTitleStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@null"
android:fadingEdge="horizontal"
android:gravity="center_vertical" />
</FrameLayout>
<FrameLayout
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:foreground="?android:attr/windowContentOverlay"
android:foregroundGravity="fill_horizontal|top" />
</LinearLayout>
在Activity中通過setContentView所設(shè)置的布局文件其實就是被加到內(nèi)容欄之中的秽浇,成為其唯一子View,就是上面的id為content的FrameLayout中甚负。所以我們每天在Activity里自動生成的setContentView(R.layout.activity_main)加載的布局就是到DecorView中的內(nèi)容欄的柬焕。
4.ViewRoot--多事的,測量梭域、布局斑举、繪制和事件分發(fā)它都要管
?? ViewRoot可能比較陌生,但是其作用非常重大病涨。所有View的繪制和事件分發(fā)等交互都是通過它來執(zhí)行或傳遞的富玷。
?? ViewRoot對應ViewRootImpl類,它是連接WindowManagerService和DecorView的紐帶,View的三大流程(測量(measure)赎懦,布局(layout)雀鹃,繪制(draw))均通過ViewRoot來完成。
??ViewRoot并不屬于View樹的一份子励两。從源碼實現(xiàn)上來看黎茎,它既非View的子類,也非View的父類当悔,但是傅瞻,它實現(xiàn)了ViewParent接口,這讓它可以作為View的名義上的父視圖盲憎。ViewRoot繼承了Handler類嗅骄,可以接收事件并分發(fā),Android的所有觸屏事件焙畔、按鍵事件掸读、界面刷新等事件都是通過ViewRoot進行分發(fā)的。
四宏多、DecorView是怎么創(chuàng)建的
先是從Activity中的setContentView()開始儿惫。
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
可以看到實際是調(diào)用getWindow().setContentView,這個getWindow()指的就是Window的實現(xiàn)類PhoneWindow伸但。接下來我們看看這個PhoneWindow是什么時候被創(chuàng)建的肾请。
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window) {
..................................................................
mWindow = new PhoneWindow(this, window);//創(chuàng)建一個Window對象
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);//設(shè)置回調(diào),向Activity分發(fā)點擊或狀態(tài)改變等事件
mWindow.setOnWindowDismissedCallback(this);
.................................................................
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);//給Window設(shè)置WindowManager對象
....................................................................
}
在Activity中的attach()方法中更胖,生成了PhoneWindow實例铛铁。然后我們在看看DecorView是怎么被嵌入到PhoneWindow中的
public void setContentView(int layoutResID) {
if (mContentParent == null) {//mContentParent為空,創(chuàng)建一個DecroView
installDecor();
} else {
mContentParent.removeAllViews();//mContentParent不為空却妨,刪除其中的View
}
mLayoutInflater.inflate(layoutResID, mContentParent);//為mContentParent添加子View,即Activity中設(shè)置的布局文件
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();//回調(diào)通知饵逐,內(nèi)容改變
}
}
看了下來,可能有一個疑惑:mContentParent到底是什么彪标? 就是前面布局中@android:id/content所對應的FrameLayout倍权。
繼續(xù)分析PhoneWindow類的setContentView方法
從源碼可以知道,這里主要包括三個步驟:
- 如果父容器為空則初始化父容器捞烟,否則移除所有子視圖薄声;
- 調(diào)用LayoutInflater類的inflate方法將xml布局文件加載到父容器;
- 回調(diào)Callback通知ContentView發(fā)生改變题画,其中的Callback可能由Activity實現(xiàn)默辨。
后面兩步比較簡單,這里主要來看第一步的父容器初始化流程苍息,進入PhoneWindow類的installDecor方法:
PhoneWindow類的installDecor方法
這個方法的代碼有點兒長缩幸,這里只截取重要部分壹置,主要操作包括三部分:
- 調(diào)用generateDecor()創(chuàng)建出mDecor,即DecorView對象表谊;
- generateLayout(mDecor)傳入mDecor對象蒸绩,生成mContentParent ;
- 設(shè)置標題欄信息铃肯。
installDecor方法中調(diào)用的generateLayout方法:
這個方法代碼非常多,我們只需要關(guān)注重點即可传蹈。
1. 首先獲取<Application android:theme=""/>, <Activity/>節(jié)點指定的themes或者代碼押逼;
2. 然后獲取窗口Features, 設(shè)置相應的修飾布局文件,這些xml文件位于frameworks/base/core/res/res/layout下惦界;
3. 接著調(diào)用了DecorView的onResourcesLoaded方法將上面選定的布局文件inflate為View挑格,添加到DecorView中;
4. 找到id為content的framlayout賦給mContentParent沾歪,由于已經(jīng)將屏幕View加為mDecor的子View漂彤,因此mContentParent也是mDecor的子View;
5. 設(shè)置mDecor的背景和標題灾搏。
再回到PhoneWindow的setContentView方法中挫望, 繼續(xù)調(diào)用了mLayoutInflater.inflate(layoutResID, mContentParent),在這里就是把我們寫的布局文件通過inflater加入到mContentParent中狂窑。這樣我們寫的布局文件成功的添加到DecorView中的mContentParent媳板。
現(xiàn)在只是完成了DecorView的創(chuàng)建并初始化,我們還需要把這個創(chuàng)建并初始化完DecorView添加并顯示到屏幕上泉哈,這里我們就需要用到WindowManager蛉幸。
通過上面的流程我們大致知道,在Activity里面有個window的兒子PhoneWindow丛晦,PhoneWindow先生了一個老大DecroView奕纫,其中創(chuàng)建的過程中可能根據(jù)Theme不同,加載不同的布局格式烫沙,例如有沒有Title匹层,或有沒有ActionBar等,然后老大帶著很多個弟弟新出生的弟弟妹妹斧吐,也就是setContentView加載的布局中的控件又固。到此位置,window家族有了很多個孩子煤率。
五仰冠、DecorView的顯示
以上僅僅是將老大DecorView生出來了,還有他的弟弟妹妹layout里面的控件加進來了蝶糯。但是他們是什么時候被爸爸媽媽帶出來給外人看見他們這么可愛的模樣的呢洋只,通過setContentView()設(shè)置的界面,為什么在onResume()之后才對用戶可見呢?
這就要從ActivityThread识虚,也就是那個管理Activity的管家開始說起肢扯。
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//就是在這里調(diào)用了Activity.attach()呀,接著調(diào)用了Activity.onCreate()和Activity.onStart()生命周期担锤,
//但是由于只是初始化了mDecor蔚晨,添加了布局文件,還沒有把
//mDecor添加到負責UI顯示的PhoneWindow中肛循,所以這時候?qū)τ脩魜碚f铭腕,是不可見的
Activity a = performLaunchActivity(r, customIntent);
......
if (a != null) {
//這里面執(zhí)行了Activity.onResume()
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);
if (!r.activity.mFinished && r.startsNotResumed) {
try {
r.activity.mCalled = false;
//執(zhí)行Activity.onPause()
mInstrumentation.callActivityOnPause(r.activity);
}
}
}
}
重點看下handleResumeActivity(),在這其中,DecorView將會顯示出來多糠,同時重要的一個角色:ViewRoot也將登場累舷。
final void handleResumeActivity(IBinder token, boolean clearHide,
boolean isForward, boolean reallyResume) {
//這個時候,Activity.onResume()已經(jīng)調(diào)用了夹孔,但是現(xiàn)在界面還是不可見的
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
//decor對用戶不可見
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
//被添加進WindowManager了被盈,但是這個時候,還是不可見的
wm.addView(decor, l);
}
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
//在這里搭伤,執(zhí)行了重要的操作,使得DecorView可見
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
}
}
}
當我們執(zhí)行了Activity.makeVisible()方法之后只怎,界面才對我們是可見的。
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());//將DecorView添加到WindowManager
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);//DecorView可見
}
調(diào)用addView方法,這個方法非常關(guān)鍵怜俐,wm是上面a.getWindowManager()獲取到的mWindowManger對象尝盼,而這個對象是WindowManagerImpl。
繼續(xù)進入WindowManagerImpl類的addView方法:
WindowManagerImpl類的addView方法
其實WindowManagerImpl類的方法大部分都是代理的WindowManagerGlobal的方法佑菩。繼續(xù)進入WindowManagerGlobal類的addView方法:
WindowManagerGlobal類的addView方法
從上面的代碼可以看出盾沫,addView方法中,創(chuàng)建了一個ViewRootImpl對象殿漠,然后調(diào)用ViewRootImpl.setView()方法赴精,繼續(xù)查看setView()方法。
ViewRootImpl類的setView方法
該方法首先將傳進來的參數(shù)view賦值給mView绞幌,mView將是這個對象所認識的root節(jié)點蕾哟,也是整個Activity的root的節(jié)點,即DecorView莲蜘。
setView方法調(diào)用了requestLayout方法
接著調(diào)用了requestLayout()方法谭确,首次調(diào)度執(zhí)行 layout,這里會觸發(fā) onAttachToWindow 和 創(chuàng)建 Surface方法票渠。深入查看ViewRootImpl中requestLayout()方法:
該方法首先檢查了是否在主線程逐哈,然后就執(zhí)行了scheduleTraversals()方法。
這里需要注意的就是Runnable對象问顷,繼續(xù)往后看:
這個Runnable的run()方法中昂秃,調(diào)用了doTraversal()方法:
可以看到doTraversal()方法又調(diào)用了performTraversals()方法:
performTraversals方法調(diào)用 performMeasure方法
performTraversals方法調(diào)用 performLayout方法
performTraversals方法調(diào)用 performDraw方法
這個方法非常長禀梳,內(nèi)部邏輯也很復雜,但是主體邏輯很清晰肠骆。其執(zhí)行的過程可簡單的概括為:是否需要重新計算視圖的大小(measure)算途、是否需要重新布局視圖的位置(layout),以及是否需要重繪(Draw)蚀腿。也就是我們常說的View的繪制流程嘴瓤,由于這里涉及的內(nèi)容實在太多,關(guān)于View的繪制后續(xù)再分享莉钙。
回到ViewRootImpl類的setView()方法纱注,繼續(xù)查看源碼:
setView方法調(diào)用assignParent方法
從這里可以看到view的父親注冊為自己,于是mDecor知道了自己父親是誰胆胰,即整個Activity設(shè)置了一個根節(jié)點,在此之前調(diào)用setContentView()只是將自己的layout布局add到PhoneWindow.mContentParent刻获,但是mDecor并不知道自己的parent是誰蜀涨,現(xiàn)在整個view的樹形結(jié)構(gòu)中有了根節(jié)點,也就是ViewRootImpl蝎毡,那么requestLayout()就有效了厚柳,就可以進行后面的measure、layout沐兵、draw三步操作了别垮。
具體流程圖如下所示
其實ViewRootImpl的作用不止如此,還有許多功能扎谎,如事件分發(fā)碳想。
要知道,當用戶點擊屏幕產(chǎn)生一個觸摸行為毁靶,這個觸摸行為則是通過底層硬件來傳遞捕獲胧奔,然后交給ViewRootImpl,接著將事件傳遞給DecorView预吆,而DecorView再交給PhoneWindow龙填,PhoneWindow再交給Activity,然后接下來就是我們常見的View事件分發(fā)了拐叉。
硬件 -> ViewRootImpl -> DecorView -> PhoneWindow -> Activity
終于熬到頭了Q乙拧!7锸荨宿礁!作者,你這個騙子蔬芥!老是騙我說在堅持堅持窘拯!其實沒騙你红且,看到這,你肯定不想罵我了涤姊,你肯定受益匪淺暇番!
六、從Activity初始化到顯示各個View到界面過程總結(jié)
1.從Activity的setContentView(R.id.layout)開始思喊,加載一個布局id到DecorView的內(nèi)容欄中壁酬。
2.在Activity中的attach方法中初始化Window抽象類的實例PhoneWindow對象。
3.把加載進來的布局id也就是DecorView添加到PhoneWindow中恨课。到處視圖就嵌套上來了舆乔。
4.在ActivityThread類中的handleLaunchActivity方法中調(diào)用了Activity.attach()方法,然后開始Activity生命周期的創(chuàng)建剂公。
5.調(diào)用里面的handleResumeActivity()方法中的makeVisible()顯示布局希俩,也就是DecorView.
6.在makeVisible()過程中創(chuàng)建了一個ViewRoot抽象類的對象ViewRootImpl。
7.WindowManager交給WindowManagerImpl實現(xiàn)纲辽,再交給WindowManagerGlobal 的addView()方法颜武。
里面實例化了ViewRootImpl對象,然后調(diào)用其setView()方法拖吼。最終就是將DecorView添加并顯示到屏幕上
8.setView()方法經(jīng)過各種調(diào)用最終調(diào)用performTraversals()方法鳞上,然后就是自定義View的顯示流程了
9.先調(diào)用peformMeasure,再measure,再onMeasure完成測量
10.同理完成布局Layout
11.在同理完成繪制Draw
12.最終顯示出來
神概述--不懂你打我
1.setContentView加載布局吊档,先生了個老大DecorView
2.然后再生layout里面的孩子篙议,把他們交給老大DecorView帶
3.怎么顯示出來的呢一家人
4.通過onResume方法執(zhí)行后才解開他們的廬山真面目
5.他們是怎么決定哪個孩子長哪樣的呢
6.就是ViewRootImpl這個家伙來決定的,我決定你是白雪公主還是丑小鴨
7.通過自定義View一個個繪制出來
8.一家人都有自己的樣子了怠硼,然后就給人們帶出來看看這個美好的世界了
七鬼贱、面試總結(jié)
1.Application創(chuàng)建流程?
答:
1.在ActivityThread類main方法調(diào)用attach() 方法香璃,
2.將進程和指定的Application綁定起來吩愧。通過Binder IPC向sytem_server進程發(fā)起attachApplication請求。這個是通過ActivityThread對象中調(diào)用bindApplication()方法完成的增显。該方法發(fā)送一個BIND_APPLICATION的消息到H hander中, 最終通過handleBindApplication()方法處理該消息. 然后調(diào)用 LoadedApk的makeApplication()方法
3.makeApplication()方法獲取了我們的 android.app.Application 類 的完整類名雁佳,得到了類的加載器,還有上下文同云,最后調(diào)用 Instrumentation 類的 newApplication() 方法糖权,通過反射的方式,創(chuàng)建了 Application 類的實例炸站。 然后調(diào)用mInstrumentation.callApplicationOnCreate(app);最終調(diào)用Application 類的 onCreate()星澳。
1.Activity創(chuàng)建流程?
答:>1.從Activity的setContentView(R.id.layout)開始旱易,加載一個布局id到DecorView的內(nèi)容欄中禁偎。
2.在Activity中的attach方法中初始化Window抽象類的實例PhoneWindow對象腿堤。
3.把加載進來的布局id也就是DecorView添加到PhoneWindow中。到處視圖就嵌套上來了如暖。
4.在ActivityThread類中的handleLaunchActivity方法中調(diào)用了performLaunchActivity,它里面調(diào)用Activity.attach()方法笆檀,然后回調(diào)Activity的onCreate()方法和onStart()方法
5.調(diào)用里面的handleResumeActivity()方法中的makeVisible()顯示布局态兴,也就是DecorView.
6.在makeVisible()過程中創(chuàng)建了一個ViewRoot抽象類的對象ViewRootImpl挠羔。
7.實例化了ViewRootImpl對象雕欺,然后調(diào)用其setView()方法翅溺。
8.setView()方法經(jīng)過各種調(diào)用最終調(diào)用performTraversals()方法,然后就是自定義View的顯示流程了
9.先調(diào)用peformMeasure动漾,再measure,再onMeasure完成測量
10.同理完成布局Layout
11.在同理完成繪制Draw
12.最終顯示出來
八创译、全劇終
你真的以為完了店溢,上面自定義View只是大概介紹了下酒唉,你以為自定義View那么簡單啊矩桂,還有好多好多呢。別灰心痪伦,能學到這說明你是個善于堅持的人侄榴,你會接著看下去的,期待下文---《通俗易懂吃透自定義View》
我好累啊流妻,寫文章有激情的時候能bb很多,但是有時候你準備寫的時候一個字都憋不出來笆制,那你就會很煩躁绅这。發(fā)現(xiàn)寫完文章,自己一步步的用自己語言概括出來在辆,突然發(fā)現(xiàn)android源碼都是我寫的哈哈哈哈证薇,膨脹了,寫文章還是能體會到很多的匆篓,但是就是有點耗時間啊哈哈哈哈浑度,親愛的你們,我知道你們也累了鸦概,休息去吧箩张。
慢著慢著,留下你的一鍵三連在走得唔得窗市!
感謝您的閱讀先慷,要是有收獲請記得三連點擊,別告訴我下次一定咨察!