Recently approached a more challenging project, I found that the use of a large number of message mechanisms, and now this blog I want to specifically analyze: What is the message of Android, what is the message mechanism in the end what good?
Actually speaking of Android message mechanism, we may soon think of message/handle/looper these three objects, yes, in Android is through the three objects to implement the message mechanism. So what I want to ask is, why do we need a messaging mechanism? The bottom-end is for a better, smoother user experience, because in the app we are more or less likely to design some more time-consuming operations, this time if we just blindly put all the current operations on a thread, then the thread will be special card, Causes the user interface to have a long lag and no response. So, can we be smart about putting some time-consuming operations on top of a worker thread? Now is possible, this time our main thread-ui threads, we can only handle the UI updates, show the work, when the work thread is finished, we can be a mechanism to notify the UI to update, and here you may be able to understand the message mechanism of the original intention. Just for the sake of mutual trust between multiple threads, the most common scenario might be HTTP network requests.
OK, let's start with the following code example:
Btnmsgtest.setonclicklistener (New View.onclicklistener () { @Override public void OnClick (View v) { Workthread workthread = new Workthread (); Workthread.start (); } });
The above code I just subscribed to a button click event, in the button click event we opened a new worker thread, let's look at the implementation code of the worker thread:
Class Workthread extends Thread { @Override public void Run () { btnmsgtest.settext ("Hello"); } }
In fact, it is very simple, we just modify the button's Text property value in the work thread, but the current click button, but found that the program error:
Android.view.viewrootimpl$calledfromwrongthreadexception:only the original thread that created a view hierarchy can tou Ch its views. At Android.view.ViewRootImpl.checkThread (viewrootimpl.java:6556) at Android.view.ViewRootImpl.invalidateChildIn Parent (viewrootimpl.java:942) at Android.view.ViewGroup.invalidateChild (viewgroup.java:5081) at Android Oid.view.View.invalidateInternal (view.java:12713) at Android.view.View.invalidate (view.java:12677) A T Android.view.View.invalidate (view.java:12661) at Android.widget.TextView.checkForRelayout (textview.java:7159) At Android.widget.TextView.setText (textview.java:4342) at Android.widget.TextView.setText (TextView. java:4199) at Android.widget.TextView.setText (textview.java:4174) at example.xiaocai.com.testgumpcom E.mainactivity$workthread.run (mainactivity.java:157) 03-05 04:15:00.741 1600-1613/ Example.xiaocai.com.testgumpcome E/suRface:getSlotFromBufferLocked:unknown buffer:0xee9f20e0
What is this for? Let's analyze the exception hint above, meaning that the UI update can only be on its own original thread. So the question is, how do we notify the UI of the update in the worker thread? This time the news mechanism can be a good fist.
Let's take a look at the message object, which, like the literal meaning of understanding, is the real message object, and we can define a message with the following code:
Message Message=handler.obtainmessage (); message.what=1;message.obj= "Hello"; handler.sendmessage (message);
Note that we're here to create a message object from the Obtainmessage method, instead of creating a new Message object with new message. Humorous, what is the difference between the two? Through source analysis we will find that Obtainmessage is allocating an object from the message pool, thus avoiding the creation of new objects.
Careful you, perhaps from the above code inside can also see Handler object, below we say a handler object it! How do we send out a message after we create it? And then how to dispatch processing it? We can create a handler object directly inside the code, with the following code:
Handler handler2=new Handler () { @Override public void Handlemessage (Message msg) { super.handlemessage ( msg); } };
Then we can send the message out by invoking the SendMessage method with the Handler2 object. Through this process analysis, we can probably guess the role of handler, it is the specific object of the message, through the handler we can send various types of message, can handle messages and so on.
In this case, we have another question: if I want to send multiple message objects, how are different message objects dispatched? Take a look at the following code:
Message Message=handler.obtainmessage (); message.what=1;message.obj= "Hello"; handler.sendmessage (message); Message message1=handler.obtainmessage (); message1.what=2;message1.obj= "World"; Handler.sendmessage (Message1);
In this case, does that mean there will be a messagequene in the background? Otherwise, where are the multiple messages placed? And then which way does the message object dispatch? Yes, more than one message is scheduled to be executed through the Looper object.
Here, let's use the message mechanism to re-modify the top instance code:
Class Workthread extends Thread { @Override public void Run () {// Btnmsgtest.settext ("Hello"); Message message=handler.obtainmessage (); Message.what=1; message.obj= "Modify button text"; Handler.sendmessage (message); } }
At this point we're not updating the UI directly in the work thread, we're sending a message and then the UI update in handler, let's take a look at this piece of code:
Private Handler Handler = new Handler () { @Override public void Handlemessage (Message msg) { switch (msg.wh At) {case 1: btnmsgtest.settext ("Hello"); Break;}} ;
You will be surprised to see here perhaps smart, why I did not see Looper figure? So the new handler can be sure it's on the UI thread? Pro, don't worry, let's take a look at the same code as above, perhaps you will understand.
Private Handler handler2=new Handler (Looper.getmainlooper (), new Handler.callback () { @Override public Boolean handlemessage (Message msg) { btnmsgtest.settext ("set by Looper"); return false; } });
In this code we can see the looper figure, and we call the Getmainlooper method to get to the main thread, which is the UI thread we need. See here we can use the following image to illustrate the relationship between the three.
From the above analysis, we can understand what the Android message mechanism is all about, but also be able to use them more skillfully. Let's take a closer look at the source code of the Android messaging mechanism, and more intuitively understand how the message is sent, dispatched, and processed. First look at the source of the message:
Public final class Message implements Parcelable
We can see that the message implements a parcelable interface that is primarily used to wrap and transfer data. Yes, of course we need to transfer the data in the message. Then let's take a look at the common property Information code included in the message:
/** * user-defined Message code So, the recipient can identify * What's this message are about. Each {@link Handler} have its own name-space * for the message codes, so don't need to worry about yours conflicting * with other handlers. */public int what; /** * Arg1 and Arg2 is lower-cost alternatives to using * {@link #setData (Bundle) setData ()} If you have need to Store A * few integer values. */public int arg1; /** * Arg1 and Arg2 is lower-cost alternatives to using * {@link #setData (Bundle) setData ()} If you have need to Store A * few integer values. */public int arg2; /** * An arbitrary object to send to the recipient. When using * {@link Messenger} to send the message across processes this can is only * is non-null if it contains a P Arcelable of a framework class (not one * implemented by the application). For other data transfer use * {@link #setData}. * * <p>note that Parcelable objects here is not supported prior to * the {@link Android.os.build.version_codes#froyo} release. */public Object obj;
The above four attributes, when we set up a message message, may be used frequently, specific when the scene can be seen in the comments. Finally, let's take a look at the main methods:
/** * Return A new Message instance from the global pool. allows us to * Avoid allocating new objects in many cases. */public static Message obtain () { synchronized (spoolsync) { if (sPool! = null) { Message m = spool;
spool = M.next; M.next = null; spoolsize--; return m; } } return new Message ();
From the above code we can see that if the message pool is not empty, then we will synchronously assign the message from the message pool, otherwise we will instantiate a message object. In this case, we generally create a message object, it is recommended to use the obtain method, which can improve the performance of the application. There is also a recycle method in the message, the code is as follows:
/** * Return A Message instance to the global pool. You must not touch * The Message after calling this function--it has effectively been * freed. */Public void Recycle () { clearforrecycle (); Synchronized (spoolsync) { if (Spoolsize < max_pool_size) { next = SPool; SPool = this; spoolsize++;}}}
/*package*/void Clearforrecycle () { flags = 0; what = 0; arg1 = 0; arg2 = 0; obj = null; ReplyTo = null; when = 0; target = null; callback = NULL; data = null; }
We can see that in fact the implementation in the Recycle method is very simple, just to set some variables as the default value, while the message pool is larger. The rest of the way is to implement the Parcelable interface, to achieve data packaging.
Next let us look at handler inside the most important method, the first is the message distribution method, the source code is as follows:
/** * Handle system messages here. */Public void DispatchMessage (Message msg) { if (msg.callback! = null) { handlecallback (msg); } else { C6/>if (Mcallback! = null) { if (Mcallback.handlemessage (msg)) { return; } } Handlemessage (msg); } }
The main idea is that if we have written a callback method in the message, we call the method inside the message to handle it directly. If the message does not implement callback processing, then handler inside the interface callback, the code is as follows:
/** * Callback interface You can use when instantiating a Handler to avoid * have to implement your own Subcla SS of Handler. * * @param msg A {@link android.os.Message Message} object * @return True If no further handling is desired
*/Public interface Callback {public boolean handlemessage (Message msg);
This is why we are able to make the Handlermessage method in the handler to the message processing directly in the new one. Next is the construction method we saw just now, the source code is as follows:
/** * Use the provided {@link Looper} instead of the default one and take a callback * interface in which to Handl E messages. * * @param looper the looper, must not is null. * @param callback the callback interface in which to handle messages, or null. */Public Handler (Looper Looper, Callback Callback) {This (Looper, Callback, false); }
Gets the thread environment from a Looper object, and then implements the callback callback. The last more important method is a series of methods to send messages, the source code is as follows:
/** * pushes a message onto the end of the message queue after all pending messages * before the current time. It'll be received in {@link #handleMessage}, * in the thread attached to this handler. * * @return Returns True if the message is successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */Public Final Boolean sendMessage (Message msg) {return sendmessagedelayed (msg, 0); }/** * Sends a Message containing only the "what value". * * @return Returns True if the message is successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */Public Final Boolean sendemptymessage (int.) {return sendemptymessagedelayed (what, 0); }/** * Sends a Message containing only the "what Val"UE, to is delivered * after the specified amount of time elapses. * @see #sendMessageDelayed (android.os.Message, Long) * * @return Returns True if the Message was successfully pl Aced in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */Public final Boolean sendemptymessagedelayed (int what, long Delaymillis) {Message msg = Message.obtain (); Msg.what = what; Return sendmessagedelayed (msg, delaymillis); }/** * Sends a Message containing only the "what value" to is delivered * at a specific time. * @see #sendMessageAtTime (android.os.Message, Long) * * @return Returns True if the Message was successfully pla CED in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */Public final Boolean sendemptymessageattime (int what, long Uptimemillis) {Message msg = Message.obtain (); Msg.what = what; Return Sendmessageattime (msg, uptimemillis); }/** * Enqueue a message into the message queue after all pending messages * before (current time + Delaymilli s). You'll receive it in * {@link #handleMessage} with the thread attached to this handler. * * @return Returns True if the message is successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that A * result of true does not mean the message would be processed--if * the Looper is qui T before the delivery time of the message * occurs then the message would be dropped. */Public Final Boolean sendmessagedelayed (Message msg, long Delaymillis) {if (Delaymillis < 0) { Delaymillis = 0; } return Sendmessageattime (MSG, SystEmclock.uptimemillis () + Delaymillis); }/** * Enqueue a message into the message queue after all pending messages * before the absolute time (in Mill Iseconds) <var>uptimemillis</var>. * <b>the Time-base is {@link android.os.systemclock#uptimemillis}.</b> * you'll receive it in {@link #ha Ndlemessage}, in the thread attached * to this handler. * * @param uptimemillis the absolute time at which the message should be * delivered, using the * {@link Android.os.systemclock#uptimemillis} time-base. * * @return Returns True if the message is successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that A * result of true does not mean the message would be processed--if * the Looper is qui T before the delivery time of the message * occurs tHen the message would be dropped. */Public Boolean sendmessageattime (Message msg, long Uptimemillis) {MessageQueue queue = Mqueue; if (queue = = null) {runtimeexception e = new RuntimeException (this + "sendmessageattime () Called with no Mqueue "); LOG.W ("Looper", E.getmessage (), E); return false; } return Enqueuemessage (Queue, MSG, uptimemillis); }/** * Enqueue a message at the front of the message queue, to being processed on * The next iteration of the MES Sage Loop. You'll receive it in * {@link #handleMessage} with the thread attached to this handler. * <b>this method is very special circumstances--it * can easily starve the message queue, Caus E ordering problems, or has * other unexpected side-effects.</b> * * @return Returns true if the mess Age is successfully placed in to the * message queue. Returns false On failure, usually because the * looper processing the message queue is exiting. */Public Final Boolean sendmessageatfrontofqueue (Message msg) {MessageQueue queue = Mqueue; if (queue = = null) {runtimeexception e = new RuntimeException (this + "Sendmessageattime () Cal LED with no mqueue "); LOG.W ("Looper", E.getmessage (), E); return false; } return Enqueuemessage (queue, MSG, 0); }
The difference between the above method is the time of sending, some of the properties sent are different.
OK, let us look at Looper inside the source code, first of all the main method has prepare, from the name you may be able to see, the method is the most important is to prepare the MessageQueue, Then call the loop method to dispatch the message object from the MessageQueue, the source code is as follows:
/** * Run The message queue in this thread. Be sure to call * {@link #quit ()} to end of the loop. */public static void loop () {final Looper me = Mylooper (); if (me = = null) {throw new RuntimeException ("No Looper; Looper.prepare () wasn ' t called on this thread. "); Final MessageQueue queue = Me.mqueue; Make sure the identity of the the the the The local process,//and keep track of what the identity toke n actually is. Binder.clearcallingidentity (); Final Long ident = Binder.clearcallingidentity (); for (;;) {Message msg = Queue.next ();//might block if (msg = = NULL) {//No Message Indicat ES the message queue is quitting. Return }//This must is in a local variable, in case a UI event sets the logger Printer logging = Me.mlogg ing if (logging! = null) {LOGGING.PRINTLN (">>>>> dispatching to" + Msg.target + "+ msg.callback +": "+ msg.what); } msg.target.dispatchMessage (msg); if (logging! = null) {logging.println ("<<<<< finished to" + Msg.target + "" + Msg.callbac k); }//Make sure that during the course of dispatching the//identity of the thread wasn ' t corrupted. Final Long newident = Binder.clearcallingidentity (); if (ident! = newident) {LOG.WTF (TAG, "Thread identity changed from 0x" + Long.tohex String (ident) + "to 0x" + long.tohexstring (newident) + "when dispatching to" + Msg.target.getClass (). GetName () + "" + Msg.callback + "what=" + msg.what); } msg.recycle (); } }
The following: Loops through the Message object and distributes the message object. Then there are two more important methods are Getmainlooper and Getmylooper, the source code is as follows:
/** * Initialize The current thread as a looper, marking it as an * application ' s main looper. The main looper for your application * are created by the Android environment, so we should never need * to call T His function yourself. See also: {@link #prepare ()} */public static void Preparemainlooper () {prepare (false); Synchronized (Looper.class) {if (smainlooper! = null) {throw new IllegalStateException ("The MA In Looper have already been prepared. "); Smainlooper = Mylooper (); }}/** Returns the application ' s main Looper, which lives in the main thread of the application. */public static Looper Getmainlooper () {synchronized (Looper.class) {return smainlooper; }}/** * Return the Looper object associated with the current thread. Returns * NULL if the calling thread is not associated with a Looper. */public static Looper Mylooper () {return sthreadlocal.get (); }
Well, today's blog is the first to come here, if there is something wrong place welcome to shoot bricks.
The message mechanism of the Android Development series