上一章我們講解了我們的檢視是如何顯示到介面上的,這一章我們來講講View是如何新增到window上的,換句話說就是我們的DecorView是如何新增到Window上的。
同樣,我們先帶著疑問:
我們的DecorView是怎麼新增到Window上的呢?
這個問題我們要先從Activity的啟動開始講起。我們這裡從Java層面開始,不考慮其他層面。
Activity的啟動時通過ActivityThread來進行發起的。它會呼叫自己的handleLaunchActivity()
方法,
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
if (r.profilerInfo != null) {
mProfiler.setProfiler(r.profilerInfo);
mProfiler.startProfiling();
}
// Make sure we are running with the most recent config.
handleConfigurationChanged(null, null);
if (localLOGV) Slog.v(
TAG, "Handling launch of " + r);
// Initialize before creating the activity
WindowManagerGlobal.initialize();
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
if (!r.activity.mFinished && r.startsNotResumed) {
// The activity manager actually wants this one to start out paused, because it
// needs to be visible but isn't in the foreground. We accomplish this by going
// through the normal startup (because activities expect to go through onResume()
// the first time they run, before their window is displayed), and then pausing it.
// However, in this case we do -not- need to do the full pause cycle (of freezing
// and such) because the activity manager assumes it can just retain the current
// state it has.
performPauseActivityIfNeeded(r, reason);
// We need to keep around the original state, in case we need to be created again.
// But we only do this for pre-Honeycomb apps, which always save their state when
// pausing, so we can not have them save their state when restarting from a paused
// state. For HC and later, we want to (and can) let the state be saved as the
// normal part of stopping the activity.
if (r.isPreHoneycomb()) {
r.state = oldState;
}
}
} else {
// If there was an error, for any reason, tell the activity manager to stop us.
try {
ActivityManagerNative.getDefault()
.finishActivity(r.token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}複製程式碼
在這個方法中它首先會呼叫performLaunchActivity()
。好, 我們看這個方法:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//省略部分程式碼
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
...
} catch (Exception e) {
...
}
try {
...
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);
...
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()");
}
r.activity = activity;
r.stopped = true;
if (!r.activity.mFinished) {
activity.performStart();
r.stopped = false;
}
if (!r.activity.mFinished) {
if (r.isPersistable()) {
if (r.state != null || r.persistentState != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
r.persistentState);
}
} else if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
}
if (!r.activity.mFinished) {
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnPostCreate(activity, r.state,
r.persistentState);
} else {
mInstrumentation.callActivityOnPostCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onPostCreate()");
}
}
}
r.paused = true;
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
}
return activity;
}複製程式碼
首先,它會通過反射獲取Activity,然後他會呼叫我們Activity的attach()
方法,而我們的PhoneWindow是在Activity的attach()方法中初始化的,大家可以自己檢視原始碼。因此,初始化的過程都是在Activity中發起呼叫的。初始化完成之後, 它會呼叫callActivityOnCreate()
,這裡就是會呼叫activity的,也就是說我們Activity的宣告週期,執行過程,都是在我們ActivityThread中一次執行的,大家可以自己往後看,生命週期次序都有。
看完了performLaunchActivity ()
方法,我們回到handleLaunchActivity ()
,接著往下看,它會執行handleResumeActivity()
,我們看方法名,就可以猜到它肯定會去呼叫resume方法吧。
它的流程大概就是handleResumeActivity()
->performResumeActivity()
->activity.performResume()
。
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
...
//第一步
// TODO Push resumeArgs into the activity for consideration
r = performResumeActivity(token, clearHide, reason);
...
//第二步
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
//第三步
wm.addView(decor, l);
...
}複製程式碼
好,我們繼續看handleResumeActivity ()
,他會去拿window,decor,還有WindowManager,然後把我們Decor新增到我們WindowManager裡面去。那這個windowManager是什麼呢,我們點進去一看是個介面,那我們要找它的實現類,它是實現類是WindowManagerImpl。所以我們要看它的addView()方法。
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}複製程式碼
它呼叫的是WindowManagerGlobal的addView()方法,我們只能再點進去看。程式碼都很長,我都貼關鍵的,其他的就省略了。
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
//繪製流程真正發起點
ViewRootImpl root;
...
root = new ViewRootImpl(view.getContext(), display);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
...
root.setView(view, wparams, panelParentView);
}複製程式碼
它首先建立一個ViewRootImpl,然後把我們的Decor,viewrootImp,和一些引數都儲存到了這個Global類中,也就是說,我們的WindowManagerGlobal是用來管理ViewRoot和DecorView的。接下來他會把我們的DecorView設定給ViewRoot,這就是這篇文章最關鍵的一個方法了。我們點進去看ViewRoot的setView()方法:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
//將我們的DecorView賦值給mView,因此下面的mView就是DecorView
mView = view;
...
// 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();
...
//發起了跨程式訊息
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
...
//給DecorView設定parent,我們view呼叫requsetLayout的時候,其實呼叫的都是viewrootImp的requestLayout
view.assignParent(this);
}複製程式碼
首先會將我們的DecorView賦值給mView,因此下面的mView就是DecorView,這個mView這裡沒用到,下面的UI遍歷會用到。我們先來看viewRoot的requestLayout()中的scheduleTraversals();
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}複製程式碼
這裡開啟了一個runnable,我們點進去看
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}複製程式碼
而doTraversal()中有一個方法performTraversals()
,這個相信只要是看過UI繪製的部落格的人應該都很熟悉這個方法吧,這個方法就是真正執行整個UI繪製的遍歷過程。
private void performTraversals() {
...
// cache mView since it is used so much below...
final View host = mView;
...
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
performLayout(lp, mWidth, mHeight);
...
performDraw();
}複製程式碼
這篇文章不僅僅講了DecorView是如何新增到Window的,還梳理了一遍View的繪製發起點,雖然篇幅較短,但也費了些心思。
下一篇章我們真正開始DecorView的繪製流程講解。下篇文章計劃將結合《Android開發藝術探索》和其他相關部落格,並加上個人的理解,總結出一篇希望能幫助大家理解UI繪製流程的文章,盡請期待!
由於個人水平有限,文章中可能會出現錯誤,如果你覺得哪一部分有錯誤,或者發現了錯別字等內容,歡迎在評論區告訴我。
望共勉。
參考
騰訊課堂
本文為貓舍原創作品,轉載請註明原文出處: imlerry.com/2017/05/11/…。