Android进阶(8)| Window和WindowManag

作者: yzbkaka | 来源:发表于2019-05-19 21:28 被阅读4次
    本节目录

    一.Window和WindowManager

    1.通过WindowManager添加Window的过程

    代码如下:

    mButton = new Button(this);
    mLayoutParams = new WindowManager.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT,0,0,PixelFormat.TRANSPARENT);
    mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_SHOW_WHEN_LOCKED;  //flag参数
    mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
    mLayoutParams.x = 100;  //设置坐标
    mLayoutParams = 300;
    mWindowManager.addView(mButton,mLayoutParams);
    

    flags参数:
    1)FLAG_NOT_FOCUSABLE:表示window不需要获取焦点,也不需要接受各种输入事件,这些事件最终会传递到下面具有焦点的Window。
    2)FLAG_NOT_TOUCH_MODAL:默认开启。系统会将Window区域以外的单击事件传递给底层的Window,而在区域以内的单击事件则自己处理。
    3)FLAG_SHOW_WHEN_LOCKED:可以让Window显示在锁屏上。

    2.WindowManager的功能

    WindowManager常用的功能只有三个方法:添加View、更新View和删除View。这三个方法定义在ViewManager中,而WindowManager继承ViewManager。

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

    二.Window内部机制

    1.基本理解

    Window是一个抽象的概念,每一个Window都对应着一个View和ViewRootImpl,Window和View是通过ViewRootImpl来建立联系的,也就是说Window不是实际存在的,View才是Window的实体,所以在实际使用中无法直接访问Window。

    2.Window的添加过程

    Window的添加过程需要WindowManager的addView()方法来进行添加。WindowManager是一个接口,真正实现是WindowManagerImpl类,其具体实现的代码如下:

    public void addView(View view,ViewGroup.LayoutParams params){
        mGlobal.addView(view,params,mDisplay,mParentWindow);
    }
    
    public void updateView(View view,ViewGroup.LayoutParams params){
        mGlobal.updateView(view,params);
    }
    
    public void removeView(View view){
        mGlobal.removeView(view,false);
    }
    

    在添加view的方法中,主要的操作工作都是WindowManagerGlobal来实现的,它添加view的方法主要分为以下几步:
    (1)检查参数是否合法,如果是子Window那么还需要调整一些布局参数。
    (2)创建ViewManagerImpl并将View添加到列表中。

    private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
    private final ArraySet<View> mDyingViews = new ArraySet<View>();  //正在被删除的view对象
    
    root = new ViewRootImpl(view.getContext(),display);
    view.setLayoutParams(wparams);  //设置view的布局参数
    mViews.add(view);
    mRoots.add(root);
    mParams.add(wparams);
    

    (3)通过ViewRootImpl来更新界面并完成Window的添加过程。这个过程主要是由ViewRootImpl的setView()方法来完成的。

    3.Window的删除过程

    Window的删除过程同样是由WindowManagerImp交由WindowManagerGlobal来实现的,其过程如下:
    (1)首先调用WindowManagerGlobal的removeView(),实现如下:

    public void removeView(View view,boolean immediate){
        if(view == null){
            throe new IllegalArgumentException("view must not be null");
        }
    
        synchronized(mLock){
            int index = findViewLocked(view,true);  //查找待删除的View索引
            View curView = mRoots.get(index).getView();
            removeViewLocked(index,immediate);  //调用来进一步删除
            if(curView == view){
                return;
            }
    
            throw new IllegalStateException("Calling with view" + view + "but the ViewAncestor is attached" + curView);
        }
    }
    

    (2)调用removeViewLocked()方法来进一步删除。
    (3)然后通过ViewRootImpl来发送最后的删除工作的请求。在ViewRootImpl中提供了die()方法,该方法会发送一个请求删除消息后就会立刻返回,其实现如下:

    boolean die(boolean immediate){
        if(immediate && !mIsInTraversal){
            doDie();
            return false;
        }
        if(!mIsDrawing){
            destroyHardwareRenderer();
        }
        else{
            Log.e(Tag...);
        }
        mHandler.sendEmptyMessage(MSG_DIE);
        return true;
    }
    

    (4)真正删除的工作是由dispatchDetachedFromWindow()方法来完成的。该方法主要左两件事:1.垃圾回收相关的工作 2.通过Session的remove方法删除nWindow 3.调用View的dispatchDetachedFromWindow()方法 4.调用WindowManagerGlobal的doRemoveView的方法刷新数据。

    4.Window的更新过程

    更新过程同样WindowManagerGlobal来实现的,其过程如下:
    (1)先调用WindowManagerGlobal的updateViewLayout方法,实现如下:

    public void updateViewLayout(View view,ViewGroup.LayoutParams params){
        if(view == null){
            throw new IllegalArgumentException("view must not be null");
        }
    
        if(!(params instance WindowManager.LayoutParams)){
            throw new IllegalArguementException("Params must be WindowManager.LayoutParams");
        }
    
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        view.setLayoutParams(wparams);
    
        synchronized(mLock){
            int index = findViewLocked(view,true);
            ViewRootImpl root = mRoots.get(index);
            mParams.remove(index);
            root.setLayoutParams(wparams,false);
        }
    }
    

    (2)在ViewRootImpl中通过scheduleTraversals()方法来对View重新布局,包括测量、布局和重绘。

    三.Window的创建过程

    1.Activity的Window创建过程

    创建过程如图所示:


    Activity的Window创建过程

    2.Dialog的Window的创建过程

    Dialog的Window创建过程

    3.Toast的Window创建过程

    注意:
    1)由于Toast具有定时取消的功能,所以系统采用Handler来实现。
    2)在Toast的内部有两类IPC过程,第一类是Toast访问NotificationManagerService(NMS);第二类是NotificationManagerService回调Toast里的TN接口。
    3)Toast属于系统Window,它内部视图由两种方式指定,第一种是系统默认的样式;第二种是通过setView()来指定一个自定义View。
    4)Toast提供了show()和cancel()分别用于显示和隐藏Toast。
    5)Toast的显示和隐藏都需要通过NMS来实现,由于NMS运行在系统进程中,故需通过远程调用的方式来进行显示和隐藏Toast。
    6)NMS处理Toast的显示和隐藏请求时会跨进程回调TN中的方法,由于TN运行在Binder线程池中,故需通过Handler将其切换到当前线程(发送Toast请求的线程)。

    显示/隐藏过程:
    1)首先调用NMS中的enqueueToast()方法。在该方法中会将Toast请求封装为ToastRecord对象并将其添加到一个名为mToastQueue()的队列中。
    2)之后NMS会通过showNextToastLocked()方法来显示当前的Toast,在该方法中Toast的显示是由ToastRecord()的callback来完成的,这个callback实际上就是Toast中的TN对象的远程Binder。
    3)Toast显示之后,NMS还会通过scheduleTimeoutLocked()来发送一个延时消息,这就决定了Toast显示的时长。
    4)Toast的隐藏也是通过ToastRecord()的callback来完成的,其过程也是一次IPC过程。
    5)最后在callback中会调用show()和hide()方法来最终完成Toast的显示和隐藏,并且TN的handleShow会将Toast视图添加到Window中。

    相关文章

      网友评论

        本文标题:Android进阶(8)| Window和WindowManag

        本文链接:https://www.haomeiwen.com/subject/hjtnaqtx.html