在WindowManagerService中有两种常见的Token,WindowToken,和AppWindowToken。

WindowToken

http://androidxref.com/6.0.1_r10/xref/frameworks/base/services/core/java/com/android/server/wm/WindowToken.java

class WindowToken {
// The window manager!
final WindowManagerService service;
final IBinder token;
final int windowType;
final boolean explicit;
String stringName;
// If this is an AppWindowToken, this is non-null.
AppWindowToken appWindowToken; // All of the windows associated with this token.
final WindowList windows = new WindowList();
//...
}

成员变量token是IBinder对象,具有系统唯一性,因此向WMS的mWindowMap或者mTokenMap中插入对象时都是使用token值作为索引。

  explicit为false表示这个Token是在WMS中创建的,为true表示其他模块通过addAppToken()或者addWindowToke()方法显示创建的。成员变量windows的类型是WindowList,他包含一个WindowState对象的列表,所有拥有相同WindowToken的窗口都在这个列表中。

成员变量windowType表示窗口类型。

AppWindowToken

AppWindowToken从WindowToken类派生,它是一种比较特殊的WindowToken,代表应用窗口,主要是Activity中创建的顶层窗口。一个WindowToken对象成员变量appWinowToken为Null,那么他就不是AppWindowToken对象。

http://androidxref.com/6.0.1_r10/xref/frameworks/base/services/core/java/com/android/server/wm/AppWindowToken.java

class AppWindowToken extends WindowToken {
final IApplicationToken appToken;
// All of the windows and child windows that are included in this
// application token. Note this list is NOT sorted!
final WindowList allAppWindows = new WindowList();
final AppWindowAnimator mAppAnimator; final WindowAnimator mAnimator;
//... }

appToken用来表示应用Token,它是ActivityManagerService中创建的,代表一个应用。

allAppWindows也是一个WindowList,它保存了所有拥有相同AppWindowToken类型的Token

WindowToken

1.WindowToken的意义

WindowToken将属于同一个应用组件的窗口组织在了一起。所谓的应用组件可以是Activity、InputMethod、Wallpaper以及Dream。在WMS对窗口的管理过程中,用WindowToken指代一个应用组件。例如在进行窗口ZOrder排序时,属于同一个WindowToken的窗口会被安排在一起,而且在其中定义的一些属性将会影响所有属于此WindowToken的窗口。这些都表明了属于同一个WindowToken的窗口之间的紧密联系。

  WindowToken具有令牌的作用,是对应用组件的行为进行规范管理的一个手段。WindowToken由应用组件或其管理者负责向WMS声明并持有。应用组件在需要新的窗口时,必须提供WindowToken以表明自己的身份,并且窗口的类型必须与所持有的WindowToken的类型一致。在创建系统类型的窗口时不需要提供一个有效的Token,WMS会隐式地为其声明一个WindowToken,看起来谁都可以添加个系统级的窗口。难道Android为了内部使用方便而置安全于不顾吗?非也,addWindow()函数一开始的mPolicy.checkAddPermission()的目的就是如此。它要求客户端必须拥有SYSTEM_ALERT_WINDOW或INTERNAL_SYSTEM_WINDOW权限才能创建系统类型的窗口。

2.向WMS声明WindowToken

既然应用组件在创建一个窗口时必须指定一个有效的WindowToken才行,那么WindowToken究竟该如何声明呢?

final class Session extends IWindowSession.Stub
implements IBinder.DeathRecipient {
      //...
@Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
  }
}
mService是WindowManagerService
http://androidxref.com/6.0.1_r10/xref/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
 为了验证这一点,来看一下addWindow的代码,如下所示:
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
   WindowToken token = mTokenMap.get(attrs.token);
AppWindowToken atoken = null;
if (token == null) {
 ...
token = new WindowToken(this, attrs.token, -1, false);
addToken = true;
} }

WindowToken对于客户端来说只是一个令牌,一个可以添加窗口的许可,WindowToken对于WMS来说,是一个WindowToken的实例对象,存储在mTokenMap中。WindowToken将同一个应用组件(Activity,InputMethod,Wallpaper,Dream)的窗口组织在一起

来看一下另一个addWindowToken的代码

