美文网首页android
Toast BadTokenException 简单的修复方案

Toast BadTokenException 简单的修复方案

作者: WLHere | 来源:发表于2020-06-28 15:04 被阅读0次

    简单的修复方案,不用反射,也不用编译期修改代码
    Demo地址:https://github.com/WLHere/SafeToast

    Toast在android 7.1.1经常有如下崩溃

    android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@864f33a is not valid; is your activity running?

    at android.view.ViewRootImpl.setView(ViewRootImpl.java:683)
    at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:342)
    at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:94)
    at com.wuba.town.util.SafeToastServiceWindowManagerWrapper.addView(SafeToastService.kt:62) at android.widget.ToastTN.handleShow(Toast.java:502)
    at android.widget.ToastTN2.handleMessage(Toast.java:381)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:6211)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:793)

    问题根源:NotificationManagerService通知Toast show后,会发送一个延迟消息(延迟2s或3.5s)取消token。如果因为UI线程卡顿导致Toast没有及时显示,待Toast显示时就会出现这个问题。

    下边介绍一种不用反射和编译动态修改代码的修复方案。
    Toast.TN.handleShow是从applicationContext获取WindowManager。我们重写Application的getSystemService方法,对于从Toast来的调用返回兼容的WindowManager,然后try catch add view方法。

    class App : Application() {
    
        override fun getSystemService(name: String): Any? {
            return SafeToastService.getSystemService(name, super.getSystemService(name))
        }
    }
    
    object SafeToastService {
        private var mWindowManager: WindowManager? = null
    
        fun getSystemService(name: String, baseService: Any?): Any? {
            if (Build.VERSION.SDK_INT <= 25) {// 兼容android 7.1.1 toast崩溃问题
                if (name == Context.WINDOW_SERVICE && callFromToast()) {
                    if (mWindowManager == null) {
                        mWindowManager = WindowManagerWrapper(baseService as WindowManager)
                    }
                    return mWindowManager
                }
            }
            return baseService
        }
    
        private fun callFromToast(): Boolean {
            var fromToast = false
            try {
                // android.widget.Toast$TN.handleShow
                val traces = Thread.currentThread().stackTrace
                if (traces != null) {
                    for (trace in traces) {
                        if ("android.widget.Toast\$TN" == trace.className && "handleShow" == trace.methodName) {
                            fromToast = true
                            break
                        }
                    }
                }
            } catch (ignored: Throwable) {
            }
            return fromToast
        }
    
        class WindowManagerWrapper(private val baseManager: WindowManager) : WindowManager {
            override fun getDefaultDisplay(): Display {
                return baseManager.defaultDisplay
            }
    
            override fun addView(view: View?, params: ViewGroup.LayoutParams?) {
                try {
                    baseManager.addView(view, params)
                } catch (e: WindowManager.BadTokenException) {
                    Log.w("bwl", "add window failed", e)
                }
            }
    
            override fun updateViewLayout(view: View?, params: ViewGroup.LayoutParams?) {
                baseManager.updateViewLayout(view, params)
            }
    
            override fun removeView(view: View?) {
                baseManager.removeView(view)
            }
    
            override fun removeViewImmediate(view: View?) {
                baseManager.removeViewImmediate(view)
            }
        }
    }
    

    相关文章

      网友评论

        本文标题:Toast BadTokenException 简单的修复方案

        本文链接:https://www.haomeiwen.com/subject/yyqwfktx.html