Asynctask Trap: Handler,looper and MessageQueue of the detailed _android

Source: Internet
Author: User
Tags message queue prepare sleep
The Hidden Trap of Asynctask
Let's take a look at an example
This example is very simple, it shows the extreme use of asynctask, very strange.
Copy Code code as follows:

public class Asynctasktrapactivity extends activity {
Private Simpleasynctask Asynctask;
Private Looper Mylooper;
Private TextView status;

@Override
public void OnCreate (Bundle icicle) {
Super.oncreate (Icicle);
Asynctask = null;
New Thread (New Runnable () {
@Override
public void Run () {
Looper.prepare ();
Mylooper = Looper.mylooper ();
Status = New TextView (Getapplication ());
Asynctask = new Simpleasynctask (status);
Looper.loop ();
}
). Start ();
try {
Thread.Sleep (1000);
catch (Interruptedexception e) {
E.printstacktrace ();
}
Layoutparams params = new Layoutparams (layoutparams.fill_parent, layoutparams.fill_parent);
Setcontentview ((TextView) status, params);
Asynctask.execute ();
}

@Override
public void OnDestroy () {
Super.ondestroy ();
Mylooper.quit ();
}

Private class Simpleasynctask extends Asynctask<void, Integer, void> {
Private TextView Mstatuspanel;

Public Simpleasynctask (TextView text) {
Mstatuspanel = text;
}

@Override
protected void Doinbackground (void ... params) {
int prog = 1;
while (Prog < 101) {
Systemclock.sleep (1000);
Publishprogress (Prog);
prog++;
}
return null;
}

Not Okay, 'll crash, said it cannot touch TextView
@Override
protected void OnPostExecute (void result) {
Mstatuspanel.settext ("Welcome back.");
}

Okay, because it is called in #execute () which are called in main thread, so it runs in main thread.
@Override
protected void OnPreExecute () {
Mstatuspanel.settext ("Before we go, let me tell you something buried in my heart for Years ...");
}

Not okay, 'll crash, said it cannot touch TextView
@Override
protected void Onprogressupdate (Integer ... values) {
Mstatuspanel.settext ("On our way ..." + values[0].tostring ());
}
}
}

This example does not work properly in Android2.3 and will report an exception when executing onprogressupdate () and OnPostExecute ()



Copy Code code as follows:

11-03 09:13:10.501:e/androidruntime (762): FATAL exception:thread-10
11-03 09:13:10.501:e/androidruntime (762): Android.view.viewroot$calledfromwrongthreadexception:only the original Thread that created a view hierarchy can touch its views.
11-03 09:13:10.501:e/androidruntime (762): at Android.view.ViewRoot.checkThread (viewroot.java:2990)
11-03 09:13:10.501:e/androidruntime (762): at Android.view.ViewRoot.requestLayout (viewroot.java:670)
11-03 09:13:10.501:e/androidruntime (762): at Android.view.View.requestLayout (view.java:8316)
11-03 09:13:10.501:e/androidruntime (762): at Android.view.View.requestLayout (view.java:8316)
11-03 09:13:10.501:e/androidruntime (762): at Android.view.View.requestLayout (view.java:8316)
11-03 09:13:10.501:e/androidruntime (762): at Android.view.View.requestLayout (view.java:8316)
11-03 09:13:10.501:e/androidruntime (762): at Android.widget.TextView.checkForRelayout (textview.java:6477)
11-03 09:13:10.501:e/androidruntime (762): at Android.widget.TextView.setText (textview.java:3220)
11-03 09:13:10.501:e/androidruntime (762): at Android.widget.TextView.setText (textview.java:3085)
11-03 09:13:10.501:e/androidruntime (762): at Android.widget.TextView.setText (textview.java:3060)
11-03 09:13:10.501:e/androidruntime (762): at com.hilton.effectiveandroid.os.asynctasktrapactivity$ Simpleasynctask.onprogressupdate (asynctasktrapactivity.java:110)
11-03 09:13:10.501:e/androidruntime (762): at com.hilton.effectiveandroid.os.asynctasktrapactivity$ Simpleasynctask.onprogressupdate (asynctasktrapactivity.java:1)
11-03 09:13:10.501:e/androidruntime (762): at Android.os.asynctask$internalhandler.handlemessage (AsyncTask.java : 466)
11-03 09:13:10.501:e/androidruntime (762): at Android.os.Handler.dispatchMessage (handler.java:130)
11-03 09:13:10.501:e/androidruntime (762): at Android.os.Looper.loop (looper.java:351)
11-03 09:13:10.501:e/androidruntime (762): At Com.hilton.effectiveandroid.os.asynctasktrapactivity$1.run ( ASYNCTASKTRAPACTIVITY.JAVA:56)
11-03 09:13:10.501:e/androidruntime (762): at Java.lang.Thread.run (thread.java:1050)
11-03 09:13:32.823:E/DALVIKVM (762): [DVM] mmap return base = 4585e000

However, it is normal to run in Android4.0 and above (version 3.0 is not tested).



The reason for the stacktrace from the 2.3 Runtime is that the UI component is manipulated in a non-UI thread. No, Magic, Asynctask#onprogressupdate () and Asynctask#onpostexecute () documents clearly written that these two callbacks are in the UI thread, how to report such an exception!
Cause Analysis
Asynctask designed to perform asynchronous tasks but can communicate with the main thread, it has an internal internalhandler is inherited from the handler static member Shandler, this shandler is used to communicate with the main thread. Look at the declaration of this object: private static final Internalhandler Shandler = new Internalhandler (), and Internalhandler inherits from Handler. So essentially speaking Shandler is a handler object. Handler is used to communicate with threads, it must be used with looper and thread bindings, you must specify Looper when creating handler, if you do not specify a Looper object, use the thread where the call stack is located, and if the call stack thread does not looper it will report an exception. It seems that this shandler is bound to the thread that called the New Internalhandler () and it is statically private, which is bound to the thread that created the Asynctask object for the first time. Therefore, if the Asynctask object is created in the main thread, then its shandler is bound to the main thread, which is normal. In this example, Asynctask is created in the derivation line Chengri, so its shandler is bound to the derived thread, so it naturally cannot manipulate UI elements and throws an exception in Onprogressupdate () and OnPostExecute ().

The reason for the above example is that the Simpleasynctask object was created in the derived thread. As for why there is no problem with the 4.0 version, because the Bindapplication action is performed in the Activitythread.main () method in 4.0, the Asynctask object will be used, and the Shandler object will be created. This is the main thread so Shandler is bound to the main thread. When the Asynctask object is created later, it is not initialized again because the Shandler is already initialized. As to what is bindapplication, why does the bindapplication action not affect the discussion of this problem.
Defects of Asynctask and its modification method
This is actually a asynctask hidden bug, it should not be so dependent on developers, should impose conditions to ensure that the first time asynctask on Like it was created in the main thread:
1. It is obviously not a best practice to check whether the current thread is the primary and throw an exception in the Internalhandler construct.
Copy Code code as follows:

New Internalhandler () {

Copy Code code as follows:

if (Looper.mylooper ()!= looper.getmainlooper ()) {
throw new RuntimeException ("Asynctask must is initialized in main thread");
}

Copy Code code as follows:

11-03 08:56:07.055:e/androidruntime (890): FATAL exception:thread-10
11-03 08:56:07.055:e/androidruntime (890): Java.lang.ExceptionInInitializerError
11-03 08:56:07.055:e/androidruntime (890): at Com.hilton.effectiveandroid.os.asynctasktrapactivity$1.run ( ASYNCTASKTRAPACTIVITY.JAVA:55)
11-03 08:56:07.055:e/androidruntime (890): at Java.lang.Thread.run (thread.java:1050)
11-03 08:56:07.055:e/androidruntime (890): caused By:java.lang.RuntimeException:AsyncTask must is initialized in main th Read
11-03 08:56:07.055:e/androidruntime (890): at Android.os.asynctask$internalhandler.<init> (AsyncTask.java:455 )
11-03 08:56:07.055:e/androidruntime (890): at android.os.asynctask.<clinit> (asynctask.java:183)
11-03 08:56:07.055:e/androidruntime (890): ... 2 more

2. A better approach is to pass the mainlooper of the main thread to the Internalhandler construction
Copy Code code as follows:

New Intenthandler () {
Super (Looper.getmainlooper ());
}

Would anyone write that, would you ask? Normally, no one would intentionally create a asynctask in a derived thread. But if there is a class called worker, used to complete the asynchronous task of downloading pictures from the network, and then show that there is a workerscheduler to assign tasks, Workerscheduler is also running in a separate thread, the worker with Asynctask to achieve, Workscheduler creates a worker to complete the request when the request is received, and then it appears in the Workerscheduler thread---the derived thread---create the Asynctask object. This bug is extremely covert and hard to spot.
How to restrict the caller's thread
Normally a Java application is a process, and there is a thread, and the entry is the Main method. The Android application is essentially a Java application, its main entrance is Activitythread.main (), and Looper.preparemainlooper () is invoked in the main () method, which initializes the looper of the main thread. The Looper object mmainlooper with the main thread is stored in the Looper, and it also provides a way to obtain the Looper,getmainlooper () of the main thread. So if you need to create a Handler that is bound to the main thread, you can use the new Handler (Looper.getmainlooper ()) to ensure that it does bind to the main thread.
You can check the caller's Looper object if you want to ensure that some methods can only be invoked in the main thread:
Copy Code code as follows:

if (Looper.mylooper ()!= looper.getmainlooper ()) {
throw new RuntimeException ("This is the can only is called in Main thread");
}

handler,looper,messagequeue mechanism
Interactive collaboration between threads and threads
Although the shared memory space between threads and threads can access the heap space of the process, the thread has its own stack, and the method calls running in one thread are all in the thread's own call stack. In layman's terms, the western route is a run () method and the method it calls internally. All of the method calls in this case are independent of other threads, and because of the method invocation, a method calls another method, and another method is also in the caller's thread. So, threads are the concept of timing, essentially a list of method calls.
If you want to collaborate between threads, or if you want to change the thread on which a method is located (in order not to block its own thread), you can only send a message to another thread and then return, and the other thread will perform some action when it receives the message. If a simple operation can be identified with a variable, such as a thread owner needs a B-thread to do something, you can set the value of an object obj, B, when you see obj!= null to do things, this kind of thread interaction collaboration in the Java programming idea has a large number of examples.
Itc-inter Thread Communication in Android
Note: Of course handler can also be used as a message loop within a thread, without having to communicate with another thread, but the focus here is on things between threads.

One of the special limitations of Android is that a non main thread cannot manipulate UI elements, and an application is unlikely to create a derivative thread, so that communication between the main thread and the derived threads is necessary. Because this communication is frequent, it is not possible to identify all variables, and the program becomes very confusing. This is when Message Queuing becomes very necessary, which is to establish a message queue in each thread. When a needs B, a sends a message to B, which in essence adds the message to B's Message queue, where a return,b does not wait for a message, but loops through its message queue to see if there is a message to execute.

The basic idea of the whole ITC is to define a message object, put the required data into it, and define the method of handling the message as a callback in the message, and then send the message to another thread, and the other thread is looping through the messages in its queue. When you see a message, the callback that is attached to the message is invoked to process the message. As you can see, this is just a change in the execution sequence of processing messages: Normally it is handled on the spot, which is encapsulated into a message that is dropped to another thread, executed at an indeterminate time, and the other thread provides only the CPU timing, not interfering with what the message is and how the message is handled. In short, a method is put into another thread to invoke, and the call stack of the caller of the method ends, and the call stack of the method is transferred to another thread.
So what is the mechanism that has changed? From the above it is only to have one method (the processing of the message) arranged into another thread (asynchronous processing), not immediately synchronized, it changes the CPU's execution timing (execution sequence).
So where are the message queues stored? Can not be placed in heap space (direct new MessageQueue ()), so that the object's reference is easy to lose, for the thread is not easy to maintain. The Java support thread's local storage threadlocal, through Threadlocal objects can put objects into the thread space, each thread has its own object. Therefore, you can create a message queue for each thread that needs to communicate and place it in its local store.
Based on this model, it can be extended, such as prioritizing messages.



MessageQueue
Store messages in queues, mainly two operations one is row enqueuemessage, one is column next (), the need to ensure thread safety, because row is usually another thread in the call.
MessageQueue is a very close to the bottom of the mechanism, so it is not convenient for developers to use directly, in order to use this MessageQueue must do two aspects of work, one is the target line Cheng: Created, associated with the thread, running; the other is the client of the queue thread: creating the message, Defines callback processing, sending messages to queues. Looper and handler are the encapsulation of MessageQueue: Looper is for the target thread: The purpose is to create MessageQueue, associate MessageQueue with threads, and let MessageQueue run, and Looper has a protection mechanism that allows one thread to create only one MessageQueue object, while handler is for queue clients: used to create messages, define callbacks, and send messages.
Because the Looper object encapsulates the target queue thread and its queues, the Looper object represents a thread with a MessageQueue and a MessageQueue of the thread for the client of the queue thread. It also means that when you build a handler object, you use a Looper object, and you use Looper objects when you verify that a thread is an expected thread.
Looper Insider
The Looper task is to create the message queue MessageQueue, put it in the thread's threadlocal (associated with the thread), and let the MessageQueue run, in a ready state, and to provide an interface to stop the message loop. It has four main interfaces:
public static void Looper.prepare ()
This method creates a Looper object and MessageQueue object for the thread, and puts the Looper object into the thread space through threadlocal. Note that this method can only be invoked once per thread, usually in the first sentence of the thread run () method, but only before the loop () is guaranteed.
public static void Looper.loop ()
This method is called after prepare (), which is to let the thread's MessageQueue run, and once this method is invoked, the thread will loop indefinitely (while (true) {...}), hibernate without a message, and wake up when the message is queued. Until the Quit () call. Its simplified implementation is:
Copy Code code as follows:

Loop () {
while (true) {
Message msg = Mqueue.next ();
If MSG is a quit message, then
Return
Msg.processmessage (msg)
}
}

public void Looper.quit ()
Let the thread end the MessageQueue loop, terminate the loop, the run () method ends, the thread stops, so it is the object's method, meaning that it terminates a Looper object. Be sure to remember to call this method when you do not need a thread, or the thread will not terminate the exit, and the process will run and occupy resources. If a large number of threads do not exit, the process will eventually collapse.
Public static Looper Looper.mylooper ()
This is the method that gets the Looper object owned by the caller's thread.
There are two other interfaces that are related to the main thread:
one is specially prepared for the main thread.
public static void Looper.preparemainlooper ();
This method is used only for the main thread initialization looper, which is invoked only in the Activitythread.main () method and not elsewhere or by other threads, if there is an exception thrown in the main thread, because a thread can only create one Looper object. However, if this method is called in another thread, the Mainlooper will change, and the next getmainlooper will return it rather than the Looper object of the true main thread, which will not throw any exceptions, and there will be no obvious errors, but the program will not work properly. Because the method that was originally designed to run in the main thread will go into the threads, it will create a very strange bug. Here Looper.preparemainthread () method should be added to the judgment:
Copy Code code as follows:

public void Preparemainlooper () {
if (getmainlooper ()!= null) {
throw new RuntimeException ("Looper.preparemainthread () can only is called by Frameworks");
}
//...
}

To prevent illegal invocation by other threads, the document binding is far from enough.
• The other is to get the main thread Looper interface:
public static Looper Looper.getmainlooper ()
This is mainly used to check the thread's legality, which means that certain methods can only be invoked inside the main thread. But this is not insurance, as mentioned above, if a derivative thread calls Preparemainlooper () will change the real mmainlooper, this derivative thread can pass the above detection, causing getmainlooper ()!= mylooper () The tests are becoming unreliable. So the viewroot approach is to use Thread to detect: Mthread!= thread.currentthread () Mthread is obtained by Viewroot () when the system is created Thread.CurrentThread. This method detects whether the main thread is more reliable, because it does not rely on the outside but trusts its own saved thread reference.
Message Object
Message messages are only a data structure, the carrier of information, which is independent of the queue mechanism, encapsulates the necessary information to perform actions and perform actions, what, arg1, arg2, obj can be used to transmit data; , why? Because the message is only a carrier, it can not run itself to the target MessageQueue, it must be operated by the handler, the message to the target queue, since it needs to handler to unify the MessageQueue , you can also have handler to uniformly define callbacks that handle messages. Note that the same message object can only be used once, because it will be recycled after the message is processed, so that the messages object can only be used once, and MessageQueue throws an exception when trying to use it again.
Handler Objects
It is designed to facilitate the operation of the queue thread client, hiding the complexity of the direct operation MessageQueue. The main function of handler is to send messages to the MessageQueue of a thread that is bound to this handler, so you must specify a Looper object when you build handler, and if you do not specify the Looper object that gets the caller's thread through Looper. It has a lot of overloaded send*message and post methods, you can send messages to the target queue in a variety of ways, when the mountain is sent, or put in the head of the queue and so on;
It also has two functions, one is to create a message object through the obtain* system method, and the other is to define the callback Mcallback and handlemessage that handle messages, because a handler may send more than one. These messages often share this handler callback method, so it is necessary to distinguish these different messages in handlemessage or Mcallback. Usually it is message.what to distinguish between, of course, can also use other fields, as long as can distinguish different message can. It is necessary to point out that messages in message queues themselves are independent and irrelevant, and that the namespace of the message is in the handler object, because messages are sent and processed by handler, so only the same handler object needs to distinguish between different message objects. Broadly speaking, if a message has its own definition of a processing method, then all messages are irrelevant, and when the message is fetched from the queue, the callback method on it is invoked, and there is no naming conflict. However, the callback method of the message sent by handler is either Handler.handlemessage or handler.mcallback, so it will have an effect, but the scope of the effect is also limited to the same handler object.

Because the role of handler is to send a message to the destination queue and define a callback (processing message) that handles the message, it is only dependent on the MessageQueue of the thread, so handler can have any number of them bound to a MessageQueue and there is no limit to the number. And MessageQueue is limited in number, each thread can only have one, MessageQueue created by Looper, Looper stored in the threadlocal of the thread, the Looper is limited, each thread can only create one. However, without this limitation, the creation of handler through its constructor, only need to provide a Looper object, so it has no handler limit.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.