[1].View#invalidate作了什麼,爲何會觸發View的重繪?
[2].View是如何被添加到ViewGroup中的?
[3].ViewGroup和ViewRootImpl在invalidate孩子上作了什麼?
[4].源碼的層次上分析invalidate和postInvalidate的差別與聯繫?
複製代碼
View#invalidate
方法---->[View#invalidate]--------------------
public void invalidate() {
invalidate(true);
}
---->[View#invalidate(boolean)]--------------------
* @hide
*/
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
---->[View#invalidateInternal]--------------------
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
if (mGhostView != null) {
mGhostView.invalidate(true);
return;
}
...
final AttachInfo ai = mAttachInfo;
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
///調用父控件的invalidateChild()刷新本View
p.invalidateChild(this, damage);
}
...
}
}
|--- 到這裏暫停一下
這mGhostView真跟鬼同樣,View中出現了8次,竟沒有一次對它賦值
因爲是包訪問的可能在其餘類裏吧,這裏注意一下,畢竟若是他不爲空,就畫他而後return了
複製代碼
整個View中並無ViewGroup的身影,而是依靠接口[ViewParent]全權負責
這有一個問題:ViewParent的實現類是誰? 明面有一個ViewGroup的實現, 但別忘了幕後還有個大佬ViewRootImpl也是實現了ViewParent的,那這個p究竟是誰呢?git
|--能夠看到p是承接mParent的局部變量,全文搜索[mParent =]來查看他什麼時候初始化或被賦值的
---->[View#成員變量]-----------------------------
//The parent this view is attached to. -- 該View添加到的父View
protected ViewParent mParent; //注意是protected的訪問權限
---->[View#assignParent]-----------------------------
|-- 這裏可見assignParent是初始化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");
}
}
|-- 搜索了一下assignParent在View中並未被調用,那隻能說是別人調的
|-- 和View認老爸關係最密切的當屬ViewGroup中的addView了,來看一下
---->[ViewGroup#addView(View)]-----------------------------
public void addView(View child) {
--->addView(child, -1);
}
---->[ViewGroup#addView(View,int)]-----------------------------
public void addView(View child, int index) {
...
LayoutParams params = child.getLayoutParams();
if (params == null) {
params = generateDefaultLayoutParams();
...
}
--->addView(child, index, params);
}
---->[ViewGroup#addView(View,int,LayoutParams)]-----------------------------
public void addView(View child, int index, LayoutParams params) {
...
requestLayout();
invalidate(true);
--->addViewInner(child, index, params, false);
}
---->[ViewGroup#addViewInner]-----------------------------
private void addViewInner(View child, int index, LayoutParams params,
boolean preventRequestLayout) {
...
--->addInArray(child, index);//將孩子加入到本身的數組裏
// tell our children -- 告訴咱們的孩子們,他們有爹了
if (preventRequestLayout) {
---> child.assignParent(this);// 即是咱們要尋的
} else {
---> child.mParent = this; //這直接讓孩子的mParent賦值
}
...
}
|-- 如今再看一下ViewRootImpl,我就單刀直入了,從setView開始,不懂的,看前面幾篇相關內容
---->[ViewRootImpl#setView]-------------------------
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
... view.assignParent(this);
}
|-- 此時的View前幾篇分析過是DecorView,其中調用了DecorView的assignParent,
因此DecorView是認ViewRootImpl爲老爹的,雖然ViewRootImpl不是View,但它倒是是個ViewParent
因此當爹是沒問題的,那麼View的invalidate方法走的是ViewGroup仍是ViewRootImpl的invalidateChild?
答:若是是一個ViewGroup,它添加了子View,該子View的爹就是ViewGroup,
走的固然也是ViewGroup#invalidateChild,這是咱們平常開發中最多見的
但對於最頂層的DecorView,誰敢當他爹?ViewRootImpl就是他老爸,因此對於DecorView的invalidate方法
固然走的是ViewRootImpl#invalidateChild,因此這就是爲何ViewRootImpl爲何那麼厲害的緣由
換句話來講,協天子以令諸侯有沒有。ViewRootImpl說大家不要在子線程給我刷新UI,View們就乖乖照作
複製代碼
ViewGroup#invalidateChild
方法---->[ViewGroup#invalidateChild]--------------------
|--- ViewGroup做爲ViewParent的實現類, invalidateChild方法咱們看到了
public final void invalidateChild(View child, final Rect dirty) {
...
ViewParent parent = this;
if (attachInfo != null) {
...
}
do {
View view = null;
if (parent instanceof View) {
view = (View) parent;
}
...
//循環找到根view,並調用invalidateChildInParent()方法
parent = parent.invalidateChildInParent(location, dirty);
if (view != null) {
...
}
} while (parent != null);
}
}
|-- 這裏經過 while 來遍歷 this ,都執行了一個invalidateChildInParent的方法
該方法返回了一個ViewParent對象,來看一下這個方法:
---->[ViewGroup#invalidateChildInParent]--------------------
public ViewParent invalidateChildInParent(final int[] location, final Rect dir
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {
...
return mParent;
}
return null;
}
|-- 這方法看起來只是設置了一下本身的區域和擺位,並無什麼實質性的東西
|-- 不過亮點是他的返回值mParent,也就是它把本身整理一下,把老爸跑出去了
|-- 這樣看來上面的invalidateChild就是一直拋老爸,直到DecorView
|-- 由於DecorView 的老爸是ViewRootImpl,因此[parent instanceof View]的條件不知足
|-- 這時候就調用了ViewRootImpl#invalidateChild(ViewGroup全程打醬油的既視感...)
複製代碼
ViewRootImpl#invalidateChild
方法ViewGroup並不給力,並無觸發孩子繪製方法,ViewRootImpl大佬出場,一招定乾坤github
---->[ViewRootImpl#invalidateChild]--------------------
@Override
public void invalidateChild(View child, Rect dirty) {
invalidateChildInParent(null, dirty);
}
---->[ViewRootImpl#invalidateChildInParent]--------------------
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();//劃重點...這裏檢查線程。曹操說:子線程不能更新UI
if (dirty == null) {
invalidate();
return null;
} else if (dirty.isEmpty() && !mIsAnimating) {
return null;
}
...
--->invalidateRectOnScreen(dirty);
return null;
}
---->[ViewRootImpl#invalidateRectOnScreen]--------------------
private void invalidateRectOnScreen(Rect dirty) {
...
if (!mWillDrawSoon && (intersected || mIsAnimating)) {
---> scheduleTraversals();//開啓了一個遍歷的計劃
}
}
---->[ViewRootImpl#scheduleTraversals]--------------------
|---Choreographer 翻譯一下:舞蹈指導者?--大佬真會起名字...
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
---> Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
--------下一段不想看能夠跳過,主要追了一下傳入的Runnable是何時被執行的-------
|---Choreographer的postCallback核心調用的是下面的這個方法:
|--- 主要看入參Runnable的去向,下面的action即是Runnable
---->[Choreographer##postCallbackDelayedInternal]--------------------
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
...
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
//這裏對action作了處理
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
public void addCallbackLocked(long dueTime, Object action, Object token) {
CallbackRecord callback = obtainCallbackLocked(dueTime, action, token);
...
}
private CallbackRecord obtainCallbackLocked(long dueTime, Object action, Object token) {
CallbackRecord callback = mCallbackPool;
if (callback == null) {
callback = new CallbackRecord();
} else {
mCallbackPool = callback.next;
callback.next = null;
}
callback.dueTime = dueTime;
--->callback.action = action;
callback.token = token;
return callback;
}
|-- 可見action流轉到了CallbackRecord的action字段中了
---->[Choreographer#CallbackRecord]------------------------------------------
|-- 可見CallbackRecord的run方法觸發了action的run
private static final class CallbackRecord {
public CallbackRecord next;
public long dueTime;
public Object action; // Runnable or FrameCallback
public Object token;
public void run(long frameTimeNanos) {
if (token == FRAME_CALLBACK_TOKEN) {
((FrameCallback)action).doFrame(frameTimeNanos);
} else {
((Runnable)action).run();
}
}
}
|-- 全局搜了一下,代碼就不貼了,最後[c.run(frameTimeNanos)]在[doCallbacks]方法中觸發
|-- 而[doCallbacks]在[doFrame]觸發,[doFrame]在handler接收[MSG_DO_FRAME]時觸發
---------------------------------------------------------------------------------------
|--言歸正傳:mTraversalRunnable是一個Runnable,經過Choreographer#postCallback最終會被執行
|-- 看一下mTraversalRunnable是什麼,幹了啥
---->[Choreographer#CallbackRecord]--------------------------------------
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
|-- 簡單易懂,執行doTraversal()操做,至於doTraversal()是幹嗎的...
|-- 簡單講一下,doTraversal()操做遍歷全部節點,進行測量、佈局、繪製--(這曹操當得也不容易啊)
|-- 一樣的分析我不想寫第二遍,詳見:所得與所見:[-View周邊-] 框架層#三#4
複製代碼
到這裏總算解開我:invalidate怎樣觸發View重繪的謎題了。編程
---->[View#postInvalidate]-----------------------
public void postInvalidate() {
postInvalidateDelayed(0);
}
---->[View#postInvalidateDelayed]-----------------------
public void postInvalidateDelayed(long delayMilliseconds) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds);
}
}
|-- 感受挺直爽,直接拿ViewRootImpl#dispatchInvalidateDelayed
---->[ViewRootImpl#dispatchInvalidateDelayed]-----------------------
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
|-- Handler經過obtainMessage將view放在一個MSG_INVALIDATE標識的Message中
|-- 若是Handler不熟悉的,還請移駕:Android點將臺:烽火狼煙[-Handler-]
|-- 看Handler,首先不是看它的handleMessage是怎麼處理的,而是看Handler在哪一個線程建立的
|-- 也就是Handler的Looper是在哪一個線程。
---->[ViewRootImpl#mHandler]-----------------------
|-- 並無在子線程,加上ViewRootImpl是在主線程被建立的(不知道到的看前文),因此mHandler是主線程
final ViewRootHandler mHandler = new ViewRootHandler();
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_INVALIDATE:
((View) msg.obj).invalidate();//調用了View的invalidate,已切至主線程
break;
|-- 到這裏就到頭了,也就是說谷歌的大佬怕咱們在子線程invalidate煩神
|-- 就內置的了一個Handler幫咱們省去麻煩,至於用invalidate仍是postInvalidate?
一條直線能到家,你還非要拐個彎嗎?畢竟postInvalidate也是觸發了View#invalidate
還要額外發個消息才能玩。因此主線程用invalidate,在子線程能夠用postInvalidate
固然你以爲postInvalidate太長很差看,能夠也無視大佬的一片好心,本身新建Handler,只要你開心...
複製代碼
總的看來,View的invalidate方法也並無我相信中的那麼複雜,半天就寫完了...數組
項目源碼 | 日期 | 附錄 |
---|---|---|
V0.1-- | 2018-2-23 | 無 |
發佈名:
invalidate方法知多少[-View-] 源碼級
捷文連接:juejin.im/post/5c70ce…bash
筆名 | 微信 | |
---|---|---|
張風捷特烈 | 1981462002 | zdl1994328 |
個人github:github.com/toly1994328
個人簡書:www.jianshu.com/u/e4e52c116…
個人掘金:juejin.im/user/5b42c0…
我的網站:www.toly1994.com微信
1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大編程愛好者共同交流
3----我的能力有限,若有不正之處歡迎你們批評指證,一定虛心改正
4----看到這裏,我在此感謝你的喜歡與支持框架