《Android 开发艺术探索》笔记——(8)Window 和 WindowManager
2016-05-02 15:16
597 查看
Android 中的 Window 有两种概念,一种是 Window 抽象类,它的具体实现是 PhoneWindow,用来进行操作,比如生成 DecorView,另一种是以 View 为实体的抽象概念,用来显示。
外界访问 Window (显示)要通过 WindowManager,WindowManager 会进一步访问 WindowManagerService(WMS),WMS 再去生成Window(显示),WindowManager 和 WindowManagerService 的交互是一个IPC过程。
WindowManager –> WindowManagerService –> Window
Window 通过 ViewRootImpl 对 View 进行管理。
WindowManager.addView(view, layoutParams);
可以看到,WindowManager 添加 Window 其实是在添加 View,然后它会生成 View 对应的 ViewRootImpl, View 和 ViewRootImpl 一起就代表了 Window(显示)。上面 layoutParams 是 WindowManager.LayoutParams,其中 flags 和 type 两个参数比较重要。
Flags 表示 Window 的属性,三种常用的:
FLAG_NOT_FOCUSABLE
表示 Window 不需要获取焦点,也不需要输入事件。
FLAG_NOT_TOUCH_MODAL
当前 WIndow 区域之外的点击事件会传递给下面的 Window,一般都会开启。
FLAG_SHOW_WHEN_LOCKED
让 Window 显示在锁屏上。
Type 表示 Window 的类型,有三种,每种 Window 都有不同的层级:
应用 Window (Activity) 层级:1-99
子 Window (Dialog) 层级:1000-1999
系统 Window (Toast 和 系统状态栏) 层级:2000-2999
不同层级范围对应着 WindowManager.LayoutParams 的 type 参数,有很多对应的值。若采用系统 Window, 要声明相应权限,如使用TYPE_SYSTEM_ERROR 就要声明
< user-permission android:name=”android.permission.SYSTEM_ALERT_WINDOW” />。
WindowManager 常用的有三个方法,即添加VIew,更新VIew,删除View。它们定义在 ViewManager 中,WindowManager 继承了 ViewManager。
添加、删除和更新都是通过这样的过程来完成的:
WindowManager(接口)→
WindowManagerImpl (类)→
WindowManagerGlobal (工厂)。
过程:
检查参数是否合法,如果是子 Window 那么还需要调整一些布局参数
创建 ViewRootImpl 并将 View 添加到列表中
通过 ViewRootImpl 来更新界面并完成 Window 的添加过程
View 的绘制过程都是由 ViewRootImpl 来完成的,所以这里也由ViewRootImpl 的 setView 方法来完成,setView 内部会通过 requestLayout 来完成异步刷新请求。其中 scheduleTraversals() 就是绘制的入口。
接着会通过 WindowSession 最终来完成 Window 的添加过程。WindowSession 的类型是 IWindowSession,它时一个 Binder 对象,真正的实现是 Session,在 Session 内部会通过 WindowManagerService 来实现 Window (显示)的添加。
进一步的删除在 removeViewLocked() 中完成,代码如下:
root 的 die 方法根据参数 imediate 判断是异步删除还是同步删除。
异步:die 会发一个消息给 ViemRootImpl 的 Handler, Handler 会处理此消息并调用 doDie 方法。
同步:直接调用 doDie 方法。
若是异步,则 die 方法后 view 并没有被删除,view 将被放入 mDyingViews 中等待删除。
updateViewLayout 方法比较简单,先更新 View 的 LayoutParams 并替代老的 LayoutParams,接着通过 ViewRootImpl 的 setLayoutParams 方法来更新 ViewRootImpl 的 LayoutParams。
在 Activity 的 attach() 方法中,系统会通过 PolicyManager.makeNewWindow() 创建 Activity 所属的 Window 对象并为其设置回调接口。
实际中,PolicyManager 的真正实现是 Policy 类,Policy 类中的 makeNewWindow 会返回一个 PhoneWindow 对象。
大致过程如下(==> 为内部,—> 为之后):
performLaunchActivity() ===>
activity = mInstrumentation.newActivity(…) —>
activity.attach() ===>
mWindow = PolicyManager.makeNewWindow(this) ===>
return new PhoneWindow(context);
现在 Window 已经创建完成,下面分析 Activity 的视图是怎样附属在 Window 上的,大致过程如下:
Activity.setContentView() ==>
getWindow.setContentView() ==>
PhoneWindow.setContentView()
PhoneWindow.setContentView() 方法大致有以下步骤:
如果没有 DecorView , 就创建它
将 View 添加到 DecorView 的 mContentParent 中
回调 Activity 的 onContentChanged 方法通知 Activity 视图已经发生变化
创建 Window
初始化 DecorView 并将 Dialog 的视图添加到 DecorView 中
将 DecorView 添加到 WindowManager
Dialog 的 Window 创建过程与 Activity 很类似,几乎没什么区别。当 Dialog 被关闭,它会通过 WindowManager 来移除 DecorView:mWindowManager.removeViewImmediate(mDecor)(同步)。
普通的 Dialog 必须采用 Activity 的 Context,因为需要 Activity 的应用 Token(?)。
Toast 提供了 show 和 cancel 分别用于显示和隐藏 Toast,它们内部是一个 IPC 过程。
Toast 的显示:
INotificationManager service = getService() —>
service.enqueueToast(pkg, tn, mDuration) ===>
将 Toast 请求封装为 ToastRecord 添加到 mToastQueue 队列中 —>
showNextToastLocked() ===>
ToastRecord.callback.show() (显示) —>
scheduleTimeoutLocked(record) (延时)
Toast 的隐藏与显示类似,也是通过 ToastRecord 的 callback 来完成的。
ToastRecord.callback.hide() (隐藏)
callback 实际上是 Toast 中的 TN 对象的远程 Binder,通过 callback 来访问 TN 中的方法是需要跨进程来完成的,最终被调用的 TN 中的方法会运行在发起 Toast 请求的应用的 Binder 线程池中。
所以,Toast 的显示和隐藏过程实际上是通过 Toast 中 TN 这个类来实现的,它有两个方法 show 和 hide,即显示和隐藏。由于这两个方法被 NotificationManagerService 以跨进程的方式调用的,因此它们运行在 Binder 线程池中。为了将执行环境切换到 Toast 请求所在线程,在它们内部使用了 Handler。
外界访问 Window (显示)要通过 WindowManager,WindowManager 会进一步访问 WindowManagerService(WMS),WMS 再去生成Window(显示),WindowManager 和 WindowManagerService 的交互是一个IPC过程。
WindowManager –> WindowManagerService –> Window
Window 通过 ViewRootImpl 对 View 进行管理。
Window 和 WindowManager
以添加 Window 为例展示 Window 和 WIndowManager 的关系:WindowManager.addView(view, layoutParams);
可以看到,WindowManager 添加 Window 其实是在添加 View,然后它会生成 View 对应的 ViewRootImpl, View 和 ViewRootImpl 一起就代表了 Window(显示)。上面 layoutParams 是 WindowManager.LayoutParams,其中 flags 和 type 两个参数比较重要。
Flags 表示 Window 的属性,三种常用的:
FLAG_NOT_FOCUSABLE
表示 Window 不需要获取焦点,也不需要输入事件。
FLAG_NOT_TOUCH_MODAL
当前 WIndow 区域之外的点击事件会传递给下面的 Window,一般都会开启。
FLAG_SHOW_WHEN_LOCKED
让 Window 显示在锁屏上。
Type 表示 Window 的类型,有三种,每种 Window 都有不同的层级:
应用 Window (Activity) 层级:1-99
子 Window (Dialog) 层级:1000-1999
系统 Window (Toast 和 系统状态栏) 层级:2000-2999
不同层级范围对应着 WindowManager.LayoutParams 的 type 参数,有很多对应的值。若采用系统 Window, 要声明相应权限,如使用TYPE_SYSTEM_ERROR 就要声明
< user-permission android:name=”android.permission.SYSTEM_ALERT_WINDOW” />。
WindowManager 常用的有三个方法,即添加VIew,更新VIew,删除View。它们定义在 ViewManager 中,WindowManager 继承了 ViewManager。
Window的内部机制
Window 是一个抽象概念,每一个 Window 都对应着一个 View 和 一个 ViewRootImpl。View 是 Window 的实体,通过 ViewRootImpl 与 Window 建立联系。添加、删除和更新都是通过这样的过程来完成的:
WindowManager(接口)→
WindowManagerImpl (类)→
WindowManagerGlobal (工厂)。
Window 的添加过程
实际上通过 WindowManagerGlobal.addView() 来完成。过程:
检查参数是否合法,如果是子 Window 那么还需要调整一些布局参数
创建 ViewRootImpl 并将 View 添加到列表中
通过 ViewRootImpl 来更新界面并完成 Window 的添加过程
View 的绘制过程都是由 ViewRootImpl 来完成的,所以这里也由ViewRootImpl 的 setView 方法来完成,setView 内部会通过 requestLayout 来完成异步刷新请求。其中 scheduleTraversals() 就是绘制的入口。
public void requestLayout(){ if (!mHandlingLayoutInLayoutRequest){ checkThread(); mLayoutRequested = true; scheduleTraversals(); } }
接着会通过 WindowSession 最终来完成 Window 的添加过程。WindowSession 的类型是 IWindowSession,它时一个 Binder 对象,真正的实现是 Session,在 Session 内部会通过 WindowManagerService 来实现 Window (显示)的添加。
Window 的删除过程
实际上通过 WindowManagerGlobal.removeView() 来完成。public void removeView(View view, boolean immediate) { if (view == null) { throw new IlleagalArgumentException("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 to" + curView); } }
进一步的删除在 removeViewLocked() 中完成,代码如下:
private void removeViewLocked(int index, boolean immediate) { ViewRootImpl root = mRoots.get(index); View view = root.getView(); if(view != null) { InputMethodManager imm = InputMethodManager.getInstance(); if (imm != null){ imm.windowDismissed(mViews.get(index).getWindowToken()); } } boolean deferred = root.die(immediate); if (view != null){ view.assignParent(null); if (deferred) { mDyingViews.add(view); } } }
root 的 die 方法根据参数 imediate 判断是异步删除还是同步删除。
异步:die 会发一个消息给 ViemRootImpl 的 Handler, Handler 会处理此消息并调用 doDie 方法。
同步:直接调用 doDie 方法。
若是异步,则 die 方法后 view 并没有被删除,view 将被放入 mDyingViews 中等待删除。
Window 的更新过程
实际上通过 WindowManagerGlobal.updateViewLayout() 来完成。public void updateViewLayout(View view, ViewGroup.LayoutParams params) { if (view == null) { throw new IllegalArgumentException("view must not be null"); } if (!(params instanceof WindowManager.LayoutParams)) { throw new IllegalArgumentException("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); mParams.add(index, wparams); root.setLayoutParams(wparams, false); } }
updateViewLayout 方法比较简单,先更新 View 的 LayoutParams 并替代老的 LayoutParams,接着通过 ViewRootImpl 的 setLayoutParams 方法来更新 ViewRootImpl 的 LayoutParams。
Window 的创建过程
这里指操作上的 WindowActivity 的 Window 创建过程
Activity 的启动过程很复杂,最终会由 ActivityThread 中的 performLaunchActivity() 来完成整个启动过程,在这个方法内部会通过类加载器创建 Activity 的实例对象,并调用其 attach() 方法为其关联运行过程中所依赖的一系列上下文环境变量。在 Activity 的 attach() 方法中,系统会通过 PolicyManager.makeNewWindow() 创建 Activity 所属的 Window 对象并为其设置回调接口。
实际中,PolicyManager 的真正实现是 Policy 类,Policy 类中的 makeNewWindow 会返回一个 PhoneWindow 对象。
大致过程如下(==> 为内部,—> 为之后):
performLaunchActivity() ===>
activity = mInstrumentation.newActivity(…) —>
activity.attach() ===>
mWindow = PolicyManager.makeNewWindow(this) ===>
return new PhoneWindow(context);
现在 Window 已经创建完成,下面分析 Activity 的视图是怎样附属在 Window 上的,大致过程如下:
Activity.setContentView() ==>
getWindow.setContentView() ==>
PhoneWindow.setContentView()
PhoneWindow.setContentView() 方法大致有以下步骤:
如果没有 DecorView , 就创建它
将 View 添加到 DecorView 的 mContentParent 中
回调 Activity 的 onContentChanged 方法通知 Activity 视图已经发生变化
Dialog 的 Window 创建过程
Dialog 的 Window 创建过程与 Activity 类似,有一下几个步骤:创建 Window
初始化 DecorView 并将 Dialog 的视图添加到 DecorView 中
将 DecorView 添加到 WindowManager
Dialog 的 Window 创建过程与 Activity 很类似,几乎没什么区别。当 Dialog 被关闭,它会通过 WindowManager 来移除 DecorView:mWindowManager.removeViewImmediate(mDecor)(同步)。
普通的 Dialog 必须采用 Activity 的 Context,因为需要 Activity 的应用 Token(?)。
Toast 的 Window 创建过程
Toast 的 Window 的创建过程就比较复杂了。由于 Toast 具有定时取消这一功能,所以系统采用了 Handler。Toast 提供了 show 和 cancel 分别用于显示和隐藏 Toast,它们内部是一个 IPC 过程。
Toast 的显示:
INotificationManager service = getService() —>
service.enqueueToast(pkg, tn, mDuration) ===>
将 Toast 请求封装为 ToastRecord 添加到 mToastQueue 队列中 —>
showNextToastLocked() ===>
ToastRecord.callback.show() (显示) —>
scheduleTimeoutLocked(record) (延时)
Toast 的隐藏与显示类似,也是通过 ToastRecord 的 callback 来完成的。
ToastRecord.callback.hide() (隐藏)
callback 实际上是 Toast 中的 TN 对象的远程 Binder,通过 callback 来访问 TN 中的方法是需要跨进程来完成的,最终被调用的 TN 中的方法会运行在发起 Toast 请求的应用的 Binder 线程池中。
所以,Toast 的显示和隐藏过程实际上是通过 Toast 中 TN 这个类来实现的,它有两个方法 show 和 hide,即显示和隐藏。由于这两个方法被 NotificationManagerService 以跨进程的方式调用的,因此它们运行在 Binder 线程池中。为了将执行环境切换到 Toast 请求所在线程,在它们内部使用了 Handler。
相关文章推荐
- Linux下XWindow图形界面的基本概念
- Android实现表情 抓取新浪表情
- 详解Android解析Xml的三种方式——DOM、SAX以及XMLpull
- AS3自写类整理笔记 ClassLoader类第1/2页
- AS3自写类整理笔记 Dot类第1/2页
- DB2新手使用的一些小笔记:新建实例、数据库路径不存在、客户端连接 .
- 快速掌握Node.js之Window下配置NodeJs环境
- perl脚本学习指南--读书笔记
- 《C++ primer plus》读书笔记(三)
- 《C++ primer plus》读书笔记(二)
- ipc通道入侵相关命令整理
- 判断window.onload是否多次使用的方法
- Javascript中封装window.open解决不兼容问题
- jquery 笔记 事件
- jquery自定义插件开发之window的实现过程
- Linux IPC命令的用法详解
- window.returnValue使用方法示例介绍
- javascript类型系统 Window对象学习笔记
- jqGrid 学习笔记整理――进阶篇(一 )
- window系统的Rsync同步实战分析第1/2页