WindowInspector (window inspector) has been out for two years, still don't understand? ! !

This article has participated in the Haowen Summoning Order activity, click to view: the back-end and front-end dual-track submissions, the 20,000 yuan prize pool is waiting for you to challenge!

foreword

This knowledge point has been out for two years, and now I search on the Internet and have not seen any relevant sharing. A very easy-to-use Api, which was only added in Android 10, to solve a pain point of the floating window. Let me share my experience with you, and I hope it will be useful.

Pain point of floating window (View is attach)

Why do you say "The judgment of View is attach is the pain point of the floating window"?

Before Android 10

WinodwManager provides addView and removeView operations. There is no query interface. How to judge whether a view is added or not, the only way I can think of is view is attach. Adding the source code log of "View not attached to window manager" makes developers convinced that by judging by View.isAttachedToWindow, it is possible to judge whether the view is on the window, without investigating the possible risks in use.

Sadly, without other good APIs, using View.isAttachedToWindow has become almost the only option.

Android 10 pushes new APIs, giving developers a better choice.

Use a case to illustrate this API and solve this pain point.

Case review

Use of floating windows

windowManager.addView(view, layoutParams);//新增窗口
windowManager.removeView(view);移除窗口
复制代码

But adding or removing can easily lead to Crash, the log is as follows

Such as: the same view is added twice

05-21 03:19:13.285 3463 3463 W System.err: java.lang.IllegalStateException: View XXX{7afdd92 V.E...... ......I. 0,0-56,290} has already been added to the window manager.

05-21 03:19:13.285 3463 3463 W System.err:  at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:359)

05-21 03:19:13.285 3463 3463 W System.err:  at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:96)
复制代码

Remove a window without Attach

W System.err: java.lang.IllegalArgumentException: View=XXXX{25626d6 V.E...... ........ 0,0-56,229} not attached to window manager

08-19 07:08:08.832 25836 25836 W System.err:   at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:517)

08-19 07:08:08.832 25836 25836 W System.err:   at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:426)

08-19 07:08:08.832 25836 25836 W System.err:   at android.view.WindowManagerImpl.removeView(WindowManagerImpl.java:123)
复制代码

The usual solution: log prompts not attached -> just use View.isAttachedToWindow to judge the code as follows:

if(view.isAttachedToWindow()){
    try {
        windowManager.removeView(textView);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
if(!view.isAttachedToWindow()){
    try {
        windowManager.addView(view,layoutParams);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
复制代码

problems encountered

Using the above code, but there is still a very low probability of crash in the project.

That is, isAttachedToWindow ≠ view is add window

Solve the problem

  • Option One:

    应用中给View标志位,表示view 是否被add。但是总觉得本地标识,没有系统api准确,该方案不算太好。偶现的问题,必然有必现的过程,后面进行了深入的分析。

    项目中遇到的问题是:同一个view,windowManager.addView 被连续执行了两次,也不是异步造成的。

    最后发现,两次调用时间间隔极短,isAttachedToWindow 还没有来得及改变。

    经详细调查有了方案二。

  • 方案二:

    使用 WindowInspector,此类在Android 10 framework 才增加,且只有getGlobalWindowViews 这一个静态方法

    view.isAttachedToWindow() 改为 WindowInspector.getGlobalWindowViews().contains(view)
    复制代码

源码分析

getGlobalWindowViews

看下主要类的调用关系,以及主要的方法

RmzUoV.png

/**
* addView 与 removeView 都会调用此方法
* addView  required:false 如果 index !=0  同一个View重复add,抛异常
* removeView required:true  index < 0 ,没有找到View,抛异常
*/
private int findViewLocked(View view, boolean required) {
    final int index = mViews.indexOf(view);
    if (required && index < 0) {
        throw new IllegalArgumentException("View=" + view + " not attached to window manager");
    }
    return index;
}
复制代码

通过以上分析:WindowManagerGlobal 中 mView 是问题的关键,管理着所属应用的所有View。

Android 10,提供了 WindowInspector.getGlobalWindowViews()。可以获取mViews。修改代码如下

if(WindowInspector.getGlobalWindowViews().contains(view)){
    windowManager.removeView(view);
}
if(!WindowInspector.getGlobalWindowViews().contains(view)){
    windowManager.addView(view,layoutParams);
}
复制代码

分析到这里,问题就解决了。那isAttachedToWindow 怎么就不行呢?

isAttachedToWindow

以下时序图看出:

当刷新线程走完后,才认为是attach。

在这里插入图片描述

两者区别是:attach 当view 被add ,且被刷新了。

总结

经过上文分析,从view add -> attach , 区别是view 是否被刷新。

分析了下:为什么要重新用WindowInspector,而不是用WIndowManager WindowManager:是对Window 的管理,查询觉得不合适吧,所以用了WindowInspector类

下面说明下可能出现的一些误区

1、getGlobalWindowViews 获取的View列表 ,其实是WindowManagerGlobal.mViews 的浅拷贝,所以增删改,都不会应该WindowManagerGlobal 中mViews的结构。

2、强调下,不要被Global迷惑,Global的范围是应用级别的,获取不到其他应用的窗口。

Guess you like

Origin juejin.im/post/6981476971394007054