八、Window

来源:互联网 发布:蓝韵网络 编辑:程序博客网 时间:2024/05/15 00:06

Window表示一个窗口的概念,直接使用的机会不多,在某些特殊的时候,比如需要在桌面上显示一个类似悬浮窗的东西,那么这种效果就需要Window来实现。
Window是一个抽象类,在手机上面的具体的实现是PhoneWindow。创建一个Window是很简单的事,只需要通过WindowManager即可完成。WindowManager是外界访问Window的入口,Window的具体实现位于WindowManagerService中,WindowManager和WindowManagerService的交互实际上是一个IPC过程。
Android中所有的视图都是通过Window来呈现的,不管是Activity,Dialog还是Toast,他们的视图实际都是附加在Window上的。

1.Window和WindowManager

WindowManager.LayoutParams中的flags和type这两个参数比较重要。
Flags表示Window的几个属性。
FLAG_NOT_FOCUSABLE
表示Window不需要获取焦点,也不需要接收各种输入事件,次标记同时会启用FLAG_NOT_TOUCH_MODAL,最终事件会直接传递给下层的具有焦点的Window。

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

FLAG_SHOW_WHEN_LOCKED
开启此模式,可以让Window显示在锁屏的界面上。

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

Window是分层的,层级大的会覆盖在层级小的Window上面,在三类Window中,应用类Window的层级范围是1-99,子Window的层级范围是1000-1999,系统Window的层级范围是2000-2999,这些层级范围对应着WindowManager.LayoutParams的type参数。

如果想要Window位于所有Window的最顶层,采用较大的层级即可。很显然系统Window的层级是最大的,而且系统层级有很多的值,一般我们选用TYPE_SYSTEM_OVERLAY或者TYPE_SYSTEM_ERROR,如果采用TYPE_SYSTEM_ERROR,只需要为type参数指定这个层级即可:

mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;

同时声明权限:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />  

系统类型的Window是需要检查权限的,不配置权限会报错。

WindowManager所提供的功能很简单,常用的只有三个,即添加View,更新View,和删除View,这三个方法定义在ViewManager中,而WindowManager继承了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);}

例子定义一个随手指移动的Button。

    @Override    public boolean onTouch(View v, MotionEvent event) {        int rawX = (int) event.getRawX();        int rawY = (int) event.getRawY();        switch (event.getAction()) {        case MotionEvent.ACTION_DOWN:            break;        case MotionEvent.ACTION_MOVE:            int x = (int) event.getX();            int y = (int) event.getY();            mLayoutParams.x = rawX;            mLayoutParams.y = rawY;            mWindowManager.updateViewLayout(mFloatingButton, mLayoutParams);            break;        case MotionEvent.ACTION_UP:            break;        default:            break;        }        return false;    }    @Override    protected void onDestroy() {        try {            mWindowManager.removeView(mFloatingButton);        } catch (IllegalArgumentException e) {            e.printStackTrace();        }        super.onDestroy();    }

2.Window的内部机制

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

2.1.Window的添加过程
WindowManager是一个接口,实现类是WindowManagerImpl类。WindowManagerImpl并没有直接实现Window的三大操作,而是全部交给WindowManagerGlobal来处理。
2.1.1.检查参数是否合法,如果是子Window那么还需要调整一些布局参数;
2.1.2.创建ViewRootImpl并将View添加到列表中;
2.1.3.通过ViewRootImpl来更新界面并完成Window的添加过程;

2.2.Window的删除过程
Window的删除过程和添加过程一样,都是先通过WindowManagerImpl后,再进一步通过WindowManagerGlobal来实现的。
最后会调用dispatchDetachedFromWindow方法,真正删除view的逻辑在dispatchDetachedFromWindow方法内部实现。

dispatchDetachedFromWindow方法做的事情:
(1)垃圾回收相关的工作,比如清除数据和消息,移除回调;
(2)通过Session的remove方法移除Window:
mWindowSession.remove(mWindow);
这同样是一个IPC过程,最终会调用WindowManagerService的removeWindow方法;
(3)调用View的dispatchDetachedFromWindow方法,在内部会调用View的onDetachedFromWindow以及onDetachedFromWindowInternal。对于onDetachedFromWindow,这个方法被调用,可以在这个方法内部做一些资源回收的工作,比如终止动画,停止线程等;
(4)调用WindowManagerGlobal的doRemoveView方法刷新数据,包括mRoots、mParams以及mDyingViews,需要将当前Window所关联的这三类对象从列表中删除。

2.3.Window的更新过程
需要看WindowManagerGlobal的updateViewLayout方法。除了View本身的重绘以外,ViewRootImpl还会通过WindowSession来更新Window的视图,这个过程最终是由WindowManagerService的relayoutWindow()来具体实现的,同样是一个IPC过程。

3.Window的创建过程
View是Android中视图的呈现方式,但是View不能单独存在,必须附着在Window这个抽象的概念上面,因此有视图的地方就有Window。
视图如:Activity,Dialog,Toast,除此之外,还有一些依托Window而实现的视图,比如PopupWindow、菜单,他们也是视图,有视图的地方就有Window,因此Activity,Dialog,Toast等视图都对应着一个Window。

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

Activity的视图附属在Window上面,Activity的setContentView方法。
步骤:
3.1.1.如果没有DecorView,创建DecorView是一个FrameLayout,内部一般包含标题栏和内部栏。ID_ANDROID_CONTENT = com.android.internal.R.id.content
3.1.2.将View添加到DecorView的mContentParent中。
3.1.3.回调Activity的onContentChanged方法通知Activity视图已经发生变化。

这个时候DecorView还没有被WindowManager正式添加到Window中,在ActivityThread的handleResumeActivity方法中,首先会调用Act的onResume方法,接着会调用Activity的makeVisible(),正是在makeVisible方法中,DecorView真正地完成了添加和显示这两个过程,到这里Act视图才能被用户看到。

3.2.Dialog的Window创建过程
3.2.1.创建Window
Window对象的创建是PolicyManager的makeNewWindow方法实现的,创建后的对象实际上就是PhoneWindow。
3.2.2.初始化DecorView并将Dialog的视图添加到DecorView中
3.2.3.将DecorView添加到Window中并显示需要使用Activity来作为Context来显示对话框,系统对话框不需要这样,比如可以设置type,dialog.getWindow.setType(LayoutParams.TYPE_SYSTEM_ERROE);
需要SYSTEM_ALERT_WINDOW权限。

3.3.Toast的Window创建过程
Toast也是基于Window来实现的,但是由于Toast具有定时取消这一功能,所以系统采用了Handler。在Toast的内部其实有两两类IPC过程,第一类是Toast访问NotificationManagerService,第二类NMS回调Toast里的TN接口。

Toast属于系统Window,它内部的视图由两种方式指定,一种是系统默认样式,另一种是通过setView方法来指定一个自定义的View,不管如何,它们都对应Toast的一个View类型的内部成员mNextView。

Toast的显示和隐藏过程实际上是通过Toast中的TN这个类来实现的,它有两个方法show和hide,分别对应Toast的显示和隐藏。由于这两个方法是被NMS以跨进程的方式调用的,因此它们运行在Binder线程池中,为了将执行环境切换到Toast请求所在的线程,在它们的内部使用了Handler。

TN中的handleShow中会将Toast的视图添加到Window中,TN中的handleHide中会将Toast的视图从Window中移除。

0 0
原创粉丝点击