阅读 2453
WindowInspector(窗口检查器)出来两年了,还不了解?!!

WindowInspector(窗口检查器)出来两年了,还不了解?!!

本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!

前言

这个知识点,出来两年了,现在在网上搜索,没有看到相关分享。一个非常好用的Api,Android 10 才增加的,解决悬浮窗口的一个痛点,下面把我的经验跟大家分享一下,希望大能够受用。

悬浮窗口的痛点(View is attach)

为什么说 “View is attach的判断是悬浮窗口的痛点”?

在Android 10 之前

WinodwManager 提供了 addView、removeView 操作,没有查询接口,如何判断view是否被add,所能想到的方式就是view is attach。加上 “ View not attached to window manager” 的 源码log,使开发者确信,通过View.isAttachedToWindow判断,就可以判断 view是否在window上,没有调查使用中可能出现的风险。

可悲的是,没有其他好的Api之前,使用View.isAttachedToWindow 也几乎变成了一个唯一的选择。

Android 10 推了新的API,让开发者多了一个更好的选择。

通过一个案例,来说明这个API,并解决这个痛点。

案例回顾

悬浮窗口的使用

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

但新增或者移除容易导致Crash,log如下

如:同一个view 被add 两次

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)
复制代码

移除一个,没有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)
复制代码

通常的解决方式:log提示 not attached -> 刚好可以使用View.isAttachedToWindow 去判断代码如下:

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();
    }
}
复制代码

遇到的问题

使用以上代码,但在项目中还是有极低概率还是会出现crash。

也就是 isAttachedToWindow ≠ view is add window

解决问题

  • 方案一:

    应用中给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的范围是应用级别的,获取不到其他应用的窗口。

文章分类
Android
文章标签