ViewRootImpl與ViewGroup凉唐,View關系

本篇解決兩個問題:

  • ViewTree中的繼承關系如何建立的?
  • mAttachInfo是如何分發(fā)的?

1:ViewTree的繼承關系如何構建的甘萧?

在ActivityThread中觸發(fā)了handleResumeActivity():

 public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason){
            ...
            ViewManager wm = a.getWindowManager();
            ...
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                } else {
                    // The activity will get a callback for this {@link LayoutParams} change
                    // earlier. However, at that time the decor will not be set (this is set
                    // in this method), so no action will be taken. This call ensures the
                    // callback occurs with the decor set.
                    a.onWindowAttributesChanged(l);
                }
            }
            ...

}

這里的ViewManager是一個接口间雀,我們知道WindowManager繼承了該接口,同時WindowManager的實現(xiàn)類WindowManagerImpl則將具體實現(xiàn)代理給了WindowManagerGlobal來實現(xiàn)。因此會調用到WindowManagerGlobal的addView():

  public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
      ...
             root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);

            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;
            }
}

ViewRootImpl是WindowManager和View之間的橋梁辕羽。而ViewRootImpl實現(xiàn)了ViewParent接口。ViewGroup也實現(xiàn)了ViewParent接口垄惧。

ViewRootImpl在setView()中調用了View的assignParent()刁愿,將ViewRootImpl傳入了DecorView中,ViewRootImpl也就成了DecorView的父親到逊。而普通view的這個方法铣口,則是在ViewGroup的addView()中調用,這樣每個View的parent就都賦值了觉壶。

    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");
        }
    }

因此我們在Activity的onCreate()中調用:

   final View viewById = findViewById(R.id.tv);
        viewById.post(new Runnable() {
            @Override
            public void run() {
                ViewParent parent = viewById.getParent();
                while (parent!=null){
                    Log.e("parentName:",parent.getClass().getName());
                    parent = parent.getParent();
                }
            }
        });

打印的結果:

E/parentName:: androidx.constraintlayout.widget.ConstraintLayout
E/parentName:: androidx.appcompat.widget.ContentFrameLayout
E/parentName:: androidx.appcompat.widget.FitWindowsLinearLayout
E/parentName:: android.widget.FrameLayout
E/parentName:: android.widget.LinearLayout
E/parentName:: com.android.internal.policy.DecorView
E/parentName:: android.view.ViewRootImpl

2:mAttachInfo是如何分發(fā)的脑题?

在ViewRootImpl的構造方法中就將自己傳入到了View的內部類AttachInfo中。

 mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
                context);

