引言
很多人应该知道Activity,Service中的Context和ApplicationContext的区别,而且也知道Context,ContextImpl,ContextWrapper,Activity,Service,Application构成的体系,在异步任务需要Context时,也知道为了防止内存泄露需要传递ApplicationContext而不是Activity的Context,但是这样的场景并不万能,因为并不是所有需要Activity的Context的地方都可以用ApplicationContext来代替。
1.Context继承体系
所以一个App进程中Context数量=Activity数量+Service数量+1.
2.Activity的startActivity()和Application的startActivity()的区别
Application.startActivity()本质上是调用其中的mBase(ContextImpl对象)的startActivity()方法,而ContextImpl.startActivity()如下:
|
|
可以看到这里对Intent的flag进行了检查,如果没有FLAG_ACTIVITY_NEW_TASK属性,就会抛出异常。
那为什么Activity中就不需要做这样的检查了?
根本原因在于Application中的Context并没有所谓的任务栈,所以待启动的Activity就找不到task了,这样的话要启动Activity就必须将它放到一个新的task中,即使用singleTask的方式启动。
3.Dialog的创建
如下示例:
|
|
如果其中的context是Application Context,那么会抛出以下异常:
|
|
这个异常是在ViewRoogImpl的setView()中抛出的。
而抛出这个异常的原因是与WMS进行Binder IPC(res=mWindowSession.addToDisplay())的结果,而这个结果是执行WMS中addWindow()的结果,该方法如下:
|
|
显然,这是由于AppWindowToken==null导致的,这个AppWindowToken对应client端中Window的IBinder mAppToken这个成员。
由于AlertDialog中的super()会调用Dialog的构造方法,所以我们先看一下Dialog的构造方法:
|
|
注意其中的w.setWindowManager(),显然,传递的appToken为null.这也是Dialog和Activity窗口的一个区别,Activity会将这个appToken设置为ActivityThread传过来的token.
在Dialog的show()方法中:
|
|
其中mWindow是PhoneWindow类型,mWindow.getAttributes()获取到的Type为TYPE_APPLICATION.
Dialog也是通过WindowManager把自己的Window添加到WMS上,但是这里在addView()之前,mWindow的token为null(前面已经分析了,w.setWindowManager的第二个参数为null).而WMS要求TYPE_APPLICATION的窗口的token不能为null.
而如果用Application或者Service的Context区获取这个WindowManager服务的话,会得到一个WindowManagerImpl对象,这个实例中token也是空的。
那为什么Activity就可以呢?
原来是Activity重写了getSystemService()方法:
|
|
显然,对于WINDOW_SERVICE,返回的是mWindowManager对象,而这个对象的创建是在Activity的attach()方法中:
|
|
注意其中的mWindow.setWindowManager(),在这里将Activity的mToken给了mWindow,所以这就是Activity中的mWindow和Dialog中的mWindow的区别。
所以不能通过Application和Service的Context来创建Dialog,只能通过Activity的Context来创建Dialog.