Android 窗口机制

1.熟悉窗口结构图:

2.对 PhoneWindow,DecorView,WindowManager,ViewRootImpl 等关键词理解。

2.1 PhoneWindow

每个 Activity 都有一个 Window 对象的实例,这个实例实际上是 PhoneWindow 类型的,由此看出:PhoneWindow 是 Window 的子类。

知识:每一个Activity都有一个 PhoneWindow 对象。PhoneWindow 中还有管理布局的 WindowManager 和承载布局的 DecorView。

2.2 DecorView

DecorView 是继承了 FrameLayout(帧布局),DecorView 中含有两个子 View 分别是:TitleView 和 ContentView,我们一般是把布局设置在 ContentView。

知识:在 Activity 的 onCreate() 方法中有 setContentView() 方法,这个方法设置布局实际上就是把布局放在 DecorView中;还需要知道:DecorView是视图树的最顶层,作用是承载布局。

2.3 WindowManager

public interface WindowManager extends ViewManager {
    Display getDefaultDisplay();

    void removeViewImmediate(View var1);
    ......//省略
}

可以看出 WindowManager 是继承了 ViewManager (下面有简单介绍 ViewManager),因此也可以对 View 进行添加,更新,删除;而 WindowManagerImpl (下面有简单介绍)继承了 WindowManager (实际是一个接口),也可以说 WindowManager 的实际实现类是 WindowManagerImpl ;在 WindowManagerImpl 里面有一个成员变量 WindowManagerGlobal (下面有简单介绍); WindowManagerGlobal 执行了 addView() 方法才添加管理窗口;所以 WindowManager 类似于代理,并没有真正的管理窗口,而是通过 WindowManagerGlobal 去管理窗口。

2.3.1 ViewManager

​public interface ViewManager{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

从上面代码可以看出,ViewManager 中 有三个方法(添加,更新,移除):add、update、remove ;用这三个方法来操作视图,也就是说 ViewManager 是用来添加和移除 activity 中 View 的接口。

2.3.2 WindowManagerImpl

​​public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Display mDisplay;
    private final Window mParentWindow;

     @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }

    .....//省略

     @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }
}

从上面代码可以看出,WindowManagerImpl 继承了 WindowManager,而在方法里面定义了一个成员变量 mGlobal ,mGlobal 是一个 WindowManagerGlobal 类型的单例对象,它维护了本应用程序内所有Window的DecorView。mGlobal 调用 addView() 方法添加并管理窗口;所以 WindowManager 并没有真正去管理窗口,而是通过 WindowManagerGlobal 管理。

2.3.3 WindowManagerGlobal

​​​public final class WindowManagerGlobal {
    private static final String TAG = "WindowManager";
     public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
            ......//省略。。
            root = new ViewRootImpl(view.getContext(), display);
            ......//省略
            root.setView(view, wparams, panelParentView);
     }
}

从上面代码可以看出,在 WindowManagerGlobal 调用 addView() 方法,在此时 WindowManager 才可以管理 DecorView 。然后还可以看到一个重要的对象 ViewRootImpl (下面会说明)。看完这里再回去看 WindowManager 就会很容易理解了。

知识:WindowManager 是在 Activity执行 attach() 时被创建的,而 attach() 方法是在 onCreate() 之前被调用的。WindowManager 实现了 ViewManager 接口,它对窗口的管理能力实际是通过 WindowManagerGlobal 实现的,WindowManagerGlobal 调用addView() 方法,WindowManager 才可以管理 DecorView ,也可以说 WindowManager 作用是管理布局。

2.4.ViewRootImpl(在Android 2.2 后替换了 ViewRoot)

​​​public final class WindowManagerGlobal {
    private static final String TAG = "WindowManager";
     public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
            ......//省略。。
            root = new ViewRootImpl(view.getContext(), display);
            ......//省略
            root.setView(view, wparams, panelParentView);
     }
}

上面可以看出 ViewRootImpl 是在 WindowManagerGlobal 调用 addView() 方法后,在里面初始化了 ViewRootImpl ,继续看 ViewRootImpl 中的 setView() 方法:

​public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
                ......//省略
                requestLayout();
                ...//省略
                try {
                ...//省略
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
               }
}

从上面代码看出,先是调用 requestLayout() 方法,又调用了 mWindowSession.addToDisPlay() 方法。下面先看 requestLayout() 方法:

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

从上面代码看出,走了 checkThread() 方法和 scheduleTraversals() 方法,先看第一个方法 checkThread() :

void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

从上面代码看出, checkThread() 方法做了一个判断,如果不是在当前线程将抛出异常(在子线程更新UI没使用handler的话就会抛出这个异常);执行完 checkThread() 方法后,走 scheduleTraversals() 方法,下面看这个方法:

void scheduleTraversals() {
        if (!mTraversalScheduled) {
            ......//省略
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }

从上面代码看出,scheduleTraversals() 中会通过 handler 去异步调用 mTraversalRunnable 接口。 Choreographer 走的方法为一个回调(Choreographer相对复杂,所以单独说的,有兴趣了解点这里&)。再往下看:

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

从上面代码看出,走了 doTraversal() 方法,继续看 doTraversal() 方法:

  void doTraversal() {
            ......//省略
            performTraversals();
    }

从上面代码看出,走了 performTraversals() 方法,继续看 performTraversals() 方法:

private void performTraversals() {  
        ......  //省略
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        ......  //省略
        performLayout(lp, desiredWindowWidth, desiredWindowHeight);
        ......  //省略
        performDraw();
        }
        ......  //省略
    }

从上面代码看出,最后走了测量,布局,绘制的方法,最后开始触发测量绘制。经过 measure、layout 和 draw 三个过程才能将一个 View 绘制出来,所以 View 的绘制是 ViewRootImpl 完成的,以上就是 requestLayout() 方法的过程,走完改方法后后面还有一个 mWindowSession.addToDisPlay() 方法,忘记的看一下上面。。。下面是 addToDisPlay() 方法:

@Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }

从上面代码看出,有一个 mService 对象,它就是 WindowManagerService ,mService 执行了 addWindow() 方法;也就是说布局最终是由 WindowManagerService 来添加的。

知识:ViewRootImpl 是在 WindowManagerGlobal 执行了 setView() 的时候才被初始化的,而 ViewRootImpl 执行了 setView() 方法,才将视图通过 WindowManager 添加到窗口上,ViewRootImpl 能够和系统的 WindowManagerService 进行交互,并且管理着 DecorView 的绘制和窗口状态。ViewRootImpl 不只有绘制的功能,还有事件分发的功能。

方便理解,做了一张图片(请忽略拙劣的 PS):

 

更多内容戳我&

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值