Android Window、WindowManager、WMS

1.窗口Window

一个app从启动到主窗口显示出来,需要app、AMS、WMS、SurfaceFlinger(SF)等几个模块相互合作。app负责业务逻辑,绘制自己的视图;AMS管理组件、进程信息和Activity的堆栈及状态等;WMS管理Activity对应的窗口、子窗口以及系统窗口等(也就是控制ViewRoot之间的组合排版);SF用于管理图形缓冲区,将app绘制的东西(也就是ViewRoot)合成渲染在屏幕上。

Android中真正展示给用户的并不是Activity,而是Window和View,Activity的作用就是处理一些逻辑问题,比如生命周期管理以及建立窗口。

view不能单独存在,它必须要附着在Window这个抽象概念上。每一个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系,因此有视图的地方就有Window,比如Activity、Dialog、Toast等。说明View是Window存在的实体,Window是实际view的直接管理者。在实际使用中无法直接访问Window,对Window的访问必须通过WindowManager。

Android的窗口分为三种类型:

①系统窗口(System Window):系统设计的,可以单独存在,不依附于任何应用的窗口,比如状态栏(Status Bar)、导航栏(Navigation Bar)、壁纸(Wallpaper)、来电显示窗口(Phone)、锁屏窗口(KeyGuard)、信息提示窗口(Toast)、音量调整窗口、鼠标光标等。

②子窗口(Sub Window):比如应用自定义的对话框或者输入法窗口,子窗口必须依附于某个应用窗口(设置相同的token)。

③应用程序窗口 (Application Window):对应一个Activity。

Window是一个抽象类,它的具体实现是PhoneWindow。创建一个Window需要通过WindowManager来完成,WindowManager是外界访问Window的入口,WindowManager的具体实现位于WindowManagerService中,WindowManager和WindowManagerService的交互是一个IPC过程。

整个Android的窗口机制是基于WindowManager接口的,这个接口可以添加view到屏幕,也可以从屏幕删除view。它面向的对象一端是屏幕,另一端就是View,直接忽略Activity或Dialog(其实Activity和Dialog的底层实现也是通过WindowManager)。WindowManager是全局的,它是显示View的最底层了。因此使用WindowManager可以实现在桌面上可移动的悬浮窗,比如流量统计、桌面歌词等。

 

2.WindowManager

WindowManager主要用来管理Window,它的实现类是WindowManagerImpl,如果想对Window进行添加、删除和更新操作,就可以使用WindowManager,具体工作是由WMS处理的。

Window、WindowManager和WMS:

Window是一个抽象类,具体的实现类为PhoneWindow,它对View进行管理;

WindowManager是一个接口类,继承自接口ViewManager,用来管理Window,它的实现类为WindowManagerImpl;

WindowManger最终会将具体的工作交给WMS来处理,WindowManager和WMS通过Binder来进行跨进程通信。

总的来说就是WindowManger将工作交给WMS来处理,并且对Window进行管理,也就是对View进行管理。

f108ffd2d0324d699f1b7827195a9d3a.webp

Window包含了View并对View进行管理,Window用虚线来表示是因为Window是一个抽象概念,并不是真实存在的,Window的实体其实也是View。WindowManager用来管理Window,而WindowManager所提供的功能最终会由WMS来进行处理。

WindowManager使用方法:

①获取WindowManager对象

WindowManager mWindowManager = (WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);

②设置WindowManager.LayoutParams,主要是type参数,这个参数决定了窗口的类型。比如定义成一个Toast窗口,Toast属于系统窗口,不需要处理父窗口、子窗口之类的事

WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams();

wmParams.type = WindowManager.LayoutParams.TYPE_TOAST;

wmParams.format = PixelFormat.RGBA_8888;

wmParams.width = 800;

wmParams.height = 800;

③添加View到WindowManager

mWindowManager.addView(mView, wmParams);

 

3.Window的内部机制
WindowManager对Window主要有三大操作:添加、更新和删除。这三个方法定义在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);

}

WindowManager也是一个接口,它继承了ViewManager接口:

public interface WindowManager extends ViewManager {

}

WindowManager也继承了这三个方法,而这些方法传入的参数都是View,说明WindowManager具体管理的是Window中的view。WindowManager在继承ViewManager的同时,又加入很多功能,包括Window的类型和层级相关的常量、内部类以及一些方法,其中有两个方法是根据Window的特性加入的:

public Display getDefaultDisplay();

public void removeViewImmediate(View view);