使用wms.addWindowToken()函数声明mToken作为它的令牌,所以在添加窗口时,通过设置lp.token为mToken向WMS进行出示,从而获得WMS添加窗口的许可。这说明,只要是一个Binder对象(随便一个),都可以作为Token向WMS进行声明。对于WMS的客户端来说,Token仅仅是一个Binder对象而已。

,如下所示:

http://androidxref.com/6.0.1_r10/xref/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

  @Override
publicvoid addWindowToken(IBinder token, int type) {
// 需要声明Token的调用者拥有MANAGE_APP_TOKENS的权限
if(!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"addWindowToken()")) {
thrownew SecurityException("Requires MANAGE_APP_TOKENS permission");
}
synchronized(mWindowMap){
......
// 注意其构造函数的参数与addWindow()中不同,最后一个参数为true,表明这个Token
// 是显式申明的
wtoken= new WindowToken(this, token, type, true);
mTokenMap.put(token,wtoken);
......
}
}

使用addWindowToken()函数声明Token,将会在WMS中创建一个WindowToken实例,并添加到mTokenMap中,键值为客户端用于声明Token的Binder实例。与addWindow()函数中隐式地创建WindowToken不同,这里的WindowToken被声明为显式的。隐式与显式的区别在于,当隐式创建的WindowToken的最后一个窗口被移除后,此WindowToken会被一并从mTokenMap中移除。显式创建的WindowToken只能通过removeWindowToken()显式地移除。

addWindowToken()这个函数告诉我们,WindowToken其实有两层含义:

  • 对于显示组件(客户端)而言的Token,是任意一个Binder的实例,对显示组件(客户端)来说仅仅是一个创建窗口的令牌,没有其他的含义。
  • 对于WMS而言的WindowToken这是一个WindowToken类的实例,保存了对应于客户端一侧的Token(Binder实例),并以这个Token为键,存储于mTokenMap中。客户端一侧的Token是否已被声明,取决于其对应的WindowToken是否位于mTokenMap中。

注意 在一般情况下,称显示组件(客户端)一侧Binder的实例为Token,而称WMS一侧的WindowToken对象为WindowToken。但是为了叙述方便,在没有歧义的前提下不会过分仔细地区分这两个概念。


接下来,看一下各种显示组件是如何声明WindowToken的。

(1) Wallpaper和InputMethod的Token

Wallpaper的Token声明在WallpaperManagerService中。参考以下代码:

boolean bindWallpaperComponentLocked(......) {
......
WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);
......
mIWindowManager.addWindowToken(newConn.mToken,
WindowManager.LayoutParams.TYPE_WALLPAPER);
......
}

WallpaperManagerService是Wallpaper管理器,它负责维护系统已安装的所有的Wallpaper并在它们之间进行切换,而这个函数的目的是准备显示一个Wallpaper。newConn.mToken与SampleWindow例子一样,是一个简单的Binder对象。这个Token将在即将显示出来的Wallpaper被连接时传递给它,之后Wallpaper即可通过这个Token向WMS申请创建绘制壁纸所需的窗口了。

注意 :WallpaperManagerService向WMS声明的Token类型为TYPE_WALLPAPER,所以,Wallpaper仅能本分地创建TYPE_WALLPAPER类型的窗口。

相应的,WallpaperManagerService会在detachWallpaperLocked()函数中取消对Token的声明:

boolean detachWallpaperLocked(WallpaperData wallpaper){
......
mIWindowManager.removeWindowToken(wallpaper.connection.mToken);
......
}

再此之后,如果这个被detach的Wallpaper想再要创建窗口便不再可能了。

WallpaperManagerService使用WindowToken对一个特定的Wallpaper做出了如下限制:

  • Wallpaper只能创建TYPE_WALLPAPER类型的窗口。
  • Wallpaper显示的生命周期由WallpaperManagerService牢牢地控制着。仅有当前的Wallpaper才能创建窗口并显示内容。其他的Wallpaper由于没有有效的Token,而无法创建窗口。

InputMethod的Token的来源与Wallpaper类似,其声明位于InputMethodManagerService的startInputInnerLocked()函数中,取消声明的位置在InputmethodManagerService的unbindCurrentMethodLocked()函数。InputMethodManagerService通过Token限制着每一个InputMethod的窗口类型以及显示生命周期。

