我們都知道Activity的生命周期流程郁稍,我們也知道View繪制的三個(gè)方法onMeasure俱病、onLayout、onDraw
搂漠。但是你知道在啟動(dòng)一個(gè)Activity時(shí)迂卢,它們是工作在哪個(gè)生命周期的嗎?這邊我開(kāi)始做一個(gè)完整的分析。以下代碼都是基于Android 8.0的源碼進(jìn)行分析的冷守,由于本人能力有限刀崖,不喜勿噴。
首先用一個(gè)時(shí)序圖來(lái)表示這一整個(gè)分析流程拍摇,performTraversals
是View三大流程onMeasure亮钦、onLayout、onDraw
方法執(zhí)行開(kāi)始的地方充活,我們的分析的就是是從ActivityThread
到RootViewImpl
執(zhí)行performTraversals
的過(guò)程蜂莉,
ActivityThread
這個(gè)類(lèi)是我們平時(shí)說(shuō)的UI線程,但是他不是一個(gè)真的線程類(lèi)混卵。它是一個(gè)App程序運(yùn)行的管理和執(zhí)行的類(lèi)映穗,其中Activty的生命周期就是在這里通過(guò)Handler
來(lái)執(zhí)行的,我們可以定位到H
這個(gè)內(nèi)部類(lèi)幕随,它是一個(gè)Handler的子類(lèi)蚁滋,在handleMessage
中包含著四大組件的生命周期的消息處理。
其中的這段代碼就是代表Activity啟動(dòng)的開(kāi)始赘淮,handleLaunchActivity
是其中最重要的方法辕录。
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
定位到handleLaunchActivity
方法
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
...
//創(chuàng)建WSM
WindowManagerGlobal.initialize();
//在這里創(chuàng)建了Activity對(duì)象,執(zhí)行onCreate和onStart方法
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;
//執(zhí)行onResume方法梢卸,View的工作流程方法
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
if (!r.activity.mFinished && r.startsNotResumed) {
performPauseActivityIfNeeded(r, reason);
if (r.isPreHoneycomb()) {
r.state = oldState;
}
}
} else {
// If there was an error, for any reason, tell the activity manager to stop us.
try {
ActivityManager.getService()
.finishActivity(r.token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
...
}
我們看看handleResumeActivity
這個(gè)方法走诞,這里首先會(huì)根據(jù)token來(lái)取得要處理的Activity,然后performResumeActivity
這個(gè)方法才是真正執(zhí)行onResume
的方法蛤高,注意在onResume
執(zhí)行的時(shí)候蚣旱,View還沒(méi)繪制完成,這時(shí)候是拿不到View的寬高的戴陡,更不要說(shuō)在onCreate
和onStart
了塞绿。decor一開(kāi)始會(huì)被設(shè)置為不可見(jiàn),所以在onResume
之前猜欺,界面對(duì)用戶都是不可見(jiàn)的位隶。從wm.addView(decor, l)
這段代碼開(kāi)始才是真正的繪制過(guò)程,并且在r.activity.makeVisible();
這里開(kāi)始才把View設(shè)置為可見(jiàn)
final void handleResumeActivity(IBinder token,boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
...
// 在這個(gè)方法里面執(zhí)行onResume的回調(diào)开皿,實(shí)際上這時(shí)還沒(méi)開(kāi)始執(zhí)行視圖的測(cè)量,所以解釋了為什么沒(méi)辦法在onResume中獲取view的寬高
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();
//這個(gè)view是DecorView篮昧,在Activity的attach方法中創(chuàng)建PhoneWindow時(shí)創(chuàng)建的赋荆。
View decor = r.window.getDecorView();
//decor一開(kāi)始會(huì)被設(shè)置為不可見(jiàn)
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) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
//把Decorview添加到WindowManager中
wm.addView(decor, l);
} else {
a.onWindowAttributesChanged(l);
}
}
}
}
...
if (r.activity.mVisibleFromClient) {
//在這里把DecorView設(shè)置為可見(jiàn),也就是界面可見(jiàn)
r.activity.makeVisible();
}
...
}
上面代碼的WindowManager
是Activity在attach()
方法中創(chuàng)建的懊昨。ViewManager是一個(gè)接口窄潭,WindowManager是繼承于ViewManager的一個(gè)接口,WindowManager的實(shí)現(xiàn)類(lèi)是WindowManagerImpl酵颁,所以上圖代碼中的addView調(diào)用的實(shí)際是WindowManagerImpl
里面的方法
WindowManagerImpl
在WindowManagerImpl中找到了addView方法的實(shí)現(xiàn)嫉你,但是這邊也很簡(jiǎn)單月帝,它把a(bǔ)ddView的操作委托給了WindowManagerGlobal的addView方法
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
這幾個(gè)類(lèi)的關(guān)系可以用UML圖表示為
WindowManagerGlobal
在這個(gè)方法中執(zhí)行添加View的是ViewRootImpl,首先先創(chuàng)建了ViewRootImpl對(duì)象幽污,然后把view嚷辅、創(chuàng)建的ViewRootImpl和布局參數(shù)保存在三個(gè)數(shù)組當(dāng)中,然后將添加工作托管給了ViewRootImpl
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
//調(diào)整布局參數(shù)等
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
// If there's no parent, then hardware acceleration for this view is
// set from the application's hardware acceleration setting.
final Context context = view.getContext();
if (context != null
&& (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
//同一個(gè)view不能在WindowManager中添加兩次
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
...
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
//保存布局參數(shù)和view距误,在更新視圖時(shí)用到
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
//真正完成視圖工作流程的方法
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
上面的流程可以大概的總結(jié)成
- 父窗口對(duì)子窗口的布局參數(shù)進(jìn)行調(diào)整
- 檢查View的添加次數(shù)簸搞,同一個(gè)View不能在WindowManager中添加兩次
- 創(chuàng)建ViewRootImpl,將View准潭、ViewRootImpl趁俊、布局參數(shù)保存在三個(gè)數(shù)組中,以供之后的查詢之需
- 調(diào)用ViewRootImpl.setView()函數(shù)刑然,將控件交給ViewRootImpl進(jìn)行托管寺擂。
ViewRootImpl
ViewRootImpl
是一個(gè)非常重要的類(lèi),實(shí)現(xiàn)了ViewParent接口泼掠,作為整個(gè)控件樹(shù)的根部怔软,負(fù)責(zé)與WMS進(jìn)行直接的通訊,負(fù)責(zé)管理Surface武鲁,負(fù)責(zé)觸發(fā)控件的測(cè)量與布局爽雄,負(fù)責(zé)觸發(fā)控件的繪制,同時(shí)也是輸入事件的中轉(zhuǎn)站沐鼠,我們平時(shí)處理的事件分發(fā)也是通過(guò)這個(gè)類(lèi)的中轉(zhuǎn)處理之后才來(lái)到Activity和各層級(jí)的View的挚瘟。ViewRootImpl如此的重要我們可以看看它的構(gòu)造方法里面做了什么事情
public ViewRootImpl(Context context, Display display) {
mContext = context;
//從WindowManagerGlobal拿到IWindowSession,它是ViewRootImpl和WMS通信的代理
mWindowSession = WindowManagerGlobal.getWindowSession();
mDisplay = display;
mBasePackageName = context.getBasePackageName();
//把當(dāng)前線程保存起來(lái)饲梭,即UI線程乘盖,在繪制時(shí)會(huì)對(duì)發(fā)起的thread和mThread進(jìn)行比較,不一致時(shí)會(huì)拋出異常
mThread = Thread.currentThread();
mLocation = new WindowLeaked(null);
mLocation.fillInStackTrace();
mWidth = -1;
mHeight = -1;
mDirty = new Rect();
mTempRect = new Rect();
mVisRect = new Rect();
mWinFrame = new Rect();
mWindow = new W(this);
mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
mViewVisibility = View.GONE;
mTransparentRegion = new Region();
mPreviousTransparentRegion = new Region();
mFirst = true; // true for the first time the view is added
mAdded = false;
//創(chuàng)建AttachInfo對(duì)象憔涉,里面保存了Handler订框,window等
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
context);
mAccessibilityManager = AccessibilityManager.getInstance(context);
mAccessibilityManager.addAccessibilityStateChangeListener(
mAccessibilityInteractionConnectionManager, mHandler);
mHighContrastTextManager = new HighContrastTextManager();
mAccessibilityManager.addHighTextContrastStateChangeListener(
mHighContrastTextManager, mHandler);
mViewConfiguration = ViewConfiguration.get(context);
mDensity = context.getResources().getDisplayMetrics().densityDpi;
mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
mFallbackEventHandler = new PhoneFallbackEventHandler(context);
//Choreographer是一個(gè)依附于當(dāng)前線程的信號(hào)同步類(lèi),用于通過(guò)VSYNC特性進(jìn)行界面繪制和刷新兜叨,界面的三大流程就是著他的回調(diào)事件里面進(jìn)行的
mChoreographer = Choreographer.getInstance();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
if (!sCompatibilityDone) {
sAlwaysAssignFocus = true;
sCompatibilityDone = true;
}
loadSystemProperties();
}
我們定位到setView
方法穿扳,其中刷新的方法是requestLayout
,它是ViewParent
接口的方法国旷,用于刷新整個(gè)視圖樹(shù)矛物,當(dāng)視圖有變動(dòng)時(shí)都會(huì)通過(guò)這個(gè)方法來(lái)通知跟布局刷新
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
requestLayout();
...
}
繼續(xù)看requestLayout
方法,這里先進(jìn)行UI線程檢查跪但,然后開(kāi)始計(jì)劃視圖樹(shù)的遍歷工作
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
//ui線程檢查履羞,
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
mThread
就是ViewRootImpl
初始化時(shí)保存起來(lái)的當(dāng)前線程,它檢查的并不是當(dāng)前線程是否是UI線程,而是當(dāng)前線程是否是操作線程忆首。這個(gè)操作線程就是創(chuàng)建ViewRootImpl
對(duì)象的線程爱榔,其實(shí)這里可以看出來(lái)操作不是一定子線程不能操作UI,只要?jiǎng)?chuàng)建和執(zhí)行在同一個(gè)線程就是可以的
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
可以試試這段代碼在activity執(zhí)行會(huì)不會(huì)報(bào)錯(cuò)
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
MyDialog dialog = new MyDialog(MainActivity.this);
dialog.show();
Looper.loop();
}
}).start();
然后看scheduleTraversals
方法糙及,它通過(guò)Choreographer
的回調(diào)來(lái)通知執(zhí)行的時(shí)機(jī)详幽。為什么要這樣做呢?
首先來(lái)理解一下丁鹉,圖形界面的繪制妒潭,大概是有CPU準(zhǔn)備數(shù)據(jù),然后通過(guò)驅(qū)動(dòng)層把數(shù)據(jù)交給GPU來(lái)進(jìn)行繪制揣钦。圖形API不允許CPU和GPU直接通信雳灾,所以就有了圖形驅(qū)動(dòng)(Graphics Driver)來(lái)進(jìn)行聯(lián)系。Graphics Driver維護(hù)了一個(gè)序列(Display List)冯凹,CPU不斷把需要顯示的數(shù)據(jù)放進(jìn)去谎亩,GPU不斷取出來(lái)進(jìn)行顯示。
其中Choreographer
起調(diào)度的作用宇姚。統(tǒng)一繪制圖像到Vsync
的某個(gè)時(shí)間點(diǎn)匈庭。這個(gè)就是VSYNC
(垂直同步)的作用,我們都知道界面刷新速度每秒60幀以上時(shí)就不會(huì)感受到界面的卡頓浑劳,我們就可以理解為當(dāng)VSYNC信號(hào)間隔是16毫秒時(shí)阱持,我們就不會(huì)覺(jué)得卡頓了。
最后執(zhí)行定位到了performTraversals
方法魔熏,這個(gè)方法就是View里面onMeasure衷咽、onLayout、onDraw
方法的起點(diǎn)蒜绽。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//Choreographer的回調(diào)镶骗,里面執(zhí)行界面的繪制
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
performTraversals方法非常長(zhǎng),這里下面是最簡(jiǎn)化后的工作流程躲雅,按順序調(diào)用了performMeasure鼎姊、performLayout、performDraw方法相赁。而這三個(gè)方法由直接或者間距的調(diào)用了onMeasure相寇、onLayout、onDraw方法钮科。
private void performTraversals() {
//定義預(yù)測(cè)量想要的寬高裆赵,對(duì)寬高進(jìn)行賦值,這兩個(gè)變量將是生成MeasureSpec參數(shù)SPEC_SIZE候選
int desiredWindowWidth;
int desiredWindowHeight;
...
//執(zhí)行RunQueue的任務(wù)跺嗽,平時(shí)我們通過(guò)`view.post`發(fā)送的任務(wù)就是在這里被執(zhí)行的。通過(guò)attachInfo里面的handler將Runnable對(duì)象發(fā)送到主線程執(zhí)行
getRunQueue().executeActions(attachInfo.mHandler);
...
//1. 執(zhí)行測(cè)量工作
if (mApplyInsetsRequested) {
mApplyInsetsRequested = false;
mLastOverscanRequested = mAttachInfo.mOverscanRequested;
dispatchApplyInsets(host);
if (mLayoutRequested) {
//執(zhí)行performMeasure()對(duì)視圖樹(shù)進(jìn)行測(cè)量
windowSizeMayChange |= measureHierarchy(host, lp,
mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight);
}
}
...
//2. 執(zhí)行布局
performLayout(lp, mWidth, mHeight);
...
//3. 執(zhí)行視圖繪制
performDraw();
...
}
measureHierarchy的方法執(zhí)行如下
private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
int childWidthMeasureSpec;
int childHeightMeasureSpec;
boolean windowSizeMayChange = false;
if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(mTag,
"Measuring " + host + " in display " + desiredWindowWidth
+ "x" + desiredWindowHeight + "...");
boolean goodMeasure = false;
//對(duì)于父控件是WRAP_CONTENT時(shí),這里指的是浮動(dòng)窗口桨嫁,要進(jìn)行測(cè)量參數(shù)的協(xié)商
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
// On large screens, we don't want to allow dialogs to just
// stretch to fill the entire width of the screen to display
// one line of text. First try doing the layout at a smaller
// size to see if it will fit.
final DisplayMetrics packageMetrics = res.getDisplayMetrics();
res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
int baseSize = 0;
if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
baseSize = (int)mTmpValue.getDimension(packageMetrics);
}
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": baseSize=" + baseSize
+ ", desiredWindowWidth=" + desiredWindowWidth);
if (baseSize != 0 && desiredWindowWidth > baseSize) {
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
//第一次測(cè)量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight()
+ ") from width spec: " + MeasureSpec.toString(childWidthMeasureSpec)
+ " and height spec: " + MeasureSpec.toString(childHeightMeasureSpec));
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
goodMeasure = true;
} else {
// Didn't fit in that size... try expanding a bit.
baseSize = (baseSize+desiredWindowWidth)/2;
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": next baseSize="
+ baseSize);
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
//第二次測(cè)量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
if (DEBUG_DIALOG) Log.v(mTag, "Good!");
goodMeasure = true;
}
}
}
}
if (!goodMeasure) {
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
//第三次測(cè)量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
//view的測(cè)量尺寸和窗口尺寸不一致時(shí)告訴外面植兰,窗口尺寸有可能變化
if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
windowSizeMayChange = true;
}
}
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals -- after measure");
host.debug();
}
//窗口尺寸是否可能需要發(fā)生變化
return windowSizeMayChange;
}
上面的代碼中首先會(huì)判斷測(cè)量的View的寬度是否為WRAP_CONTENT,這種一般是懸浮窗口璃吧,如果是則可能會(huì)比普通的窗口多兩次performMeasure
楣导,上層的View通過(guò)MeasureSpec指導(dǎo)子View的測(cè)量,我們平時(shí)在onMeasure(int widthMeasureSpec,int heightMeasureSpec)
就是從這邊開(kāi)始往下傳遞的畜挨。它和子控件自身期望的尺寸工具決定了子控件最終的測(cè)量結(jié)果筒繁。我們具體看看getRootMeasureSpec計(jì)算結(jié)果
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
// Window can't resize. Force root view to be windowSize.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
// Window can resize. Set max size for root view.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
// Window wants to be an exact size. Force root view to be that size.
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
上面的方法決定了DecorView測(cè)量參數(shù),可以得知
- ViewGroup.LayoutParams.MATCH_PARENT巴元,表示測(cè)量參數(shù)的大小就是窗口尺寸的大小毡咏,測(cè)量模式為MeasureSpec.EXACTLY。
- ViewGroup.LayoutParams.WRAP_CONTENT逮刨,表示子控件可以是它所期望的尺寸呕缭,但是不得大于窗口尺寸。
- 默認(rèn)是固定大小修己,表示子控件的尺寸就是它設(shè)置的尺寸大小
我們?cè)偻路治稣{(diào)用流程
此時(shí)測(cè)量工作已經(jīng)來(lái)到了View層級(jí)了恢总。performMeasure將measureHierarchy給予的widthSpec與heightSpec交給DecorView。而DecorView就是布局的頂級(jí)View睬愤,它是一個(gè)ViewGroup片仿,實(shí)現(xiàn)了FrameLayout布局。從這里開(kāi)始就會(huì)遍歷視圖樹(shù)中的所有View的onMeasure
方法尤辱。至此來(lái)到了我們熟悉的onMeasure流程
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
measure方法中沒(méi)有任何進(jìn)行測(cè)量的代碼砂豌,只是調(diào)用了onMeasure
方法,這里面還做對(duì)onMeasur
的正確使用做了檢查,當(dāng)沒(méi)有setMeasuredDimension
時(shí)會(huì)拋出異常。
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
...
if (forceLayout || needsLayout) {
// first clears the measured dimension flag
mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
resolveRtlPropertiesIfNeeded();
int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
if (cacheIndex < 0 || sIgnoreMeasureCache) {
// measure ourselves, this should set the measured dimension flag back
//調(diào)用onMeasure
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
} else {
long value = mMeasureCache.valueAt(cacheIndex);
// Casting a long to int drops the high 32 bits, no mask needed
setMeasuredDimensionRaw((int) (value >> 32), (int) value);
mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
// flag not set, setMeasuredDimension() was not invoked, we raise
// an exception to warn the developer
//當(dāng)開(kāi)發(fā)者沒(méi)有調(diào)用setMeasuredDimension時(shí)會(huì)拋出異常
if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
throw new IllegalStateException("View with id " + getId() + ": "
+ getClass().getName() + "#onMeasure() did not set the"
+ " measured dimension by calling"
+ " setMeasuredDimension()");
}
mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
}
...
}
performLayout將會(huì)決定所有View四個(gè)頂點(diǎn)的位置葵姥,host.layout
將會(huì)執(zhí)行DecorView的布局流程俊犯,getMeasuredWidth和getMeasuredHeight是上一步measure得到的結(jié)果
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,int desiredWindowHeight) {
final View host = mView;
...
try {
//decorView的布局
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
}
...
}
host.layout
將調(diào)用View里面的layout方法
public void layout(int l, int t, int r, int b) {
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
//保存原始坐標(biāo)
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
//將l, t, r, b設(shè)置給mLeft, mTop, mBottom, mRight
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
//從DecorView開(kāi)始調(diào)用控件樹(shù)的onLayout方法
onLayout(changed, l, t, r, b);
if (shouldDrawRoundScrollbar()) {
if(mRoundScrollbarRenderer == null) {
mRoundScrollbarRenderer = new RoundScrollbarRenderer(this);
}
} else {
mRoundScrollbarRenderer = null;
}
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLayoutChangeListeners != null) {
ArrayList<OnLayoutChangeListener> listenersCopy =
(ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
int numListeners = listenersCopy.size();
//通知監(jiān)聽(tīng)了布局變化的地方
for (int i = 0; i < numListeners; ++i) {
listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
}
}
}
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
if ((mPrivateFlags3 & PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT) != 0) {
mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
notifyEnterOrExitForAutoFillIfNeeded(true);
}
}
從上面可知layout做了如下三件事
- 通過(guò)setFrame給View設(shè)置四個(gè)角度坐標(biāo)
- 調(diào)用onLayout,使控件樹(shù)開(kāi)始執(zhí)行布局操作
- 通知設(shè)置了
View.addOnLayoutChangeListener()
的地方
經(jīng)過(guò)測(cè)量和布局之后工腋,每個(gè)View已經(jīng)知道自己都大小和位置了,最后我們來(lái)看看最終的繪制方法performDraw。
private void performDraw() {
...
try {
//調(diào)用draw方法進(jìn)行實(shí)際的繪制
draw(fullRedrawNeeded);
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
...
}
performDraw
方法很簡(jiǎn)單只是調(diào)用draw方法進(jìn)行實(shí)際的繪制蓄拣,我們繼續(xù)看看
private void draw(boolean fullRedrawNeeded) {
...
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
//當(dāng)滿足以下條件時(shí)將進(jìn)行硬件繪制
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
...
//硬件加速繪制
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
...
//軟件繪制
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
}
}
}
draw方法中會(huì)產(chǎn)生軟件繪制和硬件加速繪制兩個(gè)分支,我們看drawSoftware
方法
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
final Canvas canvas;
try {
...
//創(chuàng)建canvas
canvas = mSurface.lockCanvas(dirty);
if (left != dirty.left || top != dirty.top || right != dirty.right
|| bottom != dirty.bottom) {
attachInfo.mIgnoreDirtyState = true;
}
// TODO: Do this in native
canvas.setDensity(mDensity);
}
...
try {
//進(jìn)行canvas進(jìn)行平移
canvas.translate(-xoff, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
attachInfo.mSetIgnoreDirtyState = false;
//調(diào)用DecorView的draw方法努隙,對(duì)控件樹(shù)進(jìn)行遍歷繪制
mView.draw(canvas);
drawAccessibilityFocusedDrawableIfNeeded(canvas);
} finally {
if (!attachInfo.mSetIgnoreDirtyState) {
// Only clear the flag if it was not set during the mView.draw() call
attachInfo.mIgnoreDirtyState = false;
}
}
...
try {
//最后將繪制的內(nèi)容顯示出來(lái)
surface.unlockCanvasAndPost(canvas);
} catch (IllegalArgumentException e) {
Log.e(mTag, "Could not unlock surface", e);
mLayoutRequested = true; // ask wm for a new surface next time.
//noinspection ReturnInsideFinallyBlock
return false;
}
}
drawSoftware方法主要進(jìn)行了如下四步操作
- 創(chuàng)建canvas
- 進(jìn)行canvas進(jìn)行平移
- 調(diào)用DecorView的draw方法球恤,對(duì)控件樹(shù)進(jìn)行遍歷繪制
- 將繪制的內(nèi)容顯示出來(lái)
到這里View工作流程的onDraw就執(zhí)行完了,回到handleResumeActivity的代碼中荸镊,通過(guò)調(diào)用r.activity.makeVisible();
把Activity的內(nèi)容顯示出來(lái)
Activity
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
從上面的調(diào)用過(guò)程我們把View的繪制和Activity的生命周期聯(lián)系起來(lái)了咽斧,對(duì)于理解整個(gè)系統(tǒng)的運(yùn)作有更深的理解堪置,知道對(duì)于UI線程得知是一個(gè)相對(duì)的概念,vsync垂直同步的機(jī)制等张惹。View的工作流程是一個(gè)非常復(fù)雜的過(guò)程舀锨,里面的每一個(gè)點(diǎn)都值得深入的分析,這里只是理清了三大流程的調(diào)用過(guò)程宛逗。
- Activit啟動(dòng)時(shí)View的繪制過(guò)程是從生命周期的onResume開(kāi)始的
- Activity從onResume開(kāi)始才是對(duì)用戶可見(jiàn)的
- 在onResume或者onCreate中沒(méi)辦法直接獲得View的寬高坎匿,因?yàn)檫@時(shí)測(cè)量還沒(méi)完成