简单的修复方案,不用反射,也不用编译期修改代码
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.SafeToastServiceTN.handleShow(Toast.java:502)
at android.widget.Toast2.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)
}
}
}
网友评论