亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android那兩個你碰不到但是很重要的類之ViewRootImpl

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

前言

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

1.ViewRootImpl哪來的?

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

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

  • Activity setcontentView()內(nèi)部調(diào)用了getWindow().setContentView(layoutResID);也就是調(diào)用了Window的setContentView方法,Android里Window的唯一實(shí)現(xiàn)類就是PhoneWindow,PhoneWindow setContentView,初始化DecorView和把我們設(shè)置的View作為其子類。
  • 目光轉(zhuǎn)移到ActivityThread 沒錯是我們提及的另外一個主角,先關(guān)注他的handleResumeActivity()方法,里面關(guā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)類WindowManageImpl的addView方法里調(diào)用了mGlobal.updateViewLayout(view, params);
  • 最后我們在WindowManagerGlobal的addView方法里找到了
public void addView(){
    root = new ViewRootImpl(view.getContext(), display);
    view.setLayoutParams(wparams);
    mViews.add(view);
    mRoots.add(root);
    mParams.add(wparams);
}

小結(jié)

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

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

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

scheduleTraversals()方法發(fā)起渲染動作。交給Choreographer安排真正執(zhí)行的時間關(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.");
    }
}
  • 我們對View的操作,比如給TextView設(shè)置text,最終都會觸發(fā)ViewRootImpl的requestLayout() 方法,該方法有如上的一個check邏輯。這就是我們常說的不能在子線程中更新View。
  • 其實(shí)子線程中可以執(zhí)行View的操作,但是有個前提是:View還未掛載時。 View未掛載時時不會觸發(fā)requestLayout的,還只是一個普普通通的java對象。那掛載邏輯在哪?

4 View 掛載

  • 在ViewRootImpl的performTraversals() 里有這個代碼
private void performTraversals(){
    host.dispatchAttachedToWindow(mAttachInfo, 0);//此處的host為ViewGroup
}
  • ViewGroup的dispatchAttachedToWindo()方法會把AttachInfo對象分配每一個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)吹草動就會把事件傳遞到大bossViewRootImpl這里了。

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

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()的代碼,可見如果已經(jīng)實(shí)現(xiàn)掛載的View,會直接把post進(jìn)來的消息交給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會在View掛載的時候執(zhí)行,也就是dispatchAttachedToWindow()方法里執(zhí)行。

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

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

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

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

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

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

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

總結(jié)

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

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

相關(guān)文章

最新評論