getDefaultDisplay方法会得知这个WindowManager实例将Window添加到哪个屏幕上,换句话说,就是得到WindowManager所管理的屏幕(Display)。removeViewImmediate方法则规定在这个方法返回前要立即执行View.onDetachedFromWindow()来完成传入的View相关的销毁工作。

WindowManager的具体实现类是WindowManagerImpl:

public final class WindowManagerImpl implements WindowManager{

    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

        @Override

        public void addView(View view, ViewGroup.LayoutParams params){

            mGlobal.addView(view, params, mDisplay, mParentWindow);

        }       

        @Override

        public void updateViewLayout(View view, ViewGroup.LayoutParams params){

            mGlobal.updateViewLayout(view, params);

        }       

        @Override

        public void removeView(View view){

            mGlobal.removeView(view, false);

        }

}

WindowManagerImpl并没有直接实现Window的三大操作,而是交给了WindowManagerGlobal。WindowManagerGlobal是一个单例,即在一个进程中只有一个实例。

WindowManagerImpl的这种工作模式是典型的桥接模式,Window为抽象部分,WindowManagerImpl为实现部分,WindowManagerImpl并没有直接实现Window的三大操作,而是全部交给WindowManagerGlobal来处理。

ac4045a776aa420ca7fba7fdd850654f.webp

最终通过WindowManagerGlobal的addView()、updateViewLayout()、removeView()实现了WindowManager对Window的添加、删除和修改。

 

4.Window三大操作

(1)添加过程addView

WindowManagerGlobal.java:

public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {

    ……

    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;

    ViewRootImpl root;

    synchronized (mLock) {

         ……

        // 1.构建ViewRootImpl,作为native层与java层view系统通信的桥梁

        root = new ViewRootImpl( view.getContext(), display);

       //2.给view设置布局参数   

        view.setLayoutParams(wparams);

        //3.存储ViewRootImpl、View以及LayoutParams到对应的列表中

        mViews.add(view);

        mRoots.add(root);

        mParams.add(wparams);

        //4.调用ViewRootImpl的setView方法将view显示到手机窗口中

        root.setView(view, wparams, panelParentView); 

    }

}

ViewRootImpl是一个视图层次结构的顶部,它实现了View与WindowManager之间所需要的协议,作为WindowManagerGlobal中大部分的内部实现。因此在WindowManagerGlobal的实现方法中,都可以见到ViewRootImpl,也就是说WindowManagerGlobal方法最后还是调用到了ViewRootImpl(ViewRootImpl并不是一个View,它是作为native层与java层View系统通信的桥梁)。

接下来看一下ViewRootImpl如何通过setView()方法将视图添加到WindowManager的。

ViewRootImpl.java:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {

    synchronized (this) {

    if (mView == null) {

        mView = view;

        ...

        requestLayout(); //1.请求布局

        res = mWindowSession.addToDisplay( mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel); //2.向WMS发起显示Window的请求

    }

}

先看requestLayout方法:

ViewRootImpl.java:

public void requestLayout() {

    if (!mHandlingLayoutInLayoutRequest) {

        checkThread();

        mLayoutRequested = true;

        scheduleTraversals();//发送DO_TRAVERSAL

    }

}

void scheduleTraversals() {

    if (!mTraversalScheduled) {

        mTraversalScheduled = true;

        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();

        mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

    }

}

就是往handler发送一个DO_TRAVERSAL消息,这个消息会触发整个视图树的绘制操作,也就是最终会执行performTraversals函数,这是一个极为复杂的但又非常重要的函数。

private void performTraversals() {

    ... ...

    //1.获取Surface对象,用于图形绘制

    //2.丈量整个视图树的各个View的大小,performMeasure函数

    //3.布局整个视图树,performLayout函数

    //4.绘制整棵视图树,performDraw函数

}

在performDraw函数中,Framework会获取到图形绘制表面Surface对象,然后获取它的可绘制区域,也就是Canvas对象,然后Framework在这个Canvas对象上绘制,通知SurfaceFlinger更新视图。

其实requestLayout()方法通过Handler发送一个Message,排在所有WMS发送过来的消息之前先布局绘制一次,之后才会处理WMS传来的各种事件,比如触摸事件等。毕竟要首先将各个View的布局、位置处理好才能准确的处理WMS传来的事件。接着通过mWindowSession.addToDisplay真正的添加窗口,虽然requestLayout()执行在前,但是用的是Handler发消息的方式来处理,其Runable一定是在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);

 }

addToDisplay方法中会调用WMS的addWindow方法,并将自身也就是Session作为参数传进去,每个应用程序进程都会对应一个Session,WMS会用ArrayList来保存这些Session。这样

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值