欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android那兩個(gè)你碰不到但是很重要的類(lèi)之ViewRootImpl

 更新時(shí)間:2023年05月07日 09:37:45   作者:Drummor  
這兩個(gè)類(lèi)就是ActivityThread和ViewRootImpl,之所以說(shuō)碰不到是因?yàn)槲覀儫o(wú)法通過(guò)正常的方式引用這兩個(gè)類(lèi)或者其類(lèi)的對(duì)象,本文就嘗試從幾個(gè)我們經(jīng)常接觸的方面先談?wù)刅iewRootImpl,感興趣的可以參考閱讀下

前言

這兩個(gè)類(lèi)就是ActivityThread和ViewRootImpl,之所以說(shuō)碰不到是因?yàn)槲覀儫o(wú)法通過(guò)正常的方式引用這兩個(gè)類(lèi)或者其類(lèi)的對(duì)象,調(diào)用方法或者直接拿他的屬性。但他們其實(shí)又無(wú)處不在,應(yīng)用開(kāi)發(fā)中很多時(shí)候都和他們息息相關(guān),閱讀他們掌握其內(nèi)部實(shí)現(xiàn)對(duì)我們理解Android運(yùn)行機(jī)理有醍醐灌頂之療效,碼讀百變其義自見(jiàn),常讀常新。本文就嘗試從幾個(gè)我們經(jīng)常接觸的方面先談?wù)刅iewRootImpl。

1.ViewRootImpl哪來(lái)的?

首先是ViewRootImpl,位于android.view包下,從它所處的位置大概能猜到,跟View相關(guān)。其作用一句話(huà)總結(jié),就是連接Window和View的紐帶。

這個(gè)要從我們最熟悉的Activity開(kāi)始,我們知道Activity的設(shè)置布局View是通過(guò)setContentView() 方法這個(gè)方法里面也大有文章,我們簡(jiǎn)單的梳理下。

  • Activity setcontentView()內(nèi)部調(diào)用了getWindow().setContentView(layoutResID);也就是調(diào)用了Window的setContentView方法,Android里Window的唯一實(shí)現(xiàn)類(lèi)就是PhoneWindow,PhoneWindow setContentView,初始化DecorView和把我們?cè)O(shè)置的View作為其子類(lèi)。
  • 目光轉(zhuǎn)移到ActivityThread 沒(méi)錯(cuò)是我們提及的另外一個(gè)主角,先關(guān)注他的handleResumeActivity()方法,里面關(guān)鍵的部門(mén)代碼,
public void handleResumeActivity(){
    r.window = r.activity.getWindow();
    View decor = r.window.getDecorView();
    ViewManager wm = a.getWindowManager();
    ViewManager wm = a.getWindowManager();
    WindowManager.LayoutParams l = r.window.getAttributes();
    wm.addView(decor, l);
}
  • WindowManager的實(shí)現(xiàn)類(lèi)WindowManageImpl的addView方法里調(diào)用了mGlobal.updateViewLayout(view, params);
  • 最后我們?cè)赪indowManagerGlobal的addView方法里找到了
public void addView(){
    root = new ViewRootImpl(view.getContext(), display);
    view.setLayoutParams(wparams);
    mViews.add(view);
    mRoots.add(root);
    mParams.add(wparams);
}

小結(jié)

  • 通過(guò)梳理這個(gè)過(guò)程我們知道,setContenview()其實(shí)只是在Window的下面掛了一個(gè)View鏈,View鏈的根就是ViewRootImpl。
  • Window通過(guò)把View和Activity聯(lián)系在一起。
  • View鏈的真正添加操作最終交給了WindowManagerGlobal執(zhí)行。
  • 補(bǔ)充一點(diǎn):PopupWindow本質(zhì)就是在當(dāng)前Window下掛了一個(gè)View鏈,PopupWindow本身沒(méi)有Window,就如雷鋒塔沒(méi)有雷鋒一樣;Dialog是有自己的window關(guān)于這點(diǎn)可自行查閱源碼考證。

2 ViewRootImpl 一個(gè)View鏈渲染的中轉(zhuǎn)站

View的渲染是自定而上層層向下發(fā)起的,大致經(jīng)歷測(cè)量布局和繪制,View鏈的管理者就是ViewRootImpl。通過(guò)

scheduleTraversals()方法發(fā)起渲染動(dòng)作。交給Choreographer安排真正執(zhí)行的時(shí)間關(guān)于Choreographer不熟悉的可以參考我的其他文章。最終執(zhí)行performTraversals() 方法。

private void performTraversals(){
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    performLayout(lp, mWidth, mHeight);
    performDraw();
}

3 不能在子線程操作View?