那AttachInfo是什么呢铜靶?注釋中這么說的

 /**
     * {@hide}
     *
     * Not available for general use. If you need help, hang up and then dial one of the following
     * public APIs:
     *
     * @see #isAttachedToWindow() for current attach state
     * @see #onAttachedToWindow() for subclasses performing work when becoming attached
     * @see #onDetachedFromWindow() for subclasses performing work when becoming detached
     * @see OnAttachStateChangeListener for other code performing work on attach/detach
     * @see #getHandler() for posting messages to this view's UI thread/looper
     * @see #getParent() for interacting with the parent chain
     * @see #getWindowToken() for the current window token
     * @see #getRootView() for the view at the root of the attached hierarchy
     * @see #getDisplay() for the Display this view is presented on
     * @see #getRootWindowInsets() for the current insets applied to the whole attached window
     * @see #hasWindowFocus() for whether the attached window is currently focused
     * @see #getWindowVisibility() for checking the visibility of the attached window
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
    AttachInfo mAttachInfo;

我們可以看到很多重要的屬性叔遂,都保存在該類中。那么mAttachInfo是何時賦值的呢?在View中的該方法中進行賦值:

void dispatchAttachedToWindow(AttachInfo info, int visibility){
        mAttachInfo = info;
}

該方法由父ViewGroup來調用

 void dispatchAttachedToWindow(AttachInfo info, int visibility){
 mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
        super.dispatchAttachedToWindow(info, visibility);
        mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;

        final int count = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < count; i++) {
            final View child = children[i];
            child.dispatchAttachedToWindow(info,
                    combineVisibility(visibility, child.getVisibility()));
        }
        final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
        for (int i = 0; i < transientCount; ++i) {
            View view = mTransientViews.get(i);
            view.dispatchAttachedToWindow(info,
                    combineVisibility(visibility, view.getVisibility()));
        }
}

首先將當前ViewGroup的繼承父類View中的mAttachInfo進行了賦值已艰,然后對于每一個ChildView中的mAttachInfo進行了賦值痊末。

在ViewRootImpl.setView()中,會調用requestLayout()哩掺。

    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true凿叠;
            scheduleTraversals();
        }
    }

scheduleTraversals()開始分發(fā)遍歷任務,

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

 final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }

將任務交給了Choreographer來處理嚼吞。該類從GPU渲染出來的數(shù)據(jù)的buffer中取出盒件,交給屏幕展示。

    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }

            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

  private void performTraversals() {
     ...
//  這個host就是DecorView誊薄。而所有的mAttachInfo的分發(fā)也就是從這里開始的履恩。
     host.dispatchAttachedToWindow(mAttachInfo, 0);
    ...
     performMeasure(childWidthMeasureSpec,     childHeightMeasureSpec);
...
            performLayout(lp, mWidth, mHeight);
...
            performDraw();
...

}

我們可以看到注釋的這句

host.dispatchAttachedToWindow(mAttachInfo, 0);

將attachInfo分發(fā)給了DecorView,由此觸發(fā)了所有子ViewGroup的分發(fā)呢蔫。其實這些View拿到的mAttachInfo是同一個切心。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市片吊,隨后出現(xiàn)的幾起案子绽昏,更是在濱河造成了極大的恐慌,老刑警劉巖俏脊,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件全谤,死亡現(xiàn)場離奇詭異,居然都是意外死亡爷贫,警方通過查閱死者的電腦和手機认然,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漫萄,“玉大人卷员,你說我怎么就攤上這事√谖瘢” “怎么了毕骡?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長岩瘦。 經(jīng)常有香客問我未巫,道長,這世上最難降的妖魔是什么启昧? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任叙凡,我火速辦了婚禮,結果婚禮上密末,老公的妹妹穿的比我還像新娘握爷。我一直安慰自己宰啦,他們只是感情好,可當我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布饼拍。 她就那樣靜靜地躺著,像睡著了一般田炭。 火紅的嫁衣襯著肌膚如雪师抄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天教硫,我揣著相機與錄音叨吮,去河邊找鬼。 笑死瞬矩,一個胖子當著我的面吹牛茶鉴,可吹牛的內容都是我干的。 我是一名探鬼主播景用,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼涵叮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了伞插?” 一聲冷哼從身側響起割粮,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎媚污,沒想到半個月后舀瓢,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡耗美,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年京髓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片商架。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡堰怨,死狀恐怖,靈堂內的尸體忽然破棺而出甸私,到底是詐尸還是另有隱情诚些,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布皇型,位于F島的核電站诬烹,受9級特大地震影響,放射性物質發(fā)生泄漏弃鸦。R本人自食惡果不足惜绞吁,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望唬格。 院中可真熱鬧家破,春花似錦颜说、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至烹困,卻和暖如春玄妈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背髓梅。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工拟蜻, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人枯饿。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓酝锅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親奢方。 傳聞我的和親對象是個殘疾皇子搔扁,可洞房花燭夜當晚...
    茶點故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)袱巨,斷路器阁谆,智...
    卡卡羅2017閱讀 134,656評論 18 139
  • Android7.0發(fā)布已經(jīng)有一個多月了,Android7.0在給用戶帶來一些新的特性的同時愉老,也給開發(fā)者帶來了新的...
    東經(jīng)315度閱讀 1,364評論 0 14
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,129評論 25 707
  • 對于這些非人類來說场绿,十五年不過彈指一揮間。好像什么變了嫉入,又好像什么都沒變焰盗。 情殤還未治愈的斑斑,瞪著面前十五年來依...
    那我便再等等吧閱讀 2,184評論 0 1
  • 最近又開始折騰操作系統(tǒng)了咒林,但是申明一下我不是運維工程師熬拒。工作單位給我默認裝的Windows系統(tǒng)用起來慢吞吞的,命令...
    Phoobobo閱讀 339評論 1 0