...$wux.toast_如何解决Toast#handleShow()的WindowManager.BadTokenException?

...$wux.toast

“Every problem is a gift — without problems, we would not grow.” ― Anthony Robbins

“每个问题都是一份礼物,没有问题,我们就不会成长。” 安东尼·罗宾斯

When we have to display information for a short span of time in Android, we use Toast.

当我们不得不在Android中短时间显示信息时,可以使用Toast

Here are some key features:

以下是一些主要功能:

  • It provides simple feedback about an operation in a small popup.

    它在小弹出窗口中提供有关操作的简单反馈。
  • It is a view containing a quick little message for the user

    这是一个包含给用户的简短提示的视图
  • It only fills the amount of space required for the message and the current activity remains visible and interactive.

    它仅填充消息所需的空间量,并且当前活动保持可见且可交互。

Recently, I started getting a major number of crashes for Toast#handleShow() on Crashlytics. Here are the logs:

最近,我开始在Crashlytics上收到Toast#handleShow()的大量崩溃。 以下是日志:

Fatal Exception: android.view.WindowManager$BadTokenException
Unable to add window -- token android.os.BinderProxy@f839de9 is not valid; is your activity running?
android.view.ViewRootImpl.setView (ViewRootImpl.java:697)
android.view.WindowManagerGlobal.addView (WindowManagerGlobal.java:347)
android.view.WindowManagerImpl.addView (WindowManagerImpl.java:94)
android.widget.Toast$TN.handleShow (Toast.java:463)
android.widget.Toast$TN$2.handleMessage (Toast.java:346)
android.os.Handler.dispatchMessage (Handler.java:102)
android.os.Looper.loop (Looper.java:163)
android.app.ActivityThread.main (ActivityThread.java:6377)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:904)

Before the analysis, it's important for you to know about WindowManager.BadTokenException.

在进行分析之前,对您而言重要的是要了解WindowManager.BadTokenException。

Exception that is thrown when trying to add view whose LayoutParams LayoutParams#token is invalid.

尝试添加其LayoutParams LayoutParams#token无效的视图时引发的异常。

Lets, Look at the WindowManagerService.java file.

让我们看一下WindowManagerService.java文件。

Image for post

WindowManagerService (WMS) is a system service that manages the windows on Android.

WindowManagerService(WMS)是一项系统服务,用于管理Android上的窗口。

Window Tokens:

窗口令牌:

As the name suggest, a window token is a special type of Binder token used by window manager to uniquely identify a window in the system. Window tokens are important for security reasons because they make it impossible for malicious applications to draw on top of the windows of other applications. The window manager protects against this by requiring applications to pass their application’s window token as part of each request to add or remove a window. If the tokens don’t match, the window manager rejects the request and throws a BadTokenException. Without window tokens, this necessary identification step wouldn’t be possible and the window manager wouldn’t be able to protect itself from malicious applications.

顾名思义,窗口令牌是窗口管理器用来在系统中唯一标识窗口的一种特殊类型的Binder令牌。 出于安全考虑,窗口令牌很重要,因为它们使恶意应用程序无法在其他应用程序的窗口上绘制。 窗口管理器通过要求应用程序将其应用程序的窗口令牌作为每个添加或删除窗口的请求的一部分来传递,从而防止这种情况的发生。 如果令牌不匹配,则窗口管理器拒绝该请求并抛出 BadTokenException 没有窗口令牌,将无法执行此必要的识别步骤,并且窗口管理器将无法保护自己免受恶意应用程序的攻击。

A real-world scenario:

真实场景:

When an application starts up for the first time, the ActivityManagerService creates a special kind of window token called an application window token, which uniquely identifies the application’s top-level container window. The activity manager gives this token to both the (*application) and the window manager, and it sends the token to the window manager each time it wants to add a new window to the screen. This ensures secure interaction between the application and the window manager (by making it impossible to add windows on top of other applications), and also makes it easy for the activity manager to make direct requests to the window manager.

