Android message processing mechanism (graphic + source analysis)-looper/handler/message_android

Source: Internet
Author: User
Tags message queue prepare

This article is very good, simple, the key is a junior students to analyze their own experience. This is the reason why I like this article. Please see the text below:

As a junior preparatory programmer, I learn the fun of Android is to learn from the source code Google Daniel's design ideas. The Android SDK has a lot of design patterns in it, and in addition to that, it has designed a variety of helper classes for us, and it's too much to read for someone like me who wants to get to the next level. This is not, a few days ago in order to understand the Android message processing mechanism, I saw the looper,handler,message of these several types of source code, the result is once again by the Googler design shock, special with you to share.

Android's message processing has three core classes: Looper,handler and messages. There's actually a message queue, but MQ is encapsulated in Looper, and we don't deal directly with MQ, so I don't use it as a core class. Here are the following:

Thread of the wizard Looper

The literal meaning of Looper is the "loop-person", which is designed to make a normal thread become a looper thread . The so-called Looper thread is the thread that loops the work. In program development (especially GUI development), we often need a thread to loop continuously, once a new task is executed, the execution continues to wait for the next task, which is the looper thread. Creating a Looper thread using the Looper class is simple:

Copy Code code as follows:

Publicclass Looperthread extends Thread {
@Override
Publicvoid Run () {
Initializes the current thread to a looper thread
Looper.prepare ();

// ... Other processing, such as instantiation of handler

Start loop processing Message Queuing
Looper.loop ();
}
}

With the above two lines of core code, your thread is upgraded to a looper thread!!! Isn't it amazing? Let's slow down and see what each of these two lines of code does.

1) Looper.prepare ()

As you can see from the diagram above, you now have a Looper object in your thread that internally maintains a Message Queuing MQ. Note that a thread can only have one Looper object , why? Let's look at the source code.

Copy Code code as follows:

Publicclass Looper {
The Looper object in each thread is actually a threadlocal, that is, a thread local storage (TLS) object
Privatestaticfinal ThreadLocal sthreadlocal =new ThreadLocal ();
message queues within the Looper
Final MessageQueue Mqueue;
Current thread
Thread Mthread;
。。。 Other properties

Each Looper object has its message queue, and the thread it belongs to
Private Looper () {
Mqueue =new MessageQueue ();
Mrun =true;
Mthread = Thread.CurrentThread ();
}

We call this method to create the Looper object in the calling thread's TLS
Publicstaticfinalvoid Prepare () {
if (Sthreadlocal.get ()!=null) {
Attempting to create looper again in a thread with Looper will throw an exception
Thrownew RuntimeException ("Only one looper could be created per thread");
}
Sthreadlocal.set (New Looper ());
}
Other methods
}

Through the source code, prepare () behind the working way at a glance, its core is the Looper object defined as threadlocal. If you are not sure what is threadlocal, please refer to understanding threadlocal.

2) Looper.loop ()

When the loop method is invoked, the Looper thread begins to work, and it continues to take the message (also called the Task) of the team header from its own MQ. Its source analysis is as follows:

Copy Code code as follows:

Publicstaticfinalvoid Loop () {
Looper me = Mylooper (); Get current thread Looper
MessageQueue queue = Me.mqueue; Get the current Looper MQ

These two lines do not read = = but do not affect understanding
Binder.clearcallingidentity ();
Finallong ident = binder.clearcallingidentity ();
Start loop
while (true) {
Message msg = Queue.next (); Remove message
if (msg!=null) {
if (Msg.target ==null) {
Message has no target for end signal, exit loop
Return
}
Log...
if (me.mlogging!=null) me.mLogging.println (
">>>>> dispatching to" + Msg.target + ""
+ Msg.callback + ":" + msg.what
);
Very IMPORTANT! Give the target of the message to the real processing work, the handler
Msg.target.dispatchMessage (msg);
or the log ...
if (me.mlogging!=null) me.mLogging.println (
"<<<<< finished to" + Msg.target + ""
+ Msg.callback);

I don't understand the following, but it doesn't affect understanding.
Finallong newident = binder.clearcallingidentity ();
if (ident!= newident) {
LOG.WTF ("Looper", "Thread identity changed from 0x"
+ long.tohexstring (ident) + "to 0x"
+ long.tohexstring (newident) + "while dispatching to"
+ Msg.target.getClass (). GetName () + ""
+ Msg.callback + "what=" + msg.what);
}
Reclaim Message Resources
Msg.recycle ();
}
}
}

