PS:本文系转载文章,浏览原文可读性会更好,文章开端有原文链接
PS:本文是基于 Android Api 26 来剖析源码的。1、Dialog 的 Window 是在哪里创立的?Dialog 的 Window 是在什么中央创立的呢?咱们来看看 Dialog 的一个构造方法,那就是 Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) 办法;
看到正文 1 中的代码没有,它创立了一个 Window,而这个 Window 的实现类是 PhoneWindow,它跟 Activity 一样也是用 PhoneWindow 作为本人的 Window;好,那既然有 Window,那就必然有 View 显示进去对不对?那咱们看看这个 Dialog 的 PhoneWindow 是如何加载 View 的,咱们看一下 Dialog 其中一个加载 View 的办法 setContentView(@LayoutRes int layoutResID);
mWindow 就是 Window,而 Window 的实现类是 PhoneWindow,所以咱们往下看 PhoneWindow 的 setContentView(@LayoutRes int layoutResID) 办法;
看到 PhoneWindow 的 setContentView(@LayoutRes int layoutResID) 办法没有,Activity 的 setContentView(@LayoutRes int layoutResID) 办法解析 View 过程和 Dialog 的 setContentView(@LayoutRes int layoutResID) 办法解析 View 过程是一样的,所以就不在对 PhoneWindow 的 setContentView(@LayoutRes int layoutResID) 办法进行剖析了,能够看 Android 中 AppCompatActivity 的 setContentView 办法剖析这篇文章进行了解它。2、咱们在平时开发的时候是否遇到这样的一个问题:如果 Dialog 应用的 Context 不是 Activity 的而是 Application 的,那么就会报错。好,为了更好的了解,咱们先上一段简略的代码;
把 app 运行一下,发现报了如下谬误;
这里报错的起因是没有利用 token 所导致的,利用 token 个别只有 Activity 才领有,所以这里只用用 Activity 的 Context 来作为 Dialog 的就能够了;这里因为 Appliation 的 token 是空的,而 Dialog 一开始创立 Winow 的时候 token 要是空的;咱们来看一下 Dialog 的一个构造方法,那就是 Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper);
看正文 3 的代码,第二个参数是不是 null,那就阐明 Dialog 的 token 为 null 了;Dialog 的窗口类型为 TYPE_APPLICATION 类型的,要求必须是 Activity 的 Token,不是的话零碎会抛出 BadTokenException 异样;Dialog 是利用窗口类型,Token 必须是 Activity 的 Token;咱们看一下正文 2 的代码,假如 context 是 Activity,看 getSystemService 办法的传入的是 Context.WINDOW_SERVICE,好,咱们看一下 Activity 的 getSystemService 办法;
看正文 4 的代码,nam 为 Context.WINDOW_SERVICE,所以返回的是 mWindowManager,而 mWindowManager 的实现类是 WindowManagerImpl;那 Activity 是在什么时候设置 token 的呢?答案是在 Activity 的 attach 办法;
看到正文 5 的代码没,第二个参数就是 Activity 的 token,好,咱们往下看 Window 的 setWindowManager 办法;
正文 6 的代码,只是将 token 保留到 Activity 的 Window 中;看正文 7 的代码,这里的 this 指的是 Activity 的 PhoneWindow;好,咱们往下看 WindowManagerImpl 的 createLocalWindowManager 办法;
WindowManagerImpl 的 createLocalWindowManager 办法又调用了 WindowManagerImpl(Context context, Window parentWindow) 办法;
mParentWindow 是什么?一看这名字就晓得必定是某个 Window 的父 Window,也就是说 Activity 的 Window 是某个 Window 的父 Window;那什么时候 Activity 的 token 给了 Dialog 呢?咱们看一下 Dialog 的 show 办法;
正文 9 代码其实就是显示 Dialog 的 View 过程,并将 mShowing 置为 true,示意 View 正在显示;好,咱们看看正文 9 代码的实现,mWindowManager 是 WindowManagerImpl,咱们看看 addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) 办法;
正文 10 中的 mParentWindow 是 Activity 的 PhoneWindow(如果 mContext 是 Activity 的话),mGlobal 是 WindowManagerGlobal,咱们看一下 WindowManagerGlobal 的 addView(View view, ViewGroup.LayoutParams params, Display display, Window parent-Window) 办法;
看正文 11 的代码,parentWindow 就是 Activity 的 PhoneWindow,所以执行到 Window 的 adjustLayoutParamsForSubWindow(WindowMana-ger.LayoutParams wp) 办法;
看到正文 12 的代码没有,decor 实质上是 Activity 中 PhoneWindow 的 DecorView,decor 拿到的是 mAttachInfo.mWindowToken,而 mAttachInfo.mWindowToken 正是 Activity 中 PhoneWindow 的 token,所以在 Dialog 的 show 过程其实也将 Activity 的 token 赋值给了 Dialog。咱们下面不是提到过 Dialog 的 Window 是 TYPE_APPLICATION 类型的窗口吗?怎么证实它是?好,咱们回过头来看正文 8 的代码,也就是 Dialog 的 PhoneWindow 的 getAttributes 办法,它在 Window 中实现的;
mWindowAttributes 是什么,咱们看看 mWindowAttributes 的申明;
咱们看看 WindowManager.LayoutParams 的无参构造方法;
看到正文 13 的代码没有,Window 默认应用的 type 是 TYPE_APPLICATION,而 Dialog 的 Window 没有扭转 type,所以 Dialog 的 Window 是 TYPE_APPLICATION 类型的窗口。最初咱们看一下 Dialog 的 dismiss 办法的实现;
看正文 14,如果以后线程是主线程,那么间接执行 dismissDialog 办法;看正文 15,如果不是主线程,那么就切换到主线程去执行;好,咱们持续看 Dialog 的 dismissDialog 办法;
看正文 16 代码,最终将 DecorView 从 PhoneWindow 中删除就完事了。