この記事はHaowenSummoningOrderアクティビティに参加しています。クリックして表示します。バックエンドとフロントエンドのデュアルトラックの提出物で、20,000元の賞金プールがあなたの挑戦を待っています!
序文
この知識のポイントは2年間出ていましたが、今はインターネットで検索していますが、関連する共有は見られません。フローティングウィンドウの問題点を解決するためにAndroid10でのみ追加された非常に使いやすいApi。私の経験を皆さんと共有させてください。それがお役に立てば幸いです。
フローティングウィンドウの問題点(ビューは添付)
なぜ「ビューの判断はアタッチである」と言うのは、フローティングウィンドウの問題点です。
Android10より前
WinodwManagerは、addViewおよびremoveView操作を提供します。クエリインターフェイスはありません。ビューが追加されているかどうかを判断する方法は、ビューをアタッチすることだけです。「ウィンドウマネージャに接続されていないビュー」のソースコードログを追加すると、開発者は、View.isAttachedToWindowで判断することにより、使用中のリスクを調査することなく、ビューがウィンドウ上にあるかどうかを判断できると確信できます。
残念ながら、他の優れたAPIがなければ、View.isAttachedToWindowを使用することがほとんど唯一のオプションになりました。
Android 10は新しいAPIをプッシュし、開発者により良い選択肢を提供します。
ケースを使用してこのAPIを説明し、この問題点を解決してください。
ケースレビュー
フローティングウィンドウの使用
windowManager.addView(view, layoutParams);//新增窗口
windowManager.removeView(view);移除窗口
复制代码
ただし、追加または削除するとクラッシュが発生しやすくなります。ログは次のとおりです。
例:同じビューが2回追加されます
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)
复制代码
アタッチせずにウィンドウを削除する
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)
复制代码
通常の解決策:ログプロンプトが添付されていない-> 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();
}
}
复制代码
発生した問題
上記のコードを使用しますが、プロジェクトでクラッシュする可能性はまだ非常に低いです。
つまり、isAttachedToWindow≠ビューはウィンドウの追加です
問題を解く
-
オプション1:
应用中给View标志位,表示view 是否被add。但是总觉得本地标识,没有系统api准确,该方案不算太好。偶现的问题,必然有必现的过程,后面进行了深入的分析。
项目中遇到的问题是:同一个view,windowManager.addView 被连续执行了两次,也不是异步造成的。
最后发现,两次调用时间间隔极短,isAttachedToWindow 还没有来得及改变。
经详细调查有了方案二。
-
方案二:
使用 WindowInspector,此类在Android 10 framework 才增加,且只有getGlobalWindowViews 这一个静态方法
view.isAttachedToWindow() 改为 WindowInspector.getGlobalWindowViews().contains(view) 复制代码
源码分析
getGlobalWindowViews
看下主要类的调用关系,以及主要的方法
/**
* 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的范围是应用级别的,获取不到其他应用的窗口。