ViewRoot的RequestLayout中有這樣一段代碼:

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

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}
  • 我們對(duì)View的操作,比如給TextView設(shè)置text,最終都會(huì)觸發(fā)ViewRootImpl的requestLayout() 方法,該方法有如上的一個(gè)check邏輯。這就是我們常說(shuō)的不能在子線程中更新View。
  • 其實(shí)子線程中可以執(zhí)行View的操作,但是有個(gè)前提是:View還未掛載時(shí)。 View未掛載時(shí)時(shí)不會(huì)觸發(fā)requestLayout的,還只是一個(gè)普普通通的java對(duì)象。那掛載邏輯在哪?

4 View 掛載

  • 在ViewRootImpl的performTraversals() 里有這個(gè)代碼
private void performTraversals(){
    host.dispatchAttachedToWindow(mAttachInfo, 0);//此處的host為ViewGroup
}
  • ViewGroup的dispatchAttachedToWindo()方法會(huì)把AttachInfo對(duì)象分配每一個(gè)View,最終實(shí)現(xiàn)我們所謂的掛載。
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
    for (int i = 0; i < count; i++) {
        final View child = children[i];
        child.dispatchAttachedToWindow(info,
                combineVisibility(visibility, child.getVisibility()));
    }
 
  • 實(shí)現(xiàn)掛載的View有任何風(fēng)吹草動(dòng)就會(huì)把事件傳遞到大bossViewRootImpl這里了。

通過(guò)addView添加進(jìn)的View也是會(huì)收到父View的mAttachInfo這里不展開(kāi)了。

5 View.post()的Runnable最終在哪執(zhí)行了?

public boolean post(Runnable action) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
        return attachInfo.mHandler.post(action);
    }
    getRunQueue().post(action);
    return true;
}
  • 以上是View post()的代碼,可見(jiàn)如果已經(jīng)實(shí)現(xiàn)掛載的View,會(huì)直接把post進(jìn)來(lái)的消息交給Hanlder處理了給執(zhí)行,不然就post了HandlerActionQueue里。
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
  ..
    if (mRunQueue != null) {
        mRunQueue.executeActions(info.mHandler);//內(nèi)部也是調(diào)用handler.post()
        mRunQueue = null;
    }
    ..
}
  • 最終這些Runnable會(huì)在View掛載的時(shí)候執(zhí)行,也就是dispatchAttachedToWindow()方法里執(zhí)行。

6 為什么View.post 可以獲取寬高

  • 這個(gè)是是一個(gè)問(wèn)題延伸,在Activity中直接獲取寬高是獲取不到的,我們通常會(huì)使用view.post一個(gè)Runnable來(lái)獲取。原因就是Activity onCreate時(shí)通過(guò)setContentView只是創(chuàng)建了View而未實(shí)現(xiàn)掛載,掛載是在onResume時(shí),未掛載的View其實(shí)沒(méi)有經(jīng)歷測(cè)量過(guò)程。。

  • 而通過(guò)post的方式,通過(guò)上一小節(jié)知道,未掛載的View上post之后,任務(wù)會(huì)在掛載之后,通過(guò)handler重新post,此時(shí)已經(jīng)ViewRootImpl已經(jīng)執(zhí)行了performTraversals()完成了測(cè)量自然可以得到寬高。

7 還有一點(diǎn)值得注意

ViewRootImpl 不單單是渲染的中轉(zhuǎn)站,還是觸摸事件的中轉(zhuǎn)站。

硬件傳感器接收到觸摸事件經(jīng)過(guò)層層傳遞分發(fā)到應(yīng)用窗口的第一站就是ViewRootImpl。為什么這么說(shuō)?因?yàn)槲矣凶C據(jù)~。這是ViewRoot里的代碼

public void setView(){
    ..
    mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
        Looper.myLooper());
}
  • WindowInputEventReceiver是ViewRootImpl的一個(gè)內(nèi)部類(lèi),其接收到input事件后,就會(huì)進(jìn)行事件分發(fā)。
  • 這里給我們的啟發(fā)是,并不是所有的主線程任務(wù)執(zhí)行都是通過(guò)Handler機(jī)制, onTouch()事件是底層直接回調(diào)過(guò)來(lái)的,這就和我們之前卡頓監(jiān)控說(shuō)的方案里有一項(xiàng)就是對(duì)onTouchEvent的監(jiān)控。

總結(jié)

  • ViewRoot的代碼有一萬(wàn)多行,本文分析的只是冰山一角,里面有大量細(xì)節(jié)直接研究。
  • 通過(guò)ViewRootImpl相關(guān)幾個(gè)點(diǎn),簡(jiǎn)單的做了介紹分析希望對(duì)你有幫助。

以上就是Android那兩個(gè)你碰不到但是很重要的類(lèi)之ViewRootImpl的詳細(xì)內(nèi)容,更多關(guān)于Android ViewRootImpl的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論