从android 4.0到 9.0有不少做app的人遇到这个问题,google还没解决
目前在android 9上继承Dialog类的有8个类,app用得最多的是AlertDialog
现有看到的解决方案只是在app应用端规避,在创建Dialog(或者它的子类)时,保存句柄,在activity的生命周期函数onDestroy函数中调用句柄的dismiss函数
这个解法可以把crash概率降低1000倍,因为调整了时序逻辑
但还需要反思的有两个问题
1、在onDestroy管理所有dialog句柄,这样冗余的代码不可以优化吗?
2、应用层没办法trycatch这个由view抛出的crash,那么这个crash报得是否合理?
都有答案
1、Activity类里有一个变量mManageDialogs。管理着所有dialog的句柄,在onDestroy函数里,有遍历它然后把所有dialog调用一下dismiss。现在这个变量已经废弃了,因为自从android4.0版本,google把showDialog这个函数标志为废弃,所以Dialog句柄没有被添加到这个mManageDialogs里。
可以优化,可以做这两步:
a)可以在activity添加一个包方法(不写private\public\protect)addDialog(Dialog dialog),把传进来的dialog加到mManageDialogs里。
b)在Dialog类的show方法里,调用Activity类的addDialog方法,(Activity)mContext.addDialog(this),把dialog自己加到mManageDialogs里。
2、WindowManagerGlobal类的removeView函数里,在synchronized(mLock)后的第一件事是findViewLocked(view,true),这里就是报错的地方,意图是,我这个线程正准备向底层传达释放资源的信号,但是我却找不到我之前被加入到列表的痕迹,如果找不到的话,我就没办法释放底层相关的资源,会造成资源泄漏的,必须报出异常。
调用removeView函数时传进来的view其实有三种情形。第一,这个view之前从来没有被add过,没有底层资源。第二,这个view被add过然后已经操作过一次remove了。第三,这个view被add过,但没有被remove,但是现在在管理列表又找不到了。这个crash把三种情况都囊括了,不合理。除了第三种情况,前两种都不应crash。
可以优化,可以做这两步:
a)view里添加一个int变量 mDoneaddtoList ,初始值为0
b)WindowManagerGlobal里有三个函数要添加逻辑,addView/removeView/doRemoveView
frameworks/base优化参考如下
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
@@ -5054,6 +5054,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private ContentCaptureSession mCachedContentCaptureSession;
/**
+ * only use by WindowManagerGlobal
+ * set 1 when addView
+ * set 2 when doRemoveView
+ * check when removeView
+ */
+ int mDoneaddtoList = 0;
+
+ /**
* Simple constructor to use when creating a view from code.
*
* @param context The Context the view is running in, through which it can
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -397,6 +397,7 @@ public final class WindowManagerGlobal {
view.setLayoutParams(wparams);
mViews.add(view);
+ view.mDoneaddtoList = 1;
mRoots.add(root);
mParams.add(wparams);
@@ -447,6 +448,10 @@ public final class WindowManagerGlobal {
}
synchronized (mLock) {
+ if(view.mDoneaddtoList == 2){
+ Log.d(TAG, "the view had been removed, no need to remove againt");
+ return;
+ }
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
removeViewLocked(index, immediate);
@@ -527,6 +532,7 @@ public final class WindowManagerGlobal {
mRoots.remove(index);
mParams.remove(index);
final View view = mViews.remove(index);
+ view.mDoneaddtoList = 2;
mDyingViews.remove(view);
}
}