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