内容简介:作者:爱写代码的何蜀黍链接:https://www.jianshu.com/p/256103c59d45
code小生 一个专注大前端领域的技术平台 公众号回复 Android
加入安卓技术群
作者:爱写代码的何蜀黍
链接:https://www.jianshu.com/p/256103c59d45
声明:本文已获 爱写代码的何蜀黍
授权发表,转发等请联系原作者授权
7.x版本,对Toast添加了Token验证,这本是对的,但是调用show()显示Toast时,如果有耗时操作卡住了主线程超过5秒,就会抛出BadTokenException的异常,而8.x系统开始,Google则在内部进行了try-catch。而7.x系统则是永久的痛,只能靠我们自己来修复了。
修复方案一
反射代理View的Context,Context内进行try-catch,处理Toast的BadTokenException问题
-
BadTokenListener,Toast抛出BadTokenException监听器
public interface BadTokenListener { /** * 当Toast抛出BadTokenException时回调 * * @param toast 发生异常的Toast实例 */ void onBadTokenCaught(@NonNull Toast toast); }
-
SafeToastContext,包裹Toast使用的Context
public class SafeToastContext extends ContextWrapper { private Toast mToast; private BadTokenListener mBadTokenListener; public SafeToastContext(Context base, Toast toast) { super(base); mToast = toast; } public void setBadTokenListener(@NonNull BadTokenListener badTokenListener) { mBadTokenListener = badTokenListener; } @Override public Context getApplicationContext() { //代理原本的Context return new ApplicationContextWrapper(super.getApplicationContext()); } private class ApplicationContextWrapper extends ContextWrapper { public ApplicationContextWrapper(Context base) { super(base); } @Override public Object getSystemService(String name) { if (Context.WINDOW_SERVICE.equals(name)) { //获取原来的WindowManager,交给WindowManagerWrapper代理,捕获BadTokenException异常 Context baseContext = getBaseContext(); return new WindowManagerWrapper((WindowManager) baseContext.getSystemService(name)); } return super.getSystemService(name); } } private class WindowManagerWrapper implements WindowManager { /** * 被包裹的WindowManager实例 */ private WindowManager mImpl; public WindowManagerWrapper(@NonNull WindowManager readImpl) { mImpl = readImpl; } @Override public Display getDefaultDisplay() { return mImpl.getDefaultDisplay(); } @Override public void removeViewImmediate(View view) { mImpl.removeViewImmediate(view); } @Override public void addView(View view, ViewGroup.LayoutParams params) { //在addView动刀,捕获BadTokenException异常 try { mImpl.addView(view, params); } catch (BadTokenException e) { e.printStackTrace(); if (mBadTokenListener != null) { mBadTokenListener.onBadTokenCaught(mToast); } } } @Override public void updateViewLayout(View view, ViewGroup.LayoutParams params) { mImpl.updateViewLayout(view, params); } @Override public void removeView(View view) { mImpl.removeView(view); } } }
-
ToastCompat,替代Toast的门面
public class ToastCompat extends Toast { private Toast mToast; public ToastCompat(Context context, Toast toast) { super(context); mToast = toast; } public static ToastCompat makeText(Context context, CharSequence text, int duration) { @SuppressLint("ShowToast") Toast toast = Toast.makeText(context, text, duration); setContextCompat(toast.getView(), context); return new ToastCompat(context, toast); } public static Toast makeText(Context context, @StringRes int resId, int duration) throws Resources.NotFoundException { return makeText(context, context.getResources().getText(resId), duration); } public @NonNull ToastCompat setBadTokenListener(@NonNull BadTokenListener listener) { final Context context = getView().getContext(); if (context instanceof SafeToastContext) { ((SafeToastContext) context).setBadTokenListener(listener); } return this; } @Override public void show() { mToast.show(); } @Override public void setDuration(int duration) { mToast.setDuration(duration); } @Override public void setGravity(int gravity, int xOffset, int yOffset) { mToast.setGravity(gravity, xOffset, yOffset); } @Override public void setMargin(float horizontalMargin, float verticalMargin) { mToast.setMargin(horizontalMargin, verticalMargin); } @Override public void setText(int resId) { mToast.setText(resId); } @Override public void setText(CharSequence s) { mToast.setText(s); } @Override public void setView(View view) { mToast.setView(view); setContextCompat(view, new SafeToastContext(view.getContext(), this)); } @Override public float getHorizontalMargin() { return mToast.getHorizontalMargin(); } @Override public float getVerticalMargin() { return mToast.getVerticalMargin(); } @Override public int getDuration() { return mToast.getDuration(); } @Override public int getGravity() { return mToast.getGravity(); } @Override public int getXOffset() { return mToast.getXOffset(); } @Override public int getYOffset() { return mToast.getYOffset(); } @Override public View getView() { return mToast.getView(); } @NonNull public Toast getBaseToast() { return mToast; } /** * 反射设置View中的Context,Toast会获取View的Context来获取WindowManager */ private static void setContextCompat(@NonNull View view, @NonNull Context context) { //7.1.1版本才进行处理 if (Build.VERSION.SDK_INT == 25) { try { Field field = View.class.getDeclaredField("mContext"); field.setAccessible(true); field.set(view, context); } catch (Throwable e) { e.printStackTrace(); } } } }
-
使用ToastCompat代替Toast
ToastCompat.makeText(context,"我是Toast的内容", Toast.LENGTH_SHORT) .setBadTokenListener(toast -> Logger.d("Toast:" + toast + " => 抛出BadTokenException异常") ) .show();
修复方案二
对Toast进行Hook,替换Toast中TN对象的Handler,对分发消息dispatchMessage()方法进行try-catch
public class SafeToast { private static Field sField_TN; private static Field sField_TN_Handler; static { try { //反射获取TN对象 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) { e.printStackTrace(); } } private SafeToast() { } @SuppressLint("ShowToast") public static void show(Context context, CharSequence message, int duration) { show(context, message, duration, null); } @SuppressLint("ShowToast") public static void show(Context context, CharSequence message, int duration, BadTokenListener badTokenListener) { Toast toast = Toast.makeText(context.getApplicationContext(), message, duration); hook(toast, badTokenListener); toast.setDuration(duration); toast.setText(message); toast.show(); } @SuppressLint("ShowToast") public static void show(Context context, @StringRes int resId, int duration) { show(context, resId, duration, null); } @SuppressLint("ShowToast") public static void show(Context context, @StringRes int resId, int duration, BadTokenListener badTokenListener) { Toast toast = Toast.makeText(context.getApplicationContext(), resId, duration); hook(toast, badTokenListener); toast.setDuration(duration); toast.setText(context.getString(resId)); toast.show(); } private static void hook(Toast toast, BadTokenListener badTokenListener) { try { Object tn = sField_TN.get(toast); Handler originHandler = (Handler) sField_TN_Handler.get(tn); sField_TN_Handler.set(tn, new SafeHandler(toast, originHandler, badTokenListener)); } catch (Throwable e) { e.printStackTrace(); } } /** * 替换Toast原有的Handler */ private static class SafeHandler extends Handler { private Toast mToast; private Handler mOriginImpl; private BadTokenListener mBadTokenListener; SafeHandler(Toast toast, Handler originHandler, BadTokenListener badTokenListener) { mToast = toast; mOriginImpl = originHandler; mBadTokenListener = badTokenListener; } @Override public void dispatchMessage(Message msg) { //对分发Message的处理方法进行try-catch try { super.dispatchMessage(msg); } catch (WindowManager.BadTokenException e) { e.printStackTrace(); if (mBadTokenListener != null) { mBadTokenListener.onBadTokenCaught(mToast); } } } @Override public void handleMessage(Message msg) { //需要委托给原Handler执行 mOriginImpl.handleMessage(msg); } } }
-
使用SafeToast代替Toast
SafeToast.show(context,"我是Toast的内容", Toast.LENGTH_SHORT, toast -> Logger.d("Toast:" + toast + " => 抛出BadTokenException异常"));
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 自然语言处理之数据预处理
- Python数据处理(二):处理 Excel 数据
- 什么是自然语处理,自然语言处理主要有什么
- 集群故障处理之处理思路以及健康状态检查(三十二)
- Spark 持续流处理和微批处理的对比
- Android(Java)日期和时间处理完全解析——使用Gson和Joda-Time优雅地处理日常开发中关于时间处理的...
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
RGB转16进制工具
RGB HEX 互转工具
XML 在线格式化
在线 XML 格式化压缩工具