上一篇文章由setContentView()方法引發的思考看到了setContentView()對安卓源碼的一些思考,也揭示了Window,PhoneWindow,DecorView他們幾個的關係。但咱們從上篇文章裏留下了一些疑問WindowManager,ViewRoot,ViewRootImpl,PhoneWindow,WindowManagerService他們到底是什麼,他們之間有什麼樣的關係呢?看了網上不少文章,終於對他們有所瞭解,下面我就從Activity啓動一步一步分析到底咱們看到的View是怎麼建立出來的。 關於Activity的啓動流程至關複雜,我還沒時間去深究,可是從網上資料看出,Activity的啓動最後會到ActivityThread類的handleLaunchActivity方法裏面,那好,咱們就去相關的類看看:java
...
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
// Initialize before creating the activity
WindowManagerGlobal.initialize();
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
...
}
}
複製代碼
一來就是就有WindowManagerGlobal這個沒見過的對象,但從名字來看應該跟WindowManager有必定的關係。這裏先無論,往下看performLaunchActivity(r, customIntent)方法:bash
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
try { //Activity經過ClassLoader建立出來
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
} ...
try {
//建立Application
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
...
if (activity != null) {
//建立Activity所需的Context
Context appContext = createBaseContextForActivity(r, activity);
...
//將Context與Activity進行綁定
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);
...
//調用activity.oncreate
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
...
//調用Activity的onstart方法
activity.performStart();
//調用activitu的OnRestoreInstanceState方法進行Window數據恢復
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,r.persistentState);
...
return activity;
}
複製代碼
咱們先看到mInstrumentation.newActivity(cl,component.getClassName(), r.intent)方法session
public Activity newActivity(ClassLoader cl, String className,Intent intent)throws InstantiationException,IllegalAccessException,ClassNotFoundException {
return (Activity)cl.loadClass(className).newInstance();
}
複製代碼
是經過ClassLoader把activity建立出來,接着經過makeApplication(false, mInstrumentation)方法建立Application,接下來咱們看看咱們熟悉的attach()方法:app
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) {
//ContextImpl的綁定
attachBaseContext(context);
//在當前Activity建立Window
mWindow = new PhoneWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
...
//爲Window設置WindowManager
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());
}
//建立完後經過getWindowManager就能夠獲得WindowManager實例
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
}
複製代碼
咱們能夠看到一個咱們想要看到的東西出來了PhoneWindow和WindowManager,首先這裏建立了一個PhoneWindow對象,而後經過setWindowManager建立出一個WindowManager對象,而後將他們綁定在一塊兒。而後咱們點進去看看WindowManager:異步
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
複製代碼
好吧,這下放心了,WindowManagerImpl就是WindowManager的實現類。 接下來會到performLaunchActivity():ide
mInstrumentation.callActivityOnCreate(activity, r.state ...);
複製代碼
該方法則是調用activity.oncreate方法的佈局
public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
activity.performCreate(icicle);
postPerformCreate(activity);
}
final void performCreate(Bundle icicle) {
onCreate(icicle);
mActivityTransitionState.readState(icicle);
performCreateCommon();
}
複製代碼
接下來就是onstart()post
activity.performStart();
複製代碼
接下來是咱們熟悉的:ui
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,r.persistentState);
複製代碼
public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState,PersistableBundle persistentState) {
activity.performRestoreInstanceState(savedInstanceState, persistentState);
}
複製代碼
裏面經過Bundle來保存恢復Window窗口信息的,接下來回到handleLaunchActivity()方法:this
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
//上面分析完了
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
...
}
}
複製代碼
奇怪,怎麼過了onStart()方法都尚未出現View的繪製的三大流程呢,按我之前的理解。彆着急,好戲在後頭,咱們看看handleResumeActivity()方法的實現吧:
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
...
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
final Activity a = r.activity;
...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
//獲取DecorView
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
...
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
//把當前的DecorView與WindowManager綁定一塊兒
wm.addView(decor, l);
}
}
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
if (r.newConfig != null) {
//而後調用這個方法回調,表示屏幕參數發生了改變
performConfigurationChangedForActivity(r, r.newConfig);
...
r.newConfig = null;
}
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
//因爲前面設置了INVASIBLE,因此如今要把DecorView顯示出來了
r.activity.makeVisible();
}
}
// Tell the activity manager we have resumed.
if (reallyResume) {
try {
//通知ActivityManagerService,Activity完成Resumed ActivityManager.getService().activityResumed(token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
}
複製代碼
handleResumeActivity方法一開始就調用了activity = performResumeActivity()方法,但看過裏面的實現,並無咱們想要的東西,因此代碼就不貼出來的。重點在下面,
(1)經過r.window.getDecorView()方法獲取了DecorView;
(2)而後把DecorView設置爲INVISIBLE;
(3)而後獲取ViewManager
(4)經過addView的方法把DecorView綁定到ViewManager上;
(5)而後調用這個方法回調,表示屏幕參數發生了改變;
(6)經過makeVisible()的方法,把以前INVISIBLE的DecorView顯示出來;
(7)通知ActivityManagerService,Activity完成Resumed。
咱們看到handleResumeActivity()方法作了不少事,咱們最關注的是第(4)點。看到這個,忽然間靈光一現,記起來上篇文章摘錄藝術探索的時候有一句話:
ViewRoot對應於ViewRootImpl類,它是鏈接WindowManager和DecorView的紐帶
到這裏咱們都沒看到ViewRoot的身影,只在上面看到了ViewRootImpl,既然他們之間有什麼關係,那麼咱們就看看wm.addView(decor, l)這個方法裏面弄個明白吧。
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
ViewRootImpl root;
View panelParentView = null;
...
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
//ViewRootImpl開始繪製view
root.setView(view, wparams, panelParentView);
...
}
複製代碼
噢,咱們終於見到ViewRootImpl的身影了,咱們查資料發現,原來ViewRoot其實就是ViewRoot,那麼咱們總算找到咱們想要的東西了。咱們看看root.setView()方法:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
...
try {
...
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
}
}
複製代碼
在setView方法中, 首先會調用到requestLayout(),表示添加Window以前先完成第一次layout佈局過程。requestLayout最終會調用performTraversals方法來完成View的繪製。
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
複製代碼
咱們看看checkThread()方法:
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
複製代碼
這個方法實際上是判斷你是否在子線程更新UI線程的,因此說當你在onCreate()方法:
接着咱們往下看scheduleTraversals()方法:
void scheduleTraversals() {
if (!mTraversalScheduled) {
...
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
...
}
}
複製代碼
scheduleTraversals中會經過handler去異步調用mTraversalRunnable接口
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
複製代碼
void doTraversal() {
...
performTraversals();
...
}
複製代碼
接着看performTraversals()方法:
private void performTraversals() {
......
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
......
performDraw();
}
......
}
複製代碼
看到這裏你們應該都懂了,繪製的三大流程(measure,layout,draw)就是在這裏。那咱們得出的結果是:View的繪製是ViewRootImpl完成的,並且WindowManager的addView()方法實際上最終的實現也在ViewRootImpl裏面。
ViewRoot對應於ViewRootImpl類,它是鏈接WindowManager和DecorView的紐帶。
那這句話的意思你們也終於明白了吧。
接着咱們返回setView()方法,addToDisplay()方法會經過WindowSession最終來完成Window的添加過程。在下面的代碼中mWindowSession類型是IWindowSession,它是一個Binder對象,真正的實現類是Session,也就是說這實際上是一次IPC過程,遠程調用了Session中的addToDisPlay方法。
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
複製代碼
這裏的mService就是WindowManagerService,也就是說Window的添加請求,最終是經過WindowManagerService來添加的。這裏咱們終於見到WindowManagerService了。
然而還有一個問題,爲何咱們在開發的時候,在onCreate方法中調用view.getMeasureHeight() = 0呢?緣由很簡單,那是由於activity繪製的三大流程發生在activity.handleResumeActivity()方法中。那有人繼續問,那在onResume()方法調用爲何view.getMeasureHeight()仍是會等於0,緣由也很簡單,由於ViewRootImpl繪製View是異步進行的,因此爲了讓咱們拿到這個測量結果的數值,在viewRootImpl的performTraversals的測量佈局以後添加了:
if (triggerGlobalLayoutListener) {
mAttachInfo.mRecomputeGlobalAttributes = false;
mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
}
...
performDraw();
複製代碼
也就是咱們日常用的:
view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// TODO Auto-generated method stub
}
});
複製代碼
利用觀察者模式在onCreate()方法裏面設置監聽,咱們就能就能獲得想要的測量結果了。
而View是怎麼跟ViewRootImpl綁定的呢?
這篇文章講到很詳細: Android窗口機制(四)ViewRootImpl與View和WindowManager
在ViewRootImpl的構造方法中:
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
...
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
...
}
複製代碼
AttachInfo(IWindowSession session, IWindow window, Display display,ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer) {
mSession = session;
mWindow = window;
mWindowToken = window.asBinder();
mDisplay = display;
mViewRootImpl = viewRootImpl;
mHandler = handler;
mRootCallbacks = effectPlayer;
}
複製代碼
而ViewRootImpl是在WindowManagerGlobal的addView()中建立的:
root = new ViewRootImpl(view.getContext(), display);
複製代碼
這就很清楚了吧。下面小弟畫了個圖總結了一下,若是有錯的話請你們來糾正一下,謝謝~
參考文獻:
《Android開發藝術探索》
Android窗口機制(四)ViewRootImpl與View和WindowManager
個人掘金: juejin.im/user/594e8e…