zoukankan      html  css  js  c++  java
  • 第八章 理解Window和WindowMannager

    Window是一个抽象类,它的具体实现是PhoneWindow。WindowManager

    是外界访问Window的入口,Window的具体实现位于WindowManagerServi

    ce中,WindowManager和WindowManagerService的交互是一个IPC过

    程。Android中所有的视图都是通过Window来呈现的,不管是Activity、Dial

    og还是Toast,它们的视图实际上都是附加在Window上的,因此Window实

    际是View的直接管理者。


    1. 为了分析Window的工作机制,先通过代码了解如何使用WindowManager添加一个Window,下面一段代码将一个Button添加到屏幕坐标为(100, 300)的位置上
    1. mFloatingButton = new Button(this);
    2. mFloatingButton.setText("test button");
    3. mLayoutParams = new WindowManager.LayoutParams(
    4. LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0,
    5. PixelFormat.TRANSPARENT);//0,0 分别是type和flags参数,在后面分别配置了
    6. mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
    7. | LayoutParams.FLAG_NOT_FOCUSABLE
    8. | LayoutParams.FLAG_SHOW_WHEN_LOCKED;
    9. mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;
    10. mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
    11. mLayoutParams.x = 100;
    12. mLayoutParams.y = 300;
    13. mFloatingButton.setOnTouchListener(this);
    14. mWindowManager.addView(mFloatingButton, mLayoutParams);

    Flags参数表示Window的属性,以下列举常用的选项:

      1. FLAG_NOT_FOCUSABLE:表示Window不需要获取焦点,也不需要接收各种输入事件,此标记会同时启动FLAG_NOT_TOUCH_MODEL,最终事件会传递给下层的具有焦点的Window

      2. FLAG_NOT_TOUCH_MODAL:在此模式下,系统会将当前Window区域以外的单击事件传递给底层的Window,当前Window区域以内的单击事件则自己处理。这个标记很重要,一般来说都需要开启此标记,否则其他Window将无法收到单击事件。

      3. FLAG_SHOW_WHEN_LOCKED:开启此模式可以让显示在锁屏的界面

      4. Type参数表示Window的类型,Window有三种类型,分别是应用Window、子Window和系统Window。应用类Window对应着一个Activity。子Window不能单独存在,它需要附属在特定的父Window之中,比如常见的一些Dialog就是一个子Window。系统Window是需要声明权限才能创建的Window,比如Toast和系统状态栏这些都是系统Window。

      Window是分层的,每个Window都有对应的z-ordered,层级最大的会覆盖在层级小的Window上面,这和HTML中的z-index的概念是完全一致的。在三类Window中,应用Window的层级范围是1~99,子Window的层级范围是1000~1999,系统Window的层级范围是2000~2999,这些层级属性范围对应着WindowManager.LayoutParams的type参数。

      如果采用TYPE_SYSTEM_ERROR,只需要为type参数指定这个层级即可:

      mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR

      同时声明权限。



      8.2 Window的内部机制

      Window是一个抽象的概念,并不是实际存在的,它是以View的形式存在,每一个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系。在实际使用中无法直接访问Window,对Window的访问必须通过WindowManager。


      下面分析一下Window的创建过程:
         
      1. Window的添加过程需要通过WindowManager的addView来实现,WindowManager是一个接口,它的真正实现是WindowManagerImpl类。WindowManager的实现类对于addView、updateView和removeView方法都是委托给WindowManagerGlobal类。

      WindowManagerGlobal的addView方法分为如下几步:

        1. 检查参数是否合法,如果是子Window那么还需要调整一些布局参数
               
        1. 创建ViewRootImpl并将View添加到列表中
           
               注意到源码中有几个比较重要的列表:
        1. private final ArrayList<View> mViews = new ArrayList<View>();
        2. private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
        3. private final ArrayList<WindowMannager.LayoutParams> mParams =
        4. new ArrayList<WindowMannager.LayoutParams>();
        5. private final ArraySet<View> mDyingViews = new ArraySet<View>();
               mViews是所有Window所对应的View,mRoots存储的是所有Window所对应的ViewRootImpl,mParams存储的是所有Window所对应的布局参数
               mDyingViews是准备删除但是未删除的view对象

               在addView中通过如下方式将Window的一系列对象添加到列表中
           
        1. root = new ViewRootImpl(view.getContext(),display);
        2. view.setLayoutParams(wparams);
        3. mViews.add(view);
        4. mRoots.add(root);
        5. mParams.add(wparams);
        1. 通过ViewRootImpl来更新界面并完成Window的添加过程。ViewRootImpl内部通过WindowSession的实现类Session来调用WindowManagerService的方法来实现addWindow


        Window的删除过程:

        和添加过程一样,都是先通过WindowManagerImpl后,再进一步通过WindowManagerGlobal来实现的。

        1. public void removeView(View view,boolean immediate){
        2. .....
        3. synchronized(mLock){
        4. int index = findViewLocked(view,true);
        5. View curView = mRoots.get(index).getView();
        6. removeViewLocked(index,immediate);
        7. ....
        8. }
        9. .....
        10. }
        先找到要删除view的索引,然后调用removeViewLocked进行删除
        其中内部的删除提供了异步和同步的方法,一般只用异步,他的操作很简单,发一个Message给Hanlder然后将此view加入待删队列即可。

         

        真正删除View的逻辑在dispatchDetachedFromWindow方法的内部实现。dispatchDetachedFromWindow方法主要做四件事:

        1. 垃圾回收的工作,    比如清除数据和消息,移除回调。
        2. 通过Session的remove方法删除Window,mWindowSession.remove(mWindow),这同样是一个IP C过程,最终会调用WindowManagerService的removeWindow方法
          调用View的dispatchDetachedFromWindow方法,在内部调用View的onDetachedFromWindow()以及onDetachedFromWindowInternal()。
        3. 调用WindowManagerGlobal的doRemoveView方法刷新数据,包括mRoots、mParams以及mDyingViews,需要将当前Window所关联的这三类对象从列表中删除。




        Window的更新:

        首先需要更新View的LayoutParams并替换掉老的LayoutParams,接着再更新ViewRootImpl中的LayoutParams,这一步是通过ViewRootImpl的setLayoutParams方法来实现的。在ViewRootImpl中会通过scheduleTrversals方法来对View重新布局,包括测量、布局、重绘三个过程。除了View本身的重绘以外,ViewRootImpl还会通过WindowSession来更新Window的视图,这个过程最终是由WindowManagerService的relayoutWindow()来具体实现的,同样是一个IPC过程。

         



        8.3.1 Activity的Window创建过程

        1.Activity的启动过程很复杂,最终会由ActivityThread中的performLaunchActivity来完成整个启动过程,在这个方法内部会通过类加载器创建Activity的实例对象,并调用它的attach方法为其关联运行过程中所依赖的一系列上下文环境变量;

        2.Activity实现了Window的Callback接口,当window接收到外界的状态变化时就会回调Activity的方法,例如onAttachedToWindowonDetachedFromWindowdispatchTouchEvent等;

        3.Activity的Window是由PolicyManager来创建的,它的真正实现是Policy类,它会新建一个PhoneWindow对象,Activity的setContentView的实现是由PhoneWindow来实现的;

        4.Activity的顶级View是DecorView,它本质上是一个FrameLayout。如果没有DecorView,那么PhoneWindow会先创建一个DecorView,然后加载具体的布局文件并将view添加到DecorView的mContentParent中,最后就是回调Activity的onContentChanged通知Activity视图已经发生了变化;

        5.还有一个步骤是让WindowManager能够识别DecorView,在ActivityThread调用handleResumeActivity方法时,首先会调用Activity的onResume方法,然后会调用makeVisible方法,这个方法中DecorView真正地完成了添加和显示过程。


        8.3.2 Dialog的Window创建过程

        Dialog的Window的创建过程和Activity类似,有如下步骤:

        1. 创建Window:Diolog中Window的创建同样是通过PolicyManager的makeNewWindow方法来完成的,创建后的对象实际上就是PhoneWindow。
        2. 初始化DecorView并将Dialog的视图添加到DecorView中
        3. 将DecorView添加到Window中并显示:普通的Dialog有一个特殊之处,就是必须采用Activity的Context,如果采用Application的Context,那么就会报错。应用token只有Activity拥有,所以这里只需要Activity作为Context来显示对话框即可。

        8.3.3 Toast的Window创建过程

        在Toast的内部有两类IPC过程,第一类是Toast访问NotificationManagerService,第二类是NotificationManagerService回调Toast里的TN接口。

        Toast属于系统Window,它内部的视图由两种方式指定:一种是系统默认的演示,另一种是通过setView方法来指定一个自定义的View

        Toast具有定时取消功能,所以系统采用了Handler。Toast的显示和隐藏是IPC过程,都需要NotificationManagerService(NMS)来实现,在Toast和NMS进行IPC过程时,NMS会跨进程回调Toast中的TN类中的方法,TN类是一个Binder类,运行在Binder线程池中,所以需要通过Handler将其切换到当前发送Toast请求所在的线程,所以Toast无法在没有Looper的线程中弹出。

        对于非系统应用来说,mToastQueue最多能同时存在50个ToastRecord,这样做是为了防止DOS(Denial of Service,拒绝服务)。因为如果某个应用弹出太多的Toast会导致其他应用没有机会弹出Toast.




















      1. 相关阅读:
        colock
        ToggleButton 和 Switch
        radioButon的使用
        kotlin中val和var的区别
        textEdit
        c++ 网络编程基础
        网格布局 GridLayout
        数组、指针和引用
        Hello Word
        Win7-U盘安装出现"We were unable to copy your files. "
      2. 原文地址:https://www.cnblogs.com/You0/p/5984477.html
      Copyright © 2011-2022 走看看