Dialog的Context只能是Activity的Context,不能是Application的Context。(现在还不知道为什么,捉急)
发生一个BadTokenException的异常,不能添加window。
Window:定义窗口样式的行为的抽象基类,用于作为顶层的view加到windowmanager中,其实现类是phonewindow。每个window都需要制定一个type,这个type成员变量通常有三种类型:
1.Application windows:取值范围从FIRST_APPLICATION_WINDOW到LAST_APPLICATION_WINDOW,这种window是普通的顶层window,这些种类的window的token必须设置成Activity的token。
2. System window
取值范围从FIRST_SYSTEM_WINDOW到LAST_SYSTEM_WINDOW,这种window是特殊的window类型,一般是系统用户特殊目的使用的,这种window不应该被普通程序使用,使用时需要特殊权限。
3.Sub-window
取值范围从FIRST_SUB_WINDOW到LAST_SUB_WINDOW,这种window一般都和其他顶层window关联在一起,这种window的token必须是关联的window的token。
WindowManager:用来在应用于window之间的管理接口,管理窗口顺序、消息等。
WindowManagerService:简称WMS,用来管理窗口的创建、更新、和删除,显示顺序等,是windowmanager这个管理接口的真正实现类,它运行在system_server进程,服务端、客户端通过IPC调用和它进行交互。
Token:窗口令牌,是一种特殊的Binder令牌,WMS用它唯一标示系统中的一个窗口。
Activity有一个phonewindow,当我们调用setContentView时,其实最终结果是把我们的DecorView作为子View添加到phonewindow的DecorView中,而最终这个DecorView通过WindowManagerIpml的addView方法添加到WMS中,由WMS负责管理和绘制。
Dialog跟Activity对应的窗口一样,有一个phonewindow实例,Dialog的类型是TYPE_APPLICATION,属于应用窗口类型。Dialog最终也是通过系统的WindowManager把自己的Window添加到WMS上,在addView前,Dialog的token是null,Dialog初始化时是通过Context.getSystemServer来获取WindowManager,而如果用Application或者Service的Context去获取这个WindowManager服务的话,会得到一个WindowManagerImpl的实例,这个实例里token也是空的,之后在Dialog的show方法中将Dialog的View添加到WindowManager时会给token设置默认值还是null。如果这个Context是Activity,则直接返回Activity的mWindowManager,这个mWindowManager在Activity的attach方法被创建,token指向此Activity的token。系统对TYPE_APPLICATION类型的窗口,要求必须是Activity的token,不是的话系统会抛出badtokenexception异常,Dialog是应用窗口类型,token必须是Activity的token。
出于安全原因考虑,Android不允许Activity或Dialog凭空出项,一个Activity的启动必须要建立在另一个Activity的基础上,也就是以此形成的返回栈,而Dialog必须在一个Activity上面弹出(除非是System Alert类型的Dialog),因此在这种场景下,只能使用Activity类型的Context,否则会出错。
那为什么一定要是Activity的Token呢?我想使用Token应该是为了安全问题,通过Token来验证WindowManager服务请求方是否是合法的。如果我们可以使用Application的Context,或者说Token可以不是Activity的Token,那么用户可能已经跳转到别的应用的Activity界面了,但我们却可以在别人的界面上弹出我们的Dialog,想想就觉得很危险。
网友评论