• 理解Android中Dialog


    文章收藏的好句子:你能走多远、爬多高,不仅取决于你自身的力量,还取决于周围人带动的力量。

    PS:本文是基于 Android Api 26 来分析源码的。

    1、Dialog 的 Window 是在哪里创建的?

    Dialog 的 Window 是在什么地方创建的呢?我们来看看 Dialog 的一个构造方法,那就是 Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) 方法;

    f4ac8418e45a00b7c34f99e10bcb6213.png

    看到注释1中的代码没有,它创建了一个 Window,而这个 Window 的实现类是 PhoneWindow,它跟 Activity 一样也是用 PhoneWindow 作为自己的 Window;好,那既然有 Window,那就必然有 View 显示出来对不对?那我们看看这个 Dialog 的 PhoneWindow 是如何加载 View 的,我们看一下 Dialog 其中一个加载 View 的方法 setContentView(@LayoutRes int layoutResID);

    7146e6fe1f67081f269a361ad89e5775.png

    mWindow 就是 Window,而 Window 的实现类是 PhoneWindow,所以我们往下看 PhoneWindow 的 setContentView(@LayoutRes int layoutResID) 方法;

    33833314a0b6eb9c36fee057da51c74f.png

    看到 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 的,那么就会报错。

    好,为了更好的理解,我们先上一段简单的代码;

    467eceef293c767dde42f0aa6c01e43d.png

    把 app 运行一下,发现报了如下错误;

    2f3b5aefc58479c8a465befa880f6f9d.png

    这里报错的原因是没有应用 token 所导致的,应用 token 一般只有 Activity 才拥有,所以这里只用用 Activity 的 Context 来作为 Dialog 的就可以了;这里因为 Appliation 的 token 是空的,而 Dialog 一开始创建 Winow 的时候 token 要是空的;我们来看一下 Dialog 的一个构造方法,那就是 Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper);

    de33e37b9eaf10ae67fc9cd219570269.png

    看注释3的代码,第二个参数是不是 null,那就说明 Dialog 的 token 为 null 了;Dialog 的窗口类型为 TYPE_APPLICATION 类型的,要求必需是 Activity 的 Token,不是的话系统会抛出 BadTokenException 异常;Dialog 是应用窗口类型,Token 必须是 Activity 的 Token;我们看一下注释2的代码,假设 context 是 Activity,看 getSystemService 方法的传入的是 Context.WINDOW_SERVICE,好,我们看一下 Activity 的 getSystemService方法;

    8e70ae433c9d174f6387864b662f8d69.png

    看注释4的代码,nam 为 Context.WINDOW_SERVICE,所以返回的是 mWindowManager ,而 mWindowManager 的实现类是 WindowManagerImpl;那 Activity 是在什么时候设置 token 的呢?答案是在 Activity 的 attach方法;

    346695c455cdd33b8af48ef8130b826e.png

    看到注释5的代码没,第二个参数就是 Activity 的 token ,好,我们往下看 Window 的 setWindowManager方法;

    7afc02d40dc326789524ed2f3d8be50a.png

    注释6的代码,只是将 token 保存到 Activity 的 Window 中;看注释7的代码,这里的 this 指的是 Activity 的 PhoneWindow;好,我们往下看 WindowManagerImpl 的 createLocalWindowManager方法;

    9055fee69bb3b36939546121eb5cf71f.png

    WindowManagerImpl 的 createLocalWindowManager 方法又调用了 WindowManagerImpl(Context context, Window parentWindow)方法;

    d8b10ae8c860740853505ee61ce37fc9.png

    mParentWindow是什么?一看这名字就知道肯定是某个 Window 的父 Window,也就是说 Activity 的 Window 是某个 Window 的父 Window;那什么时候 Activity 的 token 给了 Dialog 呢?我们看一下 Dialog 的 show 方法;

    404832d0e9ae95697b509314b179d472.png

    注释9代码其实就是显示 Dialog 的 View 过程,并将 mShowing 置为 true,表示 View 正在显示;好,我们看看注释9代码的实现,mWindowManager 是 WindowManagerImpl,我们看看 addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params)方法;

    b25e246989873c03c075fc58204152d1.png

    注释10中的 mParentWindow是 Activity 的 PhoneWindow(如果 mContext 是 Activity 的话),mGlobal 是 WindowManagerGlobal,我们看一下 WindowManagerGlobal 的 addView(View view, ViewGroup.LayoutParams params, Display display, Window parent-Window)方法;

    2483df6bb06ecf79ec990c1967138fab.png

    看注释11的代码,parentWindow 就是 Activity 的 PhoneWindow,所以执行到 Window 的 adjustLayoutParamsForSubWindow(WindowMana-ger.LayoutParams wp)方法;

    0335ded631d4e670260c9f87c875241c.png

    看到注释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 中实现的;

    678d05c5d673cd64faca025ab8047dd7.png

    mWindowAttributes 是什么,我们看看 mWindowAttributes的声明;

    a322c3c49449b8e05b0e6af275e12e94.png

    我们看看 WindowManager.LayoutParams 的无参构造方法

    1c92e49bb9c3b0439a78d97a5a6d910f.png

    看到注释13的代码没有,Window 默认使用的 type 是 TYPE_APPLICATION,而 Dialog 的 Window 没有改变 type,所以 Dialog 的 Window 是 TYPE_APPLICATION 类型的窗口。

    最后我们看一下 Dialog 的 dismiss 方法的实现;

    e817255e40d1c8111af51000ce163ad5.png

    看注释14,如果当前线程是主线程,那么直接执行 dismissDialog 方法;看注释15,如果不是主线程,那么就切换到主线程去执行;好,我们继续看 Dialog 的 dismissDialog方法;

    8f4100f93d877aef4b1bebaa449cbeee.png

    看注释16代码,最终将 DecorView 从 PhoneWindow 中删除就完事了。

  • 相关阅读:
    郑州灵活用工平台的市场价值大吗?
    Kubernetes亲和性学习笔记
    04-云计算安全
    3.msfconle
    【C++AVL树】4种旋转详讲
    2023年中国铁路行车监测系统竞争格局、市场规模及行业发展趋势分析[图]
    漫画 | 单元测试实在是太可怕了!
    内存泄漏检测工具 - valgrind 的使用
    网络协议--TCP连接的建立与终止
    vue 大文件切片下载
  • 原文地址:https://blog.csdn.net/qq_37837621/article/details/126697302