Activity的Token

Activity的Token的使用方式与Wallpaper和InputMethod类似,但是其包含更多的内容。毕竟,对于Activity,无论是其组成还是操作都比Wallpaper以及InputMethod复杂得多。对此,WMS专为Activity实现了一个WindowToken的子类:AppWindowToken。

既然AppWindowToken是为Activity服务的,那么其声明自然在ActivityManagerService中。具体位置为ActivityStack.startActivityLocked(),也就是启动Activity的时候。相关代码如下:

ActivityStack.startActivityLocked()

http://androidxref.com/6.0.1_r10/xref/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java

private final void startActivityLocked(......) {
......
mService.mWindowManager.addAppToken(addPos,r.appToken, r.task.taskId,
r.info.screenOrientation, r.fullscreen);
......
}

startActivityLocked()向WMS声明r.appToken作为此Activity的Token,这个Token是在ActivityRecord的构造函数中创建的。随然后在realStartActivityLocked()中将此Token交付给即将启动的Activity。

ActivityStack.realStartActivityLocked()

final boolean realStartActivityLocked(......) {
......
app.thread.scheduleLaunchActivity(newIntent(r.intent), **r.appToken,**
System.identityHashCode(r), r.info,
newConfiguration(mService.mConfiguration),
r.compat, r.icicle, results, newIntents,!andResume,
mService.isNextTransitionForward(),profileFile, profileFd,
profileAutoStop);
......
}

动后的Activity即可使用此Token创建类型为TYPE_APPLICATION的窗口了。

取消Token的声明则位于ActivityStack.removeActivityFromHistoryLocked()函数中。

Activity的Token在客户端是否和Wallpaper一样,仅仅是一个基本的Binder实例呢?其实不然。看一下r.appToken的定义可以发现,这个Token的类型是IApplicationToken.Stub。其中定义了一系列和窗口相关的一些通知回调,它们是:

  • windowsDrawn(),当窗口完成初次绘制后通知AMS。
  • windowsVisible(),当窗口可见时通知AMS。
  • windowsGone(),当窗口不可见时通知AMS。
  • keyDispatchingTimeout(),窗口没能按时完成输入事件的处理。这个回调将会导致ANR。
  • getKeyDispatchingTimeout(),从AMS处获取界定ANR的时间。

AMS通过ActivityRecord表示一个Activity。而ActivityRecord的appToken在其构造函数中被创建,所以每个ActivityRecord拥有其各自的appToken。而WMS接受AMS对Token的声明,并为appToken创建了唯一的一个AppWindowToken。因此,这个类型为IApplicationToken的Binder对象appToken粘结了AMS的ActivityRecord与WMS的AppWindowToken,只要给定一个ActivityRecord,都可以通过appToken在WMS中找到一个对应的AppWindowToken,从而使得AMS拥有了操纵Activity的窗口绘制的能力。例如,当AMS认为一个Activity需要被隐藏时,以Activity对应的ActivityRecord所拥有的appToken作为参数调用WMS的setAppVisibility()函数。此函数通过appToken找到其对应的AppWindowToken,然后将属于这个Token的所有窗口隐藏。


注意: 每当AMS因为某些原因(如启动/结束一个Activity,或将Task移到前台或后台)而调整ActivityRecord在mHistory中的顺序时,都会调用WMS相关的接口移动AppWindowToken在mAppTokens中的顺序,以保证两者的顺序一致。在后面讲解窗口排序规则时会介绍到,AppWindowToken的顺序对窗口的顺序影响非常大。

