Android的Window和WindowManager

       在日常开发中直接接触Window的机会并不多,但是比如我们需要在界面显示一个类似悬浮窗的东西时,这种效果就需要用到Window来实现。Window实际上是一个抽象类,他的具体实现是PhoneWindow,位于WindowManagerService中。创建Window是通过WindowManager实现的,WindowManager实际上是外界访问Window的入口。而WindowManagerService和WindowManager的交互实际上是一个IPC(跨进程通信Inter-Process-Communication)过程。Android中不管Activity、Toast、Dialog等所有视图都是通过Window呈现的,也就是附加在Window上。所以Window其实是View的直接管理者。那前面博文讲的Activity设置视图的方法setContentView在底层其实也是通过Window来完成的。Window的功能更多是底层的支持,所以开发并不常用。

1.   Window和WindowManager

      要明白Window的工作机制,不如先从如何使用Windowmanager添加一个Window说起。

      Eg:Button  button  =  new Button (this);

              LayoutParams  layoutParams  =  new  WindowManager.LayoutParames(……);

              layoutParames.flags =

              LayoutParames.FLAG_NOT_TOUCH_MODAL|LayoutParames.FLAG_NOT_FOUCESABLE|                                                              LayoutParames.FLAG_SHOW_WHEN_LOCKED;

              layoutParames.gravity = Gravity.LEFT|Gravity.TOP;

              layoutParames.x = 100;

               layoutParames.y = 300;

              mWindowManager.addView ( button , layoutParams );

       上述代码使把一个Buton添加到屏幕座标未(100,300)的位置上。其中WindowManager.LayoutParames的type和flags两个参数比较重要。Flags控制Window的显示特性。type参数表示Window的类型,Window一共三种类型:应用Window,子Window和系统Window。应用Window对应一个Activity,子Window只能附属在特定的父Window中,比如Dialog就是一个子Window。而Toast就是一个系统Window。同时Window是分层的,层级大区的覆盖在层级小的Window上面,所以可见系统Window的层级大于子Window,子Window的层级大于应用Window。这些层级对应的就是上面提到的type参数。如果type使用系统层级,一定要记得在AndroidManifest.xml中声明权限。

<uses-permissionandroid:name= “android.permission.SYSTEM_ALERT_WINDOW”/>.

        WindowManager提供的方法很简单,常用的也就三个:添加View,删除View和更新View。这三个方法定义在ViewManager中,WindowManager继承了ViewManager。

2.  Window的内部机制

        准确来讲Window并不是实际存在的,他依托于View的形式存在,View才是Window存在的实体,两者通过ViewRootImpl建立联系。  View的绘制过程由ViewRootImpl的setView方法完成的.实际使用中使无法直接访问Window的,对Window的访问必须是通过WindowManager才能完成

        Window的添加过程:

        Window的添加通过WindowManager的addView实现。WindowManager实际是一个接口,他的实现是WindowManagerImpl,WindowManagerImpl又将Window的三大操作交给了WindowManagerGlobal。WindowManagerGlobal中维护的列表中有几个比较重要的:

mView:所有Window对应的View;

mParams:所有Window所对应的布局参数;

mDyingViews:正在被删除的View对象;

      Window的删除过程:

     和添加过程一样,底层都是先通过WindowManagerImpl后,在经由WindowManagerGlobal来实现的。在WindowManager中提供了removeView和removeViewImmediate两个删除接口,分别表示异步删除和同步删除。使用后者可能会发生意外的错误,一般不需要使用。

      Window的更新过程:

      通过WindowManagerGlobal中的updateViewLayout完成。该方法做的比较简单,先是用新的LayoutParams替换掉ViewRootImpl中旧的LaouyParams,在ViewRootImpl中通过scheduleTraversals对View重新布局。最终是由WindowManagerervice的relayoutWindow()来具体实现,所以可见又是一个IPC过程。

3.  Window的创建过程

       View虽然使Android中视图的呈现方式,但又不能独立存在,必须依附于Window这个抽象的概念上面。所以有视图的地方就一定有Window。Android中的Activity,Toast,Dialog,Menu其实都对应着一个Window。

       先简述一下Activity中Window的创建过程:Activity启动过程很复杂,但最终会由ActivityThread中的performLaunchActivity()来完成启动过程,这个方法中会启用类加载器创建Activity的实例对象,并调用attach方法为Activity关联运行过程中将依赖的一系列上下文环境变量。attach方法中系统会创建其所属的Window并设置回调接口,Window对象的创建由PolicyManager的makeNewWindow实现,这是一个工厂方法。

        Window mWindow = PolicyManager.makeNewWindow(this);

        那再看看Activity的视图使怎么依附于Window上的。Activity中setContentView的源码如下:

 public void setContentView(intlayoutResID){

       getWindow().setContentView(layoutResID);

       initWindowDecorActionBar();

}

       所以可以看出Activity将视图的具体实现交给了Window,Window的具体实现是PhoneWindow。所以视图的创建是在PhoneWinow中的setContentView()中完成的。DecorView是Activity中的顶级View,首先会创建一个DecorView,不过是一个空的FrameLayout,然后PhoneWindow加载布局文件到这个FrameLayout。因为Activity实现了Window的Callback接口,所以这时就回到Activity的onContentChanged方法通知Activity视图已经发生变化。经过上面三个步骤,Activity的DecoeView已经被创建并初始化完毕。前面已经说了Activity的attach方法中Window已经被创建了,但DecorView还没被添加到Window中,所以无法接受外界输入。在ActivityThread的handleResumeActivity方法中,首先调用Activity的Resume方法,接着调用Activity的makeVisible(),在这个方法中真正完成了DecorView的添加和显示的过程。这是Activity的视图才能被用户看到。

        Toast的Window相对复杂一点,因为他是系统Window,显然涉及到IPC。Toast涉及两类IPC过程:第一类是NotificationManagerService(NMS),第二类是NotificationManagerService回调Toast的TN接口,这个接口运行在Binder线程池中,他提供了show和cancle两个方法将Toast的视图添加到Window中,完成Toast的显示和隐藏。NMS中的enqueueToast方法首先将Toast请求封装成ToastRecord对象并添加到队列mToastQueue中。之后NMS通过内置方法,由ToastRecord的callback回调完成。这个回调就是前面提到的TN对象的远程Binder,为了切换到Toast请求所在的线程,在他们的内部使用了Handler。同理,Dialog的Window的创建过程和Activity是类似的。介绍到这里很清楚了,就是任何View都是附属在一个Window上面的。

发布了29 篇原创文章 · 获赞 4 · 访问量 1万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章