Android7.1.1系统,Toast的Exception: android.view.WindowManager$BadTokenException解决
最近在测谷歌支付的时候,7.1.1 系统版本,我发现问题很多,比如 有个接口字段用了 base64,但是我添加包的时候用的 java.util.base64 结果会崩溃报错,网上搜索后要用 android.util.base64 来解决,
今天在测谷歌支付的时候,发现突然有崩溃,看了下崩溃信息
at android.view.ViewRootImpl.setView(ViewRootImpl.java:806)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:369)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:94)
at android.widget.Toast$TN.handleShow(Toast.java:459)
at android.widget.Toast$TN$2.handleMessage(Toast.java:342)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:186)
at android.app.ActivityThread.main(ActivityThread.java:6491)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:914)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:804)
一下解决办法是我网上转过来的,经过自测后,解决了我的问题,我这边记录
这个 BadTokenException 是由谷歌系统产生的 BUG,只有在系统 7.x 会产生,谷歌团队已经在 8.0 以上系统修复,但对于开发者来说面对 Android 的 7.x 系统的用户不得不去解决这个 BUG
BadTokenException 产生的原因及谷歌的解决方案
在 Toast.show()之后,UI 线程做了耗时的操作阻塞了 Handlermessage 的处理。7.X 系统对 TYPE_TOAST 的 Window 类型做了超时限制,绑定了 WindowToken,如果 UI 在这段时间内没有执行完,Toast.show()内部的 handlermessage 得不到执行,NotificationManageService 那端会把这个 Toast 取消掉,同时把 Toast 对于的 windowtoken 置为无效。等 App 端真正需要显示 Toast 时,因为 windowtoken 已经失效,ViewRootImpl 就抛出了上面的异常。
我们看一下谷歌源码是如何解决的,8.0 以上系统 Toast 的 handleShow 方法里面的一段代码,mWM.addView(mView, mParams),是被 try catch 了,在 7.x 的系统是没有被 catch 住的(这里就不贴代码了)
看到谷歌的解决方案,所以我们 7.x 的解决方案其实也是把异常给 catch 住。那如何 catch 住呢?我们看 Toast 的源码时里面有个 mHandler 的成员变量,我们想办法把分发消息的 dispatchMessage(Message msg) 异常捕获不就可以了吗?这个其实和谷歌的解决方案一样。
我们把 dispatchMessage 给 catch 住,handleMessage 处理的消息具体实现还是原有的 handler 处理逻辑,不去影响原有的处理逻辑,我们只是 catch 住异常防止应用崩溃。
完整实现类如下:
* create by wuchu
* 解决部分7.1.1手机崩溃Toast解决方案
*/
public class ZtAppCompatToast {
private static Field sField_TN;
private static Field sField_TN_Handler;
private Toast mToast;
static {
//安卓7.0的做处理,其它版本系统的不用处理
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1 && Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
try {
sField_TN = Toast.class.getDeclaredField("mTN");
sField_TN.setAccessible(true);
sField_TN_Handler = sField_TN.getType().getDeclaredField("mHandler");
sField_TN_Handler.setAccessible(true);
} catch (Exception e) {
}
}
}
public ZtAppCompatToast(Context context) {
mToast = new Toast(context);
setHook(mToast);
}
public Toast getToast() {
return mToast;
}
public static Toast makeText(Context context,
CharSequence text, int duration) {
Toast toast = Toast.makeText(context, text, duration);
setHook(toast);
return toast;
}
public static Toast makeText(Context context,
int textSrc, int duration) {
Toast toast = Toast.makeText(context, textSrc, duration);
setHook(toast);
return toast;
}
private static void setHook(Toast toast) {
//安卓7.0的做处理,其它版本系统的不用处理
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1 && Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
hook(toast);
}
}
/*****
*
* @param toast
*/
private static void hook(Toast toast) {
try {
Object tn = sField_TN.get(toast);
Handler preHandler = (Handler) sField_TN_Handler.get(tn);
sField_TN_Handler.set(tn, new SafelyHandlerWarpper(preHandler));
} catch (Exception e) {
}
}
/****
* 自定义Handler catch处理异常
*/
public static class SafelyHandlerWarpper extends Handler {
private Handler impl;
public SafelyHandlerWarpper(Handler impl) {
this.impl = impl;
}怀化纱厂科比
2
2
0
专栏目录
android系统
————————————————
版权声明:本文为CSDN博主「怀化纱厂科比」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yangbin0513/article/details/124098348
@Override
public void dispatchMessage(Message msg) {
try {
super.dispatchMessage(msg);
} catch (Exception e) {
}
}
@Override
public void handleMessage(Message msg) {
impl.handleMessage(msg);//需要委托给原Handler执行
}
}
}