Introduction
When we do Android development, we often need to implement the asynchronous loading of images/Web pages/other. In fact, to implement asynchronous loading, you need to implement inter-thread communication, while in Android, using Handler, Looper, and Message enables different threads to communicate and complete asynchronous tasks. Although Android has provided us with the Asynctask class to accomplish asynchronous tasks, there are many problems with this class, and it is not easy to use it, and Asynctask is also loaded asynchronously by Handler and Thread, so it is necessary to learn this knowledge.
This article explains the idea roughly as follows: drawing Android message processing mechanism diagram--source parsing message processing mechanism components---implementation of a picture asynchronous loading Demo. The final Demo is as follows:
An analysis of message processing model based on Android message processing mechanism
Let's start by thinking about what a message processing mechanism requires. Of course it is:
- Message source
- Message Queuing
- Message Processor
- Message Manager
Where the message manager is divided into three sub-modules: Message acquisition, message distribution, message loop. Let's start with how the message processing mechanism will be implemented within Android (because the abstract structure of the processing mechanism is definitely the same, but the implementation is not the same), draw a simple message-processing model from our list of 4 modules:
Android Message Processing Component
Now that we know which components the message processing model requires, go to the Android SDK and find the appropriate class-then we'll find the following four classes:
- The message class represents messages
- MessageQueue class represents Message Queuing
- Handler on behalf of message acquisition, message processing
- Looper represents message loops, message distribution
Some people may suspect that I am blowing nb, in deceiving everyone, this time I can only choose to see the source ... To make it easy for everyone to understand, I'll start with the Looper class because the Looper class is a "Connecting" function module in the message processing mechanism.
Looper
Before parsing Looper, think about why you need Looper.
When we do Android development, in order to not block the main thread (UI thread), often need to open another thread to complete some operations, and some of these operations are done once, some may need to execute several times, dozens of times, or even as long as the program process survival will continue to do the operation. The normal thread can only perform the related action once through the start () method, and in order to meet the requirements of multiple executions, there is a Looper.
Then we will enter the Looper source code, to see what members of the Looper:
public final class looper { private static final String TAG = " Looper "; static final threadlocal<looper> sthreadlocal = new threadlocal<looper> (); private static Looper Smainlooper; //guarded by Looper.class final MessageQueue Mqueue; final Thread Mthread; private Printer mlogging;}
As you can see, the core member of Looper is a message queue, the Looper corresponding thread, the ThreadLocal object, and a reference to the main thread Looper. We analyze their role based on the use of Looper processes:
To use Looper, you must call the Looper prepare () method:
privatestaticvoidprepare(boolean quitAllowed) { ifnull) { thrownew RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
As we can see, the call to the Prepare method creates a Looper object with the ThreadLocal set method, and a thread can only create one Looper, so let's see what ThreadLocal did with the Looper object through the Set method. :
publicvoidset(T value) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); ifnull) { values = initializeValues(currentThread); } values.put(this, value); }
The actual operation of the Looper object is that the values () method returns the object
Values values(Thread current) { return current.localValues; }
The object returned by the values () method is an internal variable of a thread, and we'll go inside to see that it's defined localvalues inside the thread class ThreadLocal.Values localValues
. That is, the set method actually completes the operation of binding the Looper object to the thread, and the Looper object is valid only within that thread, and other threads cannot access the Looper object.
After executing the prepare () method, we are going to call the loop () method of Looper to implement the loop:
Public Static void Loop() {FinalLooper me = Mylooper ();if(Me = =NULL) {Throw NewRuntimeException ("No Looper; Looper.prepare () wasn ' t called on the This thread. "); }FinalMessageQueue queue = Me.mqueue;//Make sure the identity of this thread is the the local process, //And keep track of the What, identity token actually is.Binder.clearcallingidentity ();Final LongIdent = Binder.clearcallingidentity (); for(;;) {Message msg = Queue.next ();//might block if(msg = =NULL) {//No message indicates that the message queue is quitting. return; }//This must is in a local variable, with case a UI event sets the loggerPrinter logging = me.mlogging;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.callback); }//Make sure that during the course of dispatching the //identity of the thread wasn ' t corrupted. Final LongNewident = Binder.clearcallingidentity ();if(Ident! = newident) {LOG.WTF (TAG,"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); } msg.recycleunchecked (); } }
The method is a bit long, but the actual logic is simple: first determine if the prepare () method is called to ensure that Looper and a thread are bound (note that by default, the new Looper object is bound to the main thread), and then the corresponding thread's message queue is obtained. It then loops through the messages in the queue and, if there is no message in the queue, the loop () method ends (which typically does not occur), otherwise the message is handed over to the Msg.target object for distribution.
The above is the core code of Looper, through the analysis we can understand the relationship between Looper and thread, and in the message distribution mechanism, the role of
Message
Since we are in the analysis of the Looper source of the last mention of the message class, then we will first look at the message class ~ then the message class as the carrier of the information exactly what is stored, do what?
Public Final class Message implements parcelable { Public intwhat; Public intArg1; Public intarg2; PublicObject obj; PublicMessenger ReplyTo; Public intSendinguid =-1;/*package*/ Static Final intFlag_in_use =1<<0;/** If Set Message is asynchronous * / /*package*/ Static Final intFlag_asynchronous =1<<1;/** Flags to clear in the CopyFrom method * / /*package*/ Static Final intFlags_to_clear_on_copy_from = Flag_in_use;/*package*/ intFlags/*package*/ LongWhen;/*package*/Bundle data;/*package*/Handler Target;/*package*/Runnable callback;//Sometimes we store linked lists of these things /*package*/Message Next;
Now we can know that the target of the original processing message is the object of the Handler class. In the message class, what is used to identify the purpose of a message, arg1 and arg2 are used to pass some simple data, obj is used to pass objects, and data is used to pass complex data.
The method of the Message Class I think there is nothing to say, basically is the Get/set method, of course, there are recycling methods, such as in the Looper loop () method, each time the loop end will execute the Message of the Recycleunchecked () method, will be distributed M Essage Object collection.
One might wonder if the message would not have been lost if it had not been processed. Don't panic, I'll explain it to you when I analyze Handler processing the message.
Handler
We see in the previous analysis that the real processing of the message is the Handler DispatchMessage () method, then we start with this method to analyze the Handler bar:
publicvoiddispatchMessage(Message msg) { ifnull) { handleCallback(msg); else { ifnull) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
In the DispatchMessage () method, if the MSG's callback is not NULL, the Handler handlemessage () method is called to process the message. That is, as long as the callback of the message is not NULL, the Handlecallback () method is called, then the unhandled message will not return to the message queue?
privatestaticvoidhandleCallback(Message message) { message.callback.run(); }
Do you see any sense of epiphany here? Just when we analyzed the message source code, we already know that callback is an instance of the Runnable interface, that is, if the message is not processed, it will return to the message queue. So how does Handler deal with the news?
publicvoidhandleMessage(Message msg) { }
It was an empty method ... But it is also normal, because the Handler class only needs to provide abstraction, the specific processing logic should be decided by the developer. So we're done with the analysis? It's not! We have not analyzed the reason why Handler can implement asynchronous event processing, back to Handler source, we will see the following code snippet:
final MessageQueue mQueue; final Looper mLooper; final Callback mCallback; finalboolean mAsynchronous; IMessenger mMessenger;
Holy...... Handler inside the message queue, Looper, asynchronous flag bit, we recall what we have just analyzed the conclusion: A Looper can only belong to one thread, Looper has the corresponding thread message queue. Let's take a look at the construction method of Handler and pick one of them:
Public Handler(Callback Callback,BooleanAsync) {if(Find_potential_leaks) {Finalclass<? Extends handler> klass = GetClass ();if((Klass.isanonymousclass () | | klass.ismemberclass () | | klass.islocalclass ()) && (klass.getmodif Iers () & modifier.static) = =0) {LOG.W (TAG,"The following Handler class should be static or leaks might occur:"+ Klass.getcanonicalname ()); }} Mlooper = Looper.mylooper ();if(Mlooper = =NULL) {Throw NewRuntimeException ("Can ' t create handler inside thread that have not called looper.prepare ()"); } mqueue = Mlooper.mqueue; Mcallback = callback; masynchronous = async; }
As we can see, the message queue inside the Handler is the message queue in Looper, which means that Handler can communicate with any one thread's message queue and process the messages in it, or send messages to other threads in the message queue!
Asynchronous processing Demo
After completing the above analysis, we know the message processing mechanism in Android, so we can implement an asynchronous processing demo now. Demo is very simple, is to download an image asynchronously, and show it to a ImageView, the code is more, just give the core code, the following:
Public class downloadtask implements Runnable{............Private void updatemsg(Message msg) {Bundle data =NewBundle (); Bitmap img = downimage (URL); Data.putstring ("url", URL); Data.putparcelable ("img", IMG); Msg.setdata (data); } PublicBitmapDownimage(String URL) {Bitmap img =NULL;Try{URL Murl =NewURL (URL); HttpURLConnection conn = (httpurlconnection) murl.openconnection (); Conn.setdoinput (true); Conn.connect (); InputStream is = Conn.getinputstream (); IMG = Bitmapfactory.decodestream (IS); }Catch(IOException e) {LOG.D (TAG,"Downloadimg-exception"); }returnimg }}
The Downloadtask class handles the download task, downloads the download task, and then saves the image address and picture to MSG in the download task, and sends it to Downloadhandler:
publicclass DownloadHandler extends Handler{…… @Override publicvoidhandleMessage(Message msg) { String url = msg.getData().getString("url"); Bitmap img = msg.getData().getParcelable("img"); Log.d(TAG, url); loader.iLoader.update(img); }}
Finally, update the external UI via the Iloader interface:
publicinterface ILoader { publicvoidupdate(Bitmap img); }}
Android message processing mechanism: source anatomy handler, Looper, and implementation of the image asynchronous loading