Toast BadTokenException 簡(jiǎn)單的修復(fù)方案

簡(jiǎn)單的修復(fù)方案,不用反射,也不用編譯期修改代碼
Demo地址:https://github.com/WLHere/SafeToast

Toast在android 7.1.1經(jīng)常有如下崩潰

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)

問(wèn)題根源:NotificationManagerService通知Toast show后,會(huì)發(fā)送一個(gè)延遲消息(延遲2s或3.5s)取消token。如果因?yàn)閁I線程卡頓導(dǎo)致Toast沒(méi)有及時(shí)顯示,待Toast顯示時(shí)就會(huì)出現(xiàn)這個(gè)問(wèn)題。

下邊介紹一種不用反射和編譯動(dòng)態(tài)修改代碼的修復(fù)方案。
Toast.TN.handleShow是從applicationContext獲取WindowManager。我們重寫(xiě)Application的getSystemService方法,對(duì)于從Toast來(lái)的調(diào)用返回兼容的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崩潰問(wèn)題
            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)
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容