内容简介:作者:爱写代码的何蜀黍链接: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优雅地处理日常开发中关于时间处理的...
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Pro Django
Marty Alchin / Apress / 2008-11-24 / USD 49.99
Django is the leading Python web application development framework. Learn how to leverage the Django web framework to its full potential in this advanced tutorial and reference. Endorsed by Django, Pr......一起来看看 《Pro Django》 这本书的介绍吧!