1. Window简介
Window表示窗口概念,在桌面显示类似悬浮窗效果,Android中所有的视图都是通过Window来呈现的
WindowManager是外界访问Window的入口,Window的具体实现位于WindowManagerService中,WindowManager和WindowManagerService的交互是一个IPC过程。
2. Window&windowManager
2.1 Window&PhoneWindow
- Window是一个抽象类,提供了各种窗口操作的方法,比如设置背景标题ContentView等等
- PhoneWindow则是Window的唯一实现类,它里面实现了各种添加背景主题ContentView的方法,内部通过DecorView来添加顶级视图
- 每一个Activity上面都有一个Window,可以通过getWindow获取
2.2 Window&View
每个Window都对应一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系。Window并不可见,它实际以View的形式存在,它是View的直接管理者
2.3 Window&WindowManager:
实际使用中无法访问Window,对Window的访问必须通过WindowManager,对Window的操作通过它完成。
实例:Windowmanager添加window:(将一个button添加到屏幕(100,300)坐标上)
开启权限:<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
WindowManager的三个参数:
1.flags:表示Window的属性。主要的可选值含义:
- FLAG_NOT_FOCUSABLE:表示Window不需要获取焦点,也不需要接收各种输入事件,此标记会同时启动FLAG_NOT_TOUCH_MODEL,最终事件会传递给下层的具有焦点的Window。
- FLAG_NOT_TOUCH_MODAL:表示系统会将当前Window区域以外的单击事件传递给底层的Window,而区域以内的单击事件则自己处理。一般都需要开启此标记,否则其他Window将无法收到单击事件。
- FLAG_SHOW_WHEN_LOCKED:表示Window可显示在锁屏界面。
2.type:表示Window的类型。Window有三种类型:
- Applicationwindows:
层级取值在 FIRST_APPLICATION_WINDOW 和 LAST_APPLICATION_WINDOW 之间(0-99)
是通常的、顶层的应用程序窗口。必须将 token 设置成 activity 的 token 。 - Sub_windows:
层级取值在 FIRST_SUB_WINDOW 和 LAST_SUB_WINDOW 之间(1000-1999)
与顶层窗口相关联,token 必须设置为它所附着的宿主窗口的 token。 - Systemwindows:
层级取值在 FIRST_SYSTEM_WINDOW 和 LAST_SYSTEM_WINDOW 之间(2000-2999)
用于特定的系统功能。它不能用于应用程序,使用时需要特殊权限。
如果想要Window位于所有Window的最顶层,那么采用较大的层级即可。系统Window的层级是最大的,系统层级有很多值, 系统层级取值一般选用:TYPE_SYSTEM_OVERLAY
或TYPE_SYSTEM_ERROR
方法:layoutParams.type=LayoutParams. TYPE_SYSTEM_ERROR
同时必须开启权限:“android.permission.SYSTEM_ALERT_WINDOW”
3.gravity:表示Window的位置。默认是屏幕中间。x、y值相对于gravity。
2.4 .WindowManager&WindowManagerService:
Window的具体实现位于WindowManagerService中。WindowManager和WindowManagerService的交互是一个IPC(跨进程通信)过程。
3.Window的内部机制
1.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);//删除过程
}
对window进行操作实际上就是对view进行操作,举例实现window的拖动效果:
floatbutton.setOnTouchListener(this);
然后对onTouch函数进行重写操作
2.每一个window对应着一个view和一个viewRootImpl。Window和view通过viewRootImpl建立联系
WindowManager是一个接口,真正实现是WindowManagerImpl类
public final class WindowManagerImpl implements WindowManager{
@Override
public void addView(View view, ViewGroup.LayoutParams params){
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
@Override
public void updateViewLayout(View view, ViewGroup.LayoutParams params){
mGlobal.updateViewLayout(view, params);
}
@Override
public void removeView(View view){
mGlobal.removeView(view, false);
}
}
该类没有直接实现三大操作,而是交给WindowManagerGlobal处理,后者以工厂的形式向外提供自己的实例:Private final WindowManagerGlobal mGlobal=WindowManagerGlobal.getInstance();
通过WindowManagerGlobal的addView()、updateViewLayout()、removeView()实现WindowManager对Window的添加、删除和修改
- WindowManagerGlobal的addView:
- 检查参数是否合法,如果是子window还需调整布局参数
- 创建ViewRootImpl实例 并将View添加到列表中
- 通过ViewRootImpl的setView—requestLayout—scheduleTraversals—view绘制,来更新界面,最后通过WindowSession–WindowManagerService完成Window的添加过程
- WindowManagerGlobal的removeView()
removeViewLocked—通过ViewRootImpl完成删除操作,其中window提供两种删除接口,RemoteView和RemoveViewImmediate,表示异步删除和同步删除—使用ViewRootImpl的die方法发送请求删除的消息—ViewRootImpl的Handler处理异步删除的消息并调用doDie—doDie中调用真正执行删除逻辑的dispatchDetachedFormWindow,它主要完成:
(1)垃圾回收相关的工作,比如清除数据和消息、移除回调。
(2)通过Session的remove方法删除Window: mWindowSession remove(mWindow), 这是一个IPC过程,最终会调用WindowManagerService的removeWindow方法。
(3)调用View的dispatchDetachedFromWindow方法,在内部会调用View的onDetachedFromWindow(以及onDetachedFromWindowIntermalO。当View从Window中移除时,这个方法就会被调用,可以在这个方法内部做一些资源回收的工作,比如终止动画、停止线程等。
(4)调用WindowManagerGlobal的doRemoveView方法刷新数据,包括mRoots、mParams以及mDyingViews,需要将当前Window所关联的这三类对象从列表中删除。 - WindowManagerGlobal的updateViewL ayout
首先它需要更新View的LayoutParams替换掉老的LayoutParams,接着再更新ViewRootlmpl中的LayoutParams,这一步是通过ViewRootlmpl的setIayoutParams方法来实现的。在ViewRootmpl中会通过scheduleTraversals方法来对View重新布局,包括测量、布局、重绘这三个过程。除了View本身的重绘以外,ViewRootlmpl 还会通过WindowSession 来更新Window 的视图,这个过程最终是由WindowManagerService的relayoutWindow()来具体实现的,它同样是一个IPC过程。
4.Window的创建过程
View必须依附Window才能呈现出来,因此有View的地方必有Window。在Android中可以提供View的地方有Activity、Dialog和Toast
- Activity的Window创建
首先由ActvityThread.performLaunchActivity完成活动的启动工作,接着实现window的创建,然后由setContentView完成布局的加载—交给window的setContentView处理—window的具体实现是phoneWindow,由phoneWindow的setContentWindow完成—1.如果没有DecorView就创建一个,通过generateLayout加载具体的布局文件到DecorView 2.将View添加到DecorView的mContentParent 3.回调Activity的onContentChanged通知Activity视图已经发生改变—Activity的makeVisible方法呈现Activity视图 - Dialog的Window创建过程
Step1:创建WindowDialog。和Activity类似,同样是通过PolicyManager.makeNewWindow()来实现。
Step2:初始化DecorView并将Dialog的视图添加到DecorView中去。和Activity类似,同样是通过Window.setContentView()来实现。
Step3:将DecorView添加到Window中显示。和Activity一样,都是在自身要出现在前台时才会将添加Window。
Dialog.show()方法:完成DecorView的显示。
WindowManager.remoteViewImmediate()方法:当Dialog被dismiss时移除DecorView。
普通的 Dialog有一个特殊之处,那就是必须采用Activity的Context,如果采用Application的Context, 那么就会报错。 - Toast的Window创建过程
①Toast的内部的视图由两种方式指定:
系统默认的样式;通过setView()指定一个自定义View。
②Toast具有定时取消功能,故系统采用Handler做定时处理。
③在Toast内部有两类IPC过程:
Toast访问NotificationManagerService(NMS);
NotificationManagerService回调Toast里的TN接口。
④Toast提供方法show()和cancel()分别用于显示和隐藏Toast。
- Toast的显示和隐藏都需要通过NMS来实现,由于NMS运行在系统进程中,故需通过远程调用的方式来进行显示和隐藏Toast。
- NMS处理Toast的显示和隐藏请求时会跨进程回调TN中的方法,由于TN运行在Binder线程池中,故需通过Handler将其切换到当前线程(发送Toast请求的线程)。
- NMS只是起到了管理Toast队列及其延时的效果,Toast 的显示和隐藏实际是通过TN来实现的。