前言:
在Java中,程序的入口是main函數(shù),那么在Android中,程序的入口是在哪里呢?一個(gè)APP啟動(dòng)的過(guò)程又是怎樣的呢? setContentView()加載的布局,是如何添加到窗口中的,如何顯示到屏幕上的? 帶著這些疑問(wèn),我們來(lái)跟著Android系統(tǒng)的源碼,一步一步分析!
注: 分析采用的API 23,看源碼的時(shí)候?qū)uild.gradle中的compileSdkVersion 指定為23,如果找不到ActivityThread等類,說(shuō)明sdk中的api是隱藏的,需要更換Android.jar,GitHub上有去掉 /** @hide */的版本,導(dǎo)入即可!
一:APP的啟動(dòng)流程
啟動(dòng)的入口類:ActivityThread
啟動(dòng)的流程:http://www.cloudchou.com/android/post-788.html
參考博客: https://blog.csdn.net/melodev/article/details/51959347
ActivityThread:
成員變量:
內(nèi)部類:
ApplicationThread
入口函數(shù):main
1. 初始化主線程Looper:
Looper.prepareMainLooper();
Looper.loop();
2. 創(chuàng)建ActivityThread:
ActivityThread thread = new ActivityThread();
thread.attach(false);//在這個(gè)方法中,將ApplicationThread 和AMS綁定,后續(xù)由AMS調(diào)用ApplicationThread的方法
3. 給主線程handler賦值
sMainThreadHandler = thread.getHandler();//return mH; mH = new H(); private class H extends Handler
ApplicationThread:
1. 此類有很多scheduleXXX方法,由AMS調(diào)用,如:scheduleLaunchActivity()
2. 分析:scheduleLaunchActivity()
最后調(diào)用:sendMessage(H.LAUNCH_ACTIVITY, r); -> mH.sendMessage(msg); //消息機(jī)制
3. 分析mH -> handleMessage():
case LAUNCH_ACTIVITY: -> handleLaunchActivity() -> performLaunchActivity() -> activity = mInstrumentation.newActivity();//創(chuàng)建出activity
繼續(xù)往下看: callActivityOnCreate(),//看到熟悉的OnCreate()啦
二:UI繪制流程
分析:setContentView() -> getWindow().setContentView()
Window:抽象類 -> 僅有的一個(gè)實(shí)現(xiàn)類 PhoneWinodw
PhoneWindow -> setContentView():
1. installDecor() : //加載布局容器,加載基礎(chǔ)布局
generateDecor(); ->new DecorView()
mContentParent = generateLayout(mDecor); ->requestFeature():// requestFeature()方法判斷是否設(shè)置了布局(拋異常),這也是為什么requestFeature()必須在setContentView()之前調(diào)用的原因
generateLayout():會(huì)根據(jù)不會(huì)的主題,加載不同的布局,如:layoutResource = R.layout.screen_simple;
打開(kāi)這個(gè)布局文件,會(huì)看到是一個(gè)線性布局,ViewStub,id為action_mode_bar_stub,這是狀態(tài)欄,還有一個(gè)id為content的FrameLayout
我們自己添加的布局,就是添加到這個(gè)幀布局下;
概念:
-> DecorView(繼承FrameLayout) 頂層view
-> mContentParent(是一個(gè)ViewGroup)
2. 加載我們自己的布局:mLayoutInflater.inflate(layoutResID, mContentParent); // xml解析
scheduleResumeActivity():setContentView()執(zhí)行完畢,onCreate()執(zhí)行完畢,接下來(lái)AMS調(diào)用 scheduleResumeActivity(),發(fā)消息:
RESUME_ACTIVITY:
-> handleResumeActivity()
performResumeActivity()://不看這個(gè)
wm.addView(decor, l);// ViewManager wm 實(shí)現(xiàn)類:WindowManager 還是一個(gè)接口,找到實(shí)現(xiàn)類:WindowManagerImpl
mGlobal.addView(view, params, mDisplay, mParentWindow);// mGlobal
root.setView(view, wparams, panelParentView);//調(diào)用了ViewRootImpl的setView()
requestLayout();//ViewRootImpl的requestLayout()
scheduleTraversals();
mTraversalRunnable //Runnable子類 -> run() ->doTraversal(); ->performTraversals(); ->進(jìn)入view的繪制流程
繪制流程: performTraversals()分析:(traversal 意思是遍歷)
measureHierarchy():
performMeasure():最后調(diào)用了 mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
performLayout(): -> 會(huì)遍歷所有子控件,執(zhí)行view.requestLayout(); ->view.layout()
performDraw(); -> draw(fullRedrawNeeded); -> view.draw()
分析完成后,可以回答以下問(wèn)題:
為什么requestWindowFeature(Window.FEATURE_NO_TITLE);這行代碼必須在setContentView()之前調(diào)用?
為什么在onCreate()方法中,我們調(diào)用view.getWidth()等方法,獲取不到view的寬高信息?