Dialog使用Application作为context为什么会报错?(一)

相信使用过Dialog(AlertDialog、ProgressDialog)的人都会遇到这样一个问题:如果使用Application做为context去构造Dialog,在调用show方法的时候会提示如下错误:

09-21 07:23:36.023: ERROR/AndroidRuntime(1550): FATAL EXCEPTION: main
09-21 07:23:36.023: ERROR/AndroidRuntime(1550): android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
09-21 07:23:36.023: ERROR/AndroidRuntime(1550):     at android.view.ViewRoot.setView(ViewRoot.java:509)

在通过ViewRoot的setView向WindowManagerService添加一个window的时候,远端WMS出现了BadTokenException,导致app挂掉。

相信大家都知道怎么修改能够不挂,使用Activity作为context就ok了。

这里的异常类型是BadTokenException,直译过来是坏令牌异常,令牌这个概念在android的系统service中有经常使用到,其本质是Binder,利用Binder对象无论跨多少进程都会指向同一个对象,这个是由Binder driver实现的(binder driver还没研究过)。

利用Binder的这个特性,android系统中将其作为一种安全机制。举个简单的例子:在启动Activity的流程当中,首先,ActivityManagerService会创建ActivityRecord由其本身来管理,同时会为这个ActivityRecord创建一个IApplication(本质上就是一个Binder);然后,由于AMS和WMS都是有SystemServer起的,AMS调用WMS提供的接口,让WMS记录下这个Binder;当AMS这边完成数据结构的添加之后,会返回给ActivityThread一个ActivityClientRecord数据结构,中间就包含了Binder对象;ActivityThread这边拿到这个Binder对象之后,就需要让WMS去在界面上添加一个对应窗口,在添加窗口传给WMS的数据中WindowManager.LayoutParams这里面就包含了Binder;最终WMS在添加窗口的时候,就需要将这个Binder和之前AMS保存在里面的Binder做比较,对得上说明是合法的,否则,就会抛出BadTokenException这个异常。(这里插一句,如果client端调用server端,server端出了异常,传回给client端的Parcel是带有异常信息的)从上面一段描述中可以看出,Binder在系统中确实是起到一种信息验证的作用,能够起到一种令牌的作用。

在回到我们的问题,dialog使用Application会挂,而activity不会,相信大家基本都能猜的出来原因了,因为activity这类context是带有Token信息的,而Application没有,所以在WMS中会得到区别对待。

今天挖个坑开个头,后续会从源码级别分析整个流程的来龙去脉的。

参考文献:

Binder作为令牌分析

    原文作者:Kelvin wu
    原文地址: https://zhuanlan.zhihu.com/p/20376790
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