大家好缆八!我 胡漢三 又回來啦胞谭!由于之前的一段時間公司的項(xiàng)目比較忙碌恨溜,再加上我又全身心的投入到了 PMP 考證隊(duì)伍中符衔,導(dǎo)致博客停更了好久找前。
不得不說考取 PMP證書,真的需要付出很大的精力判族;首先是因?yàn)樾奶坼X躺盛,因?yàn)榭家淮尉偷酶冻?a href="" target="_blank">3900大洋,著實(shí)心疼靶伟铩槽惫!幸好付出和收獲成了正比,順利的拿到了 5A沃缘,結(jié)束了考試之旅躯枢。
好了廢話說完了,我們接下來就來上干貨啦槐臀!
View的繪制機(jī)制,究竟是怎么回事氓仲?
源碼八!源碼敬扛,你能否一股腦的鉆進(jìn)我的腦子拔鳌!這樣我就不用翻你千百遍啥箭,一回頭你還你我還我了=_=!!
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
...
//初始化WindowManageService 即初始化WMS
WindowManagerGlobal.initialize();
//創(chuàng)建Activity谍珊,并執(zhí)行onCreate生命周期方法
Activity a = performLaunchActivity(r, customIntent);
if (a != null) { //證明Activity被正常啟動
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) {
performPauseActivityIfNeeded(r, reason);
...
}
} else {
//若執(zhí)行出錯告訴ActivityManager,結(jié)束當(dāng)前Activity
try {
ActivityManager.getService()
.finishActivity(r.token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
小結(jié)
- 初始化
WindowManagerService
- 生成
Activity
,并執(zhí)行Activity$onCreate(...)
- 若
Activity
被正常啟動 則 執(zhí)行Activity$onResume(...)
- 若
Activity
未能正常啟動 則 結(jié)束當(dāng)前Activity
即通過AMS
執(zhí)行finishActivity(...)
分塊
- [ 1 ]
ActivityThread $performLaunchActivity(...)
是整個Activity
創(chuàng)建的入口- [ 2 ] 在
ActivityThread $handleResumeActivity(...)
方法執(zhí)行過程中傳遞給View
類中常量mParent的屬性值類型
[ 1 ] ActivityThread $performLaunchActivity(...)
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
//通過類加載的方式創(chuàng)建Activity
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
...
}
try {
...
// [1.1] 通過Instrumentation調(diào)取Activity的onCretae
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
...
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
...
}
return activity;
}
[ 1.1 ] Instrumentation$callActivityOnCreate
public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
//至此進(jìn)入到`Activity$onCreate(...)`
activity.performCreate(icicle);
postPerformCreate(activity);
}
至此進(jìn)入到Activity$onCreate(...)
并在onCreate(...)
中調(diào)用setContentView(int layoutResID)
進(jìn)行View的繪制鹏秋,至此我們才開始正式流程:
[ 1.2 ] Activity$setContentView(int layoutResID)
public void setContentView(@LayoutRes int layoutResID) {
//[1.3] 獲取Window對象横朋,調(diào)取其setContentView設(shè)置視圖
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
[ 1.3 ] PhoneWindow$setContentView(int layoutResID)
PhoneWindow.java 由于
Window
是一個抽象類,而PhoneWindow
是它唯一兒子压汪,所謂 父債子還 嘛 !所以找它沒錯的
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
//安裝DecorView
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
...
} else {
//解析Layout XML文件
mLayoutInflater.inflate(layoutResID, mContentParent);
}
//請求視圖更新
mContentParent.requestApplyInsets();
...
}
小結(jié)
- 若
mContentParent
為Null
,表示當(dāng)前視圖無盛放的容器,即需要安裝頂級視圖DecorView
- 解析XML布局文件
- 請求視圖更新操作
[ 1.4 ] View$requestApplyInsets()
/**
* The parent this view is attached to.
* {@hide}
*
* @see #getParent()
*/
protected ViewParent mParent;
//為mParent變量賦值
void assignParent(ViewParent parent) {
if (mParent == null) {
mParent = parent;
} else if (parent == null) {
mParent = null;
} else {
throw new RuntimeException("view " + this + " being added, but"
+ " it already has a parent");
}
}
/*
* 這是重點(diǎn):
* 判定mParent是否為Null
* 不為Null時,調(diào)取mParent的requestFitSystemWindows()
*/
@Deprecated
public void requestFitSystemWindows() {
if (mParent != null) {
mParent.requestFitSystemWindows();
}
}
/**
* 在PhoneWindow中調(diào)取的此方法,進(jìn)行視圖更新
* 此方法無任何操作,而是直接調(diào)取了requestFitSystemWindows()
*/
public void requestApplyInsets() {
requestFitSystemWindows();
}
小結(jié)
- 在
PhoneWindow
中調(diào)取了View$requestApplyInsets()
,由上述代碼可知在此方法中沒有做任何操作求妹,而是直接調(diào)取了View$requestFitSystemWindows()
- 而在
View$requestFitSystemWindows()
方法中它判定了mParent
在不為Null
的情況下,調(diào)取了mParent$requestFitSystemWindows()
- 我們發(fā)現(xiàn)
assignParent(ViewParent parent)
是為mParent
賦值的地方法竞,而mParent
的類型為ViewParent
岔霸,但可惜的是ViewParent
是一個接口俯渤,并沒有實(shí)現(xiàn)代碼
public interface ViewParent {}
那這事就郁悶了,無法確定mParent
真實(shí)的類型八匠,那么就無法找到 ViewParent$requestFitSystemWindows()
真實(shí)實(shí)現(xiàn)的地方趴酣。那么我們就得再次回歸源碼了,繼續(xù) Read The Fucking Source Code . . . .
[ 2 ] ActivityThread$handleResumeActivity(...)
Activity
被正常創(chuàng)建粗來了,也走完了Activity
的onCreate
生命周期岖寞,那么接下來就該走onResume
生命周期了仗谆,我來看看在這里我們能不能找到想要的答案~
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();
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) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
//重點(diǎn)來啦:這里將DecorView添加到WindowManager中
wm.addView(decor, l);
} else {
a.onWindowAttributesChanged(l);
}
}
} else if (!willBeVisible) {
if (localLOGV) Slog.v(
TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
...
} else {
...
}
}
[ 2.1 ]WinodwManagerImpl$addView(...)
WindowManager
是一個接口,而他的實(shí)現(xiàn)類是WindowManagerImpl
捷绒,所以我們就 單刀直入啦!
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
//不做過多論述贯要,調(diào)取WindowManagerGlobal$addView(...)
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
[ 2.2 ] WindowManagerGlobal$addView(...)
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {
....
//創(chuàng)建ViewRootImpl對象
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
//重點(diǎn):
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
...
}
}
}
[ 2.3 ] ViewRootImpl$setView(...)
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
...
//我們要的就是它,原來是在這里為 View中mParent賦值的椭住,
//為什么是 this 呢崇渗? 難道。京郑。宅广。
view.assignParent(this);
...
}
}
}
看看的類描述
public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {...}
原來如此,
ViewRootImpl
原來也是ViewParent
接口的實(shí)現(xiàn)類之一啊~
那么至此我們就搞清楚View
中mParent
的具體類型了些举,就是ViewRootImpl
類型跟狱!
是時候解答疑惑了,讓我回過頭看看[ 1.3 ]中的代碼
/*
* 這是重點(diǎn):
* 判定mParent是否為Null
* 不為Null時户魏,調(diào)取mParent的requestFitSystemWindows()
*/
@Deprecated
public void requestFitSystemWindows() {
if (mParent != null) {
mParent.requestFitSystemWindows();
}
}
我們已經(jīng)確認(rèn)當(dāng)前的mParent
是類型了驶臊,那么我們直接去ViewRootImpl
中尋找這個方法
[ 1.4 ] ViewRootImpl$requestFitSystemWindows()
@Override
public void requestFitSystemWindows() {
//檢查線程
checkThread();
mApplyInsetsRequested = true;
//開始遍歷view
scheduleTraversals();
}
void scheduleTraversals() {
//當(dāng)繪制任務(wù)執(zhí)行期間,不允許有其他繪制任務(wù)執(zhí)行
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//向Looper的消息隊(duì)列發(fā)布一個同步障礙
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//重點(diǎn)來了: post一個消息叼丑,執(zhí)行mTraversalRunnable中任務(wù)
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
[ 1.5 ] mTraversalRunnable
//它實(shí)現(xiàn)了Runnable接口
final class TraversalRunnable implements Runnable {
@Override
public void run() {
//執(zhí)行遍歷任務(wù)
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
//執(zhí)行遍歷任務(wù)
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
//重點(diǎn)中的最后一擊
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
當(dāng)我們看到performTraversals()
关翎,我們看到了黎明的曙光。鸠信。纵寝。沒錯就是它~~
[ 1.6 ] performTraversals()
private void performTraversals() {
...
//測量
performMeasure();
...
//布局
performLayout();
...
//繪制
perfromDraw
...
}
執(zhí)行完此方法后,我們就完成了對View
的 測量 ==> 布局 ==> 繪制 星立,也就完成了我們的整個流程的剖析K睢葬凳!
........................................................................................................................................................................................................................................................................................................................................................................................................
問題
- 經(jīng)過上述文章論述,我們知道了
DecorView.mParent
是ViewRootImpl類型
室奏,那么DecorView
兒子的mParent
是誰呢火焰?它兒子的兒子的mParent
又是誰呢?總不能都是ViewRootImpl類型
吧G戏堋荐健!
答案肯定是否定的。
mParent
的類型永遠(yuǎn)同當(dāng)前View
的父級一個類型琳袄。
來源碼走一圈江场,讓我們見真知!窖逗!
public void addView(View child, int index, LayoutParams params) {
...
//設(shè)置新LayoutParams時址否,addViewInner()將調(diào)用child.requestLayout()
//因此,我們之前調(diào)用requestLayout()在我們自己碎紊,以便孩子的請求
requestLayout();
invalidate(true);
//添加內(nèi)部View
addViewInner(child, index, params, false);
}
private void addViewInner(View child, int index, LayoutParams params,boolean preventRequestLayout) {
...
// tell our children
if (preventRequestLayout) {
child.assignParent(this);
} else {
child.mParent = this;
}
...
}
通過以上的分析佑附,我們表述已經(jīng)很簡明了,
父View
將自己賦予了子View
的mParent
那么我可以清晰的總結(jié)一下:
DecorView
的mParent
的類型是ViewRootImpl
其他View
的mParent
的類型是Ta的父View
仗考,即 Ta的老子R敉! 哈哈