https://www.jianshu.com/p/f42fe33d99f9
https://www.wanandroid.com/wenda/show/18281
为什么传入一个非 Activity 的 context 会出现错误?
- 首先看启动一个Activity的流程(Android10以及以上)
ActivityThread-->performLaunchActivity-->Instrumentation-->ActivityTaskManager.getService().startActivity-->startActivityAsUser-->.execute()-->ActivityStarter.executeRequest-->new ActivityRecord()---->startActivityUnchecked--->mTargetStack.startActivityLocked--->ActivityStack(startActivityLocked)
- ActivityRecord的父类是WindowToken
WindowManagerService-->addWindow-->new WindowToken-->dc.addWindowToken(token, this)--->mTokenMap.put(binder, token);
以上流程主要表面token的传递过程
-
Dialog的显示流程
WindowManagerService
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
int requestUserId) {
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
// If this is a child window, we want to apply the same type checking rules as the
// parent window type.
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
boolean addToastWindowRequiresToken = false;
if (token == null) {
}
}
根据源码可知 displayContent.getWindowToken获取token,如果没有就显示异常。
总结
1.AMS启动Activity,会构建对应的ActivityRecord对象,实例化Token对象
2.实例化的Token对象,最终会被存放在DisplayContent的mTokenMap集合中(也就是WMS的mTokenMap中)
3.WMS在addWindow时,会根据当前window对象的Token进行校验
4.由于Application的父类是ContextImpl,调用 getSystemService后,拿到的WindowManager是通过SystemServiceRegistry.getSystemService拿到的
5.SystemServiceRegistry缓存了一份registerService,所以,当前的WM是没有token的,也就是说,application的context。不能直接被用来创建Dialog
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>() {
@Override
public WindowManager createService(ContextImpl ctx) {
return new WindowManagerImpl(ctx);
}});
为什么非要一个Token?
这是因为在WMS那边需要根据这个Token来确定Window的位置(不是说坐标),如果没有Token的话,就不知道这个窗口应该放到哪个容器上了
传入的 context 一定要是 Activity 吗
想要通过非Activity对象创建并正常显示Dialog,首先必须拥有SYSTEM_ALERT_WINDOW权限,还有,在调用Dialog.show方法之前,必须把Dialog的Window的type指定为SYSTEM_WINDOW类型,比如TYPE_SYSTEM_ALERT或TYPE_APPLICATION_OVERLAY。
没有满足第一个条件的话,那肯定会报permission denied啦。
如果在show之前没有指定Window的type为SYSTEM_WINDOW类型,一样会发生BadTokenException的,message是token null is not valid; is your activity running?。