In addition to the prepare () and loop () methods, the Looper class also provides some useful methods, such as

Looper.mylooper () Gets the current thread Looper object :

Copy Code code as follows:

Publicstaticfinal Looper Mylooper () {//Calls Looper.mylooper () on any thread to return the Looperreturn (Looper) Sthreadlocal.get () of that thread;

GetThread () Gets the thread that the Looper object belongs to:
Copy Code code as follows:

Public Thread GetThread () {return mthread;}

Quit () method ends the Looper loop:
Copy Code code as follows:

Publicvoid quit () {
Creates an empty message whose target is null, indicating that the end loop messages
Message msg = Message.obtain ();
Send a message
Mqueue.enqueuemessage (msg, 0);
}

So far, you should have a basic understanding of looper, summed up a few points:

1. Each thread has and can only have one Looper object, which is a threadlocal

2.Looper There is a message queue inside, after the loop () method call, the thread starts to take out the message execution from the queue continuously

3.Looper causes a thread to become a looper thread.

So how do we add messages to MQ? Here's the handler!. (Applause ~ ~ ~)

Asynchronous Processing Master Handler

What is handler? Handler plays the role of adding messages and processing messages to MQ (handling only the messages sent by itself), that is, to notify MQ that it is performing a task (SendMessage) and to perform the task (handlemessage) on the loop to itself. The whole process is asynchronous . When handler is created, a looper is associated, and the default constructor will associate the current thread's looper, but it can also be set. Default method of construction:

Copy Code code as follows:

Publicclass Handler {

Final MessageQueue Mqueue; Associated MQ
Final Looper Mlooper; Associated Looper
Final Callback Mcallback;
Other properties

Public Handler () {
Don't understand, just skip,,,
if (Find_potential_leaks) {
Final Class<?extends handler> klass = GetClass ();
if ((Klass.isanonymousclass () | | | klass.ismemberclass () | | klass.islocalclass ()) &&
(Klass.getmodifiers () & modifier.static) ==0) {
LOG.W (TAG, "the following Handler class should be static or leaks might:" +
Klass.getcanonicalname ());
}
}
The looper of the current thread is associated by default
Mlooper = Looper.mylooper ();
Looper cannot be empty, that is, the default construction method can only be used in Looper threads
if (Mlooper ==null) {
Thrownew RuntimeException (
"Can ' t create handler inside thread that has not called looper.prepare ()");
}
Important!!! Directly to the associated Looper MQ as its own MQ, so its message will be sent to the associated Looper MQ
Mqueue = Mlooper.mqueue;
Mcallback =null;
}

Other methods
}

Here we can add handler to the previous Looperthread class:
Copy Code code as follows:

Publicclass Looperthread extends Thread {
Private Handler handler1;
Private Handler Handler2;

@Override
Publicvoid Run () {
Initializes the current thread to a looper thread
Looper.prepare ();

Instantiation of two Handler
Handler1 =new Handler ();
Handler2 =new Handler ();

Start loop processing Message Queuing
Looper.loop ();
}
}

The effect after adding handler is as follows:

As you can see, a thread can have multiple handler, but only one looper!

Handler Send Message

With handler, we can use,,,,, post(Runnable) postAtTime(Runnable, long) postDelayed(Runnable, long) sendEmptyMessage(int) sendMessage(Message) sendMessageAtTime(Message, long) and sendMessageDelayed(Message, long) These methods to send messages to MQ. Just look at these APIs you may feel that handler can send two kinds of messages, one is Runnable object, one is the message object, this is intuitive understanding, but in fact post issued runnable objects are finally encapsulated into message object, see source code:

Copy Code code as follows:

This method is used to send the Runnable object on the associated MQ, and its run method executes in the handler associated Looper thread
Publicfinalboolean Post (Runnable R)
{
Note that Getpostmessage (R) encapsulates runnable as a message
Return sendmessagedelayed (Getpostmessage (R), 0);
}

Privatefinal message Getpostmessage (Runnable r) {
Message M = Message.obtain (); Get an empty message
M.callback = R; The runnable is set to the callback of the message,
return m;
}

Publicboolean sendmessageattime (msg, long Uptimemillis)
{
Boolean sent =false;
MessageQueue queue = Mqueue;
if (queue!=null) {
Msg.target =this; The target of the message must be set to the handler!
Sent = Queue.enqueuemessage (msg, uptimemillis);
}
else {
RuntimeException e =new runtimeexception (
This+ "Sendmessageattime () called with no Mqueue");
LOG.W ("Looper", E.getmessage (), E);
}
return sent;
}

Other methods are not listed, in short, the message issued through the handler has the following characteristics:

1.message.target is the handler object, which ensures that Looper can find the handler that handles it when executing to the message, that is, the key code in the loop () method

Copy Code code as follows:

Msg.target.dispatchMessage (msg);

2.post message, whose callback is Runnable object

Handler processing messages

When you have finished sending the message, look at how handler handles the message. Message processing is done through the core method DispatchMessage (msg) and Hook Method Handlemessage (msg), see source

Copy Code code as follows:

Process the message, which is called by Looper
Publicvoid dispatchmessage (Message msg) {
if (Msg.callback!=null) {
If the message is set callback, that is, runnable messages, process the callback!
Handlecallback (msg);
} else {
If the handler itself sets callback, the execution callback
if (Mcallback!=null) {
/* This method allows the activity to implement the Handler.callback interface and avoids writing handler rewrite Handlemessage methods. See http://alex-yang-xiansoftware-com.iteye.com/blog/850865 * *
if (Mcallback.handlemessage (msg)) {
Return
}
}
If the message does not callback, call the handler hook method handlemessage
Handlemessage (msg);
}
}

Processing runnable messages
Privatefinalvoid handlecallback (Message message) {
Message.callback.run (); Call the Run method directly!
}
Hook method implemented by subclasses
Publicvoid handlemessage (Message msg) {
}

As you can see, the internal working mechanism of handler is transparent to the developer, except that the Handlemessage (message msg) and Runnable object's Run method is implemented by the developer (implementing concrete logic). This is the subtlety of handler API design!

The usefulness of handler

I described handler as "the Master of Asynchronous processing" in a small title, thanks to Handler's two important features:

1.handler can send messages on any thread that is added to the associated MQ.

2.handler handles the message in its associated looper thread .

This solves the classic Android problem of not updating the UI in other non main threads. Android's main thread is also a looper threading (Looper is widely used in Android), the handler that we create in it defaults to associating the main thread MQ. As a result, a solution that uses handler is to create a handler in an activity and pass its reference to worker Thread,worker thread to send a message to notify it of the update of the UI using handler when the task finishes. (process as pictured)

sample code is given below for reference only :

Copy Code code as follows:

Publicclass Testdriveractivity extends Activity {
Private TextView TextView;

@Override
Protectedvoid onCreate (Bundle savedinstancestate) {
Super.oncreate (savedinstancestate);
Setcontentview (R.layout.main);
TextView = (TextView) Findviewbyid (R.id.textview);
Create and start a worker thread
Thread Workerthread =new Thread (new Sampletask (new MyHandler));
Workerthread.start ();
}

Publicvoid AppendText (String msg) {
Textview.settext (Textview.gettext () + "\ n" + msg);
}

Class MyHandler extends Handler {
@Override
Publicvoid handlemessage (Message msg) {
String result = Msg.getdata (). getString ("message");
Update UI
AppendText (result);
}
}
}

Copy Code code as follows:

Publicclass Sampletask implements Runnable {
privatestaticfinal String TAG = SampleTask.class.getSimpleName ();
Handler Handler;

Public Sampletask (Handler Handler) {
Super ();
This.handler = handler;
}

@Override
Publicvoid Run () {
try {//Simulate perform a task, download, etc.
Thread.Sleep (5000);
Notify the activity to update the UI when the task completes
Message msg = preparemessage ("task completed!");
Message will be added to the main thread's MQ
Handler.sendmessage (msg);
catch (Interruptedexception e) {
LOG.D (TAG, "interrupted!");
}

}

Private message Preparemessage (String str) {
Message result = Handler.obtainmessage ();
Bundle data =new Bundle ();
Data.putstring ("message", str);
Result.setdata (data);
return result;
}

}

Of course, handler can do far more than that, because it can post runnable objects, it can also work with Looper to achieve the classic pipeline thread (pipelining) mode. Please refer to this article "Android Guts:intro to Loopers and handlers"

Encapsulate Task message

In the entire messaging mechanism, the message is called a task, encapsulating the information that the task carries and the handler that handles the task. The use of the message is simpler, not summed up here. But there are a few points to note (to be added):

1. Although the message has the default constructor for public, you should save resources by Message.obtain () to get empty message objects from the pool of messages.

2. If your message only needs to carry simple int information, please use MESSAGE.ARG1 and message.arg2 to pass the information, which is more memory than the bundle

3. Use Message.what to identify information in order to handle messages in different ways.

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.