In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-02 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article mainly explains "what are the Toast problems of Android". The content of the explanation is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "what are the Toast problems of Android".
1. Problems with exceptions and occasional non-display
When you call Toast's API in your program, you may see Toast execution exceptions like this in the background:
Android.view.WindowManager$BadTokenException Unable to add window-- token android.os.BinderProxy@7f652b2 is not valid; is your activity running? Android.view.ViewRootImpl.setView (ViewRootImpl.java:826) android.view.WindowManagerGlobal.addView (WindowManagerGlobal.java:369) android.view.WindowManagerImpl.addView (WindowManagerImpl.java:94) android.widget.Toast$TN.handleShow (Toast.java:459)
In addition, on some systems, you don't see anything unusual, but there is a problem that the Toast does not display properly. To explain the causes of the above problems, we need to read the source code of Toast first.
2. Show and hide Toast
First, the view display of all Android processes depends on a window. And this window object is recorded in our WindowManagerService (hereinafter referred to as WMS) core service. WMS is a core service dedicated to managing application windows. When the Android process needs to build a window, you must specify the type of window. The display of Toast also depends on a window, and the type specified is:
Public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;// system window
As you can see, Toast is a system window, which ensures that Toast can be displayed on top of the window where Activity is located, and can be displayed on the upper layer of other applications. So, there's a question:
"if it is a system window, why does an ordinary application process have permission to generate such a window?"
As a matter of fact, the Android system has used a trick to "change the day" here. Let's first take a look at the whole process of Toast from showing to hiding:
/ / code Toast.javapublic void show () {if (mNextView = = null) {throw new RuntimeException ("setView must have been called");} INotificationManager service = getService (); / / call the system's notification service String pkg = mContext.getOpPackageName (); TN tn = mTN;// local binder tn.mNextView = mNextView; try {service.enqueueToast (pkg, tn, mDuration) } catch (RemoteException e) {/ / Empty}}
We can see from the code that when Toast is in show, the request is placed in the queue managed by NotificationManager, and in order to ensure that NotificationManager can interact with the process, a Binder object of type TN is passed to the NotificationManager system service. In NotificationManager system services:
/ / code NotificationManagerServicepublic void enqueueToast (...) {.... Synchronized (mToastQueue) {. {/ / Limit the number of toasts that any given package except the android / / package can enqueue. Prevents DOS attacks and deals with leaks. If (! isSystemToast) {int count = 0; final int N = mToastQueue.size (); for (int item0; I = MAX_PACKAGE_NOTIFICATIONS) {/ / upper limit judgment return }} Binder token = new Binder (); mWindowManagerInternal.addWindowToken (token, WindowManager.LayoutParams.TYPE_TOAST) / / generate a Toast window record = new ToastRecord (callingPid, pkg, callback, duration, token); mToastQueue.add (record); index = mToastQueue.size ()-1; keepProcessAliveIfNeededLocked (callingPid);}. If (index = = 0) {showNextToastLocked (); / / if there is no toast currently, display the current toast}} finally {Binder.restoreCallingIdentity (callingId);}
(do not delve into the details of other code, if you are interested, you can study it by yourself and pick out the relevant parts of Toast display that we are concerned about.)
We will get the following process (in the process where the NotificationManager system service is located):
Determine whether the number of Toast popped up by the current process has exceeded the upper limit MAX_PACKAGE_NOTIFICATIONS. If so, return it directly.
Generate a system window of type TOAST and add it to WMS management
Record the Toast request as a ToastRecord object
At this point in the code, we have seen how Toast has changed the day. In fact, the required system window token is generated by our NotificationManager system service. Because the system service has high permissions, of course there will be no permission problems. However, we will have a second problem:
Now that you have generated the Token object for this window, how do you pass it to the Android process and tell the process to display the interface?
We know that Toast not only has windows, but also has timing. With timing, we can have Toast displayed in the order in which we call it. And this timing control naturally falls on our NotificationManager service. We can see from the above code that when the system does not have a Toast, it will display the next Toast by calling the showNextToastLocked (); function.
Void showNextToastLocked () {ToastRecord record = mToastQueue.get (0); while (record! = null) {. Try {record.callback.show (record.token); / / Notification process displays scheduleTimeoutLocked (record); / / timeout listening message return;} catch (RemoteException e) {...}
Here, the showNextToastLocked function will call the show method of the callback member of ToastRecord to notify the process display, so what is callback?
Binder proxy object for final ITransientNotification callback;//TN
When we see the declaration of callback, we can see that it is an object of type ITransientNotification, and this object is actually a proxy object of what we just called an object of type TN:
Private static class TN extends ITransientNotification.Stub {...}
What about the parameter record.token that needs to be passed in the show method of the callback object? It is actually the token of the window generated by the NotificationManager service we just mentioned.
I believe you are already familiar with the Binder mechanism of Android. When we call the show method of the TN proxy object, it is equivalent to RPC calling the show method of TN. Let's take a look at the TN code:
/ / code TN.javafinal Handler mHandler = new Handler () {@ Override public void handleMessage (Message msg) {IBinder token = (IBinder) msg.obj; handleShow (token); / / the processing interface displays}}; @ Override public void show (IBinder windowToken) {if (localLOGV) Log.v (TAG, "SHOW:" + this) MHandler.obtainMessage (0, windowToken) sendToTarget ();}
At this point, TN receives the notification of the show method and will post a message with a command of 0 through the mHandler object. In fact, it is a message that displays the window. Finally, the handleShow (Binder) method will be called:
Public void handleShow (IBinder windowToken) {if (localLOGV) Log.v (TAG, "HANDLE SHOW:" + this + "mView=" + mView + "mNextView=" + mNextView); if (mView! = mNextView) {. MWM = (WindowManager) context.getSystemService (Context.WINDOW_SERVICE); MParams.token = windowToken;... MWM.addView (mView, mParams);...}}
The method of displaying the window is very simple, that is, the passed window token is assigned to the window property object mParams, and then the mView object in Toast is brought into the management of WMS by calling the WindowManager.addView method.
Above we explained how the NotificationManager service passes the window token to the Android process and how the Android process is displayed. As we just mentioned, NotificationManager is not only in charge of the generation of Toast, but also manages the timing control of Toast. So we need to travel through time and space and go back to NotificationManager's showNextToastLocked () method. You can see that after calling the callback.show method, another scheduleTimeoutLocked method is called:
Record.callback.show (record.token); / / notify the process to display scheduleTimeoutLocked (record); / / timeout listening messages
This method is used to manage Toast timing:
Private void scheduleTimeoutLocked (ToastRecord r) {mHandler.removeCallbacksAndMessages (r); Message m = Message.obtain (mHandler, MESSAGE_TIMEOUT, r); long delay = r.duration = = Toast.LENGTH_LONG? LONG_DELAY: SHORT_DELAY; mHandler.sendMessageDelayed (m, delay);}
ScheduleTimeoutLocked implements timed calls by calling the sendMessageDelayed function of Handler, and the implementation class of this mHandler object is an inner class called WorkerHandler:
Private final class WorkerHandler extends Handler {@ Override public void handleMessage (Message msg) {switch (msg.what) {case MESSAGE_TIMEOUT: handleTimeout ((ToastRecord) msg.obj); break;. } private void handleTimeout (ToastRecord record) {synchronized (mToastQueue) {int index = indexOfToastLocked (record.pkg, record.callback); if (index > = 0) {cancelToastLocked (index);}
When WorkerHandler processes MESSAGE_TIMEOUT messages, the handleTimeout (ToastRecord) function is called, and the handleTimeout (ToastRecord) function, after searching, calls the cancelToastLocked function to cancel the display of Toast:
Void cancelToastLocked (int index) {ToastRecord record = mToastQueue.get (index);. Record.callback.hide (); / / call hide remotely to tell the client to hide the window. ToastRecord lastToast = mToastQueue.remove (index); mWindowManagerInternal.removeWindowToken (lastToast.token, true); / / remove the window Token generated for Toast from the WMS service.
The cancelToastLocked function will do two things:
Call the ITransientNotification.hide method remotely, telling the client to hide the window
Remove the window Token generated for Toast from the WMS service
Above, we analyzed the display and hiding of a Toast from the perspective of source code. We might as well take a closer look at the idea. The display and hiding of Toast are roughly divided into the following core steps:
When Toast calls the show method, it actually brings itself into the Toast management of NotificationManager, passing a local TN type or a Binder object of ITransientNotification.Stub
When NotificationManager receives a display request from Toast, it generates a Binder object and adds it to the WMS object as a token of a window, and the type is TOAST
NotificationManager passes the window token to the remote TN object through the show method of ITransientNotification and throws a timeout listening message scheduleTimeoutLocked
After receiving the message, the TN object will post the message to the Handler object, and then call the display processing function to add the View in Toast to the WMS management, and the Toast window displays
The WorkerHandler of NotificationManager receives the MESSAGE_TIMEOUT message, and the NotificationManager remote calling process hides the Toast window, and then deletes the window token from the WMS
3. The cause of the exception
Above we analyzed the Toast display and hidden source code flow, so why is there a display exception? Let's take a look at what this anomaly is.
Unable to add window-- token android.os.BinderProxy@7f652b2 is not valid; is your activity running? Android.view.ViewRootImpl.setView (ViewRootImpl.java:826) android.view.WindowManagerGlobal.addView (WindowManagerGlobal.java:369)
First of all, this exception occurs when the Toast is displayed because the token fails. So why did token fail?
In general, according to the normal process, this exception does not occur. However, in some cases, a message from a UI thread of the Android process is blocked. Causes the show method of TN to post the 0 (display) message after the message and does not execute it. At this point, the timeout detection of NotificationManager ends and the token record in the WMS service is deleted. That is, as shown in the figure, the token deletion occurs before the Android process show method. This leads to the anomaly above us. Let's write a piece of code to test it:
Public void click (View view) {Toast.makeText (this, "test", Toast.LENGTH_SHORT). Show (); try {Thread.sleep (10000);} catch (InterruptedException e) {e.printStackTrace ();}}
We first call the Toast.show method, and then sleep 10 seconds in the ui thread message. When the process exits abnormally, we can intercept their logs and get:
12-28 11 Unable to add window 10 token android.os.BinderProxy@2e5da2c is not valid 30.086 24599 24599 E AndroidRuntime: android.view.WindowManager$BadTokenException: Unable to add window-- Is your activity running?12-28 11 ViewRootImpl.java:679 10 at android.view.ViewRootImpl.setView 24599 24599 E AndroidRuntime: at android.view.ViewRootImpl.setView (ViewRootImpl.java:679) 12-28 11 11 V 10 at android.view.ViewRootImpl.setView 30.086 24599 24599 E AndroidRuntime: at android.view.WindowManagerGlobal.addView (WindowManagerGlobal.java:342) 12-28 11 V 10 AndroidRuntime: 30.086 24599 24599 E AndroidRuntime: at android.view.WindowManagerImpl.addView (WindowManagerImpl.java:93) 12-28 11 L 10 V 30.086 24599 24599 E AndroidRuntime: at android .widget.Toast $TN.handleShow (Toast.java:434) 12-28 1115 10 purl 30.086 24599 24599 E AndroidRuntime: at android.widget.Toast$TN$2.handleMessage (Toast.java:345)
As we expected, we recreated the stack of the problem. Then you may have the following questions:
Is it useful to add try-catch to the Toast.show method?
Of course not. According to our source code analysis, the exception occurs in our next UI thread message, so it doesn't make sense for us to add try-catch to the last ui thread message.
Why is it that some systems don't have this exception, but sometimes toast doesn't show it?
What we analyzed above is the code of 7.0. in the code of 8.0, the handleShow in Toast has changed:
/ / code handleShow () android 8.0 try {mWM.addView (mView, mParams); trySendAccessibilityEvent ();} catch (WindowManager.BadTokenException e) {/ * ignore * /}
In the 8.0 code, the mWM.addView is try-catch wrapped, so no exception is thrown, but the Toast is not displayed due to execution failure
What are the causes of this problem?
The cause of this problem is not necessarily Catton. When your TN throws a message, there are a large number of UI thread messages waiting for execution. Although each UI thread message is not stuttered, if the sum exceeds the NotificationManager timeout, there will still be a problem.
The UI thread performs a very time-consuming operation, such as loading pictures, a large number of floating-point operations, etc., for example, this is the case we simulated with sleep above.
In some cases, the process leaves the background or stops the screen, and the system reduces the cpu time allocated to the process in order to reduce power or for some reason, so that the instructions in the process can not be executed in time, which will also cause the process to look "stuttered".
Thank you for your reading, the above is the content of "what are the Toast problems of Android". After the study of this article, I believe you have a deeper understanding of what the Toast problems of Android have, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.