android window(四)WindowToken的更多相关文章

  1. 【转】android camera(四):camera 驱动 GT2005

    关键词:android  camera CMM 模组 camera参数  GT2005 摄像头常见问题 平台信息: 内核:linux系统:android 平台:S5PV310(samsung exyn ...

  2. 【转】Android LCD(四):LCD驱动调试篇

    关键词:android LCD TFTSN75LVDS83B  TTL-LVDS LCD电压背光电压 平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台 ...

  3. Android Studio(四):Android Studio集成Genymotion

    Android Studio相关博客: Android Studio(一):介绍.安装.配置 Android Studio(二):快捷键设置.插件安装 Android Studio(三):设置Andr ...

  4. Android中四种补间动画的使用示例(附代码下载)

    场景 Android中四种补间动画. 透明度渐变动画 旋转动画 缩放动画 平移动画 注: 博客: https://blog.csdn.net/badao_liumang_qizhi关注公众号 霸道的程 ...

  5. android window(三)lWindow添加流程

    http://androidxref.com/6.0.1_r10/xref/frameworks/base/services/core/java/com/android/server/wm/Windo ...

  6. android学习四(Activity的生命周期)

    要学好活动(Activity).就必需要了解android中Activity的声明周期.灵活的使用生命周期.能够开发出更好的程序,在android中是使用任务来管理活动的,一个任务就是一组存放在栈里的 ...

  7. Android Window 9问9答

    1.简述一下window是什么?在android体系里 扮演什么角色? 答:window就是一个抽象类,他的实现类是phoneWindow.我们一般通过windowManager 来访问window. ...

  8. Android Activity四种加载方式

    Android之四种加载方式 (http://marshal.easymorse.com/archives/2950 图片) 在多Activity开发中,有可能是自己应用之间的Activity跳转,或 ...

  9. android camera(四):camera 驱动 GT2005

    摄像头主要参数: 1.MCLK  24MHz: 2.PCLK  48~52MHz~: 3.电压 1.8V(1.5V).2.8V: 4.scl(IIC时钟)100KHz或者400KHz. 下载:常用摄像 ...

随机推荐

  1. 【学习总结】快速上手Linux玩转典型应用-目录

    内容链接 慕课网:快速上手Linux玩转典型应用 目录 第1章-课程介绍 第2章-linux简介 第3章-CentOS的安装 第4章-准备工作 第5章-远程连接SSH专题 第6章-linux常用命令讲 ...

  2. javascript中跨域问题的解决方法汇总

    javascript中实现跨域的方式总结 第一种方式:jsonp请求:jsonp的原理是利用<script>标签的跨域特性,可以不受限制地从其他域中加载资源,类似的标签还有<img& ...

  3. 记录一次TabBar使用本地图片

    方法一: 第一步:import  addpng  from '../../assets/img/add.png' 第二步: { title: '找折扣', image: addpng}, 即可. 方法 ...

  4. ThinkPHP中的display()和fetch()的区别

    fetch()传入的参数是模板名,用模板文件来输出; display()传入的是字符串,输出传递的内容.

  5. 基于双XCKU060+双C6678 的双FMC接口40G光纤传输加速计算卡381

    一.板卡概述 板卡采用基于双FPGA+双DSP的信号采集综合处理硬件平台,板卡大小360mmx217mm.板卡两片FPGA提供两个FMC接口,4路QSFP+接口:每片FPGA挂接2簇32-bit DD ...

  6. PAT Basic 1032 挖掘机技术哪家强 (20 分)

    为了用事实说明挖掘机技术到底哪家强,PAT 组织了一场挖掘机技能大赛.现请你根据比赛结果统计出技术最强的那个学校. 输入格式: 输入在第 1 行给出不超过 1 的正整数 N,即参赛人数.随后 N 行, ...

  7. mock.js模拟ajax数据请求

    在我们开发过程中存在着前端页面ui完成了,但是没有接口进行联调数据的情况,现在介绍一下用mock.js来完成数据的请求.这样在后期我们的后台接口完成后只需要更改请求的接口名字即可!前提是你的模拟字段名 ...

  8. Codeforces Round #573 (Div. 2) E. Tokitsukaze and Duel (博弈)

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  9. L3-006. 迎风一刀斩

    迎着一面矩形的大旗一刀斩下,如果你的刀够快的话,这笔直一刀可以切出两块多边形的残片.反过来说,如果有人拿着两块残片来吹牛,说这是自己迎风一刀斩落的,你能检查一下这是不是真的吗? 注意摆在你面前的两个多 ...

  10. 29.密码学知识-消息认证码MAC-6——2019年12月19日

    1. 消息认证码 1.1 消息认证 消息认证码(message authentication code)是一种确认完整性并进行认证的技术,取三个单词的首字母,简称为MAC. 思考改进方案? 从哈希函数 ...