当应用程序首次启动时, ActivityManagerService将 创建一种特殊的窗口令牌,称为应用程序窗口令牌,它唯一地标识应用程序的顶级容器窗口。 活动管理器将此令牌同时提供给(* application)和窗口管理器,并且每次要将新窗口添加到屏幕时,活动管理器都会将该令牌发送给窗口管理器。 这样可以确保应用程序和窗口管理器之间的安全交互(通过使其无法在其他应用程序之上添加窗口),还可以使活动管理器轻松直接向窗口管理器发出请求。

And the code that throws “BadTokenException”

和引发“ BadTokenException”的代码

Return WindowManagerGlobal.ADD_BAD_APP_TOKEN

返回WindowManagerGlobal.ADD_BAD_APP_TOKEN

If returned the WindowManagerGlobal.ADD_BAD_APP_TOKEN, the exception occurs when the WMS.addWindow () function is called to check whether the window to be added is not in violation of the policy according to the android window policy.

如果返回WindowManagerGlobal.ADD_BAD_APP_TOKEN T他当WMS.addWindow()函数被调用,以检查是否要添加的窗口不违反根据Android的窗口策略的策略发生异常。

So now, after all, my research and analysis on BadTokenException, what I noticed about all the crashes there on Crashltyics:

所以,毕竟,现在,我对BadTokenException的研究和分析,使我注意到Crashltyics上的所有崩溃:

All were on Android 7.1 (API level 25).

所有这些都在Android 7.1 (API级别25)上。

I compared the API level 25 Toast class and others for the difference and I found an interesting thing there:

我比较了API 25级Toast类和其他类的区别,发现那里很有趣:

From API 25, Android added a new param IBinder windowToken for Toast#handleShow(), and It brought an exception. 😪

从API 25开始,Android为Toast#handleShow()添加了新的参数IBinder windowToken ,并带来了异常。 😪

Let me show you the code:

让我向您展示代码:

Image for post

As you can see here, they try-catch the mWM.addView(mView, mParams)

如您所见,它们会尝试捕获mWM.addView(mView, mParams)

Since the notification manager service cancels the token right after it notifies us to cancel the toast there is an inherent race and we may attempt to add a window after the token has been invalidated. Let us hedge against that.

由于通知管理器服务在通知我们取消祝酒后立即取消了令牌,因此存在固有的竞争,我们可能会尝试在令牌无效后添加一个窗口。 让我们对此进行对冲。

However, API level 25 is still at risk. We had to capture it on our own else it would have continued to produce an exception for the users.

但是, API级别25仍处于危险中 。 我们必须自己捕获它,否则它将继续为用户产生异常。

This exception occurs regardless of whether the Context you passed to Toast is an Activity or ApplicationContext or Service. And you can not try-catch it.

无论您传递给ToastContextActivity还是ApplicationContextService都将发生此异常。 而且您无法try-catch它。

Solution :

解决方案

So I created a library that possesses a ToastHandler class which is responsible for showing Toast smoothly on all API versions and replaced the base Context to a ToastContextWrapper, it will hook the WindowManagerWrapper.addView(view, params) method and fix the exception.

所以我创建了一个拥有ToastHandler类的库 它负责在所有API版本上平稳显示Toast并将基本Context替换为ToastContextWrapper ,它将钩上WindowManagerWrapper.addView(view, params)方法并修复异常。

You just need to add this in your app/build.gradle:

您只需要在app / build.gradle中添加它:

implementation 'com.toastfix:toastcompatwrapper:{latest_version}'

Now, you are all good to use your Toast :

现在,您都可以使用Toast了:

ToastHandler.INSTANCE.showToast(this, "Hi,I am Toast", Toast.LENGTH_LONG)

You can find the source code here:

您可以在此处找到源代码

That’s all from my side!!

这就是我的全部!!

Originally published here.

最初在这里发表。

Image for post

Do share your feedback and suggestions in the comments section below.

请在下面的评论部分中分享您的反馈和建议。

如果您喜欢此博客,请点击。 请继续关注下一个! (If you liked this blog, hit the 👏 . Stay tuned for the next one!)

翻译自: https://proandroiddev.com/how-i-resolved-windowmanager-badtokenexception-for-toast-handleshow-2308203ebb91

...$wux.toast

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值