Android Message Processing Mechanism: source code analysis Handler and logoff, and asynchronous image loading

Source: Internet
Author: User

Android Message Processing Mechanism: source code analysis Handler and logoff, and asynchronous image loading
Introduction

During Android development, we often need to asynchronously load images, webpages, and others. In fact, to implement asynchronous loading, we need to implement inter-thread communication. In Android, Handler, logoff, and Message can be combined to allow different threads to communicate and complete asynchronous tasks. Although Android officially provides the AsyncTask class for us to complete asynchronous tasks, this class has many problems and is not easy to use. In addition, AsyncTask implements asynchronous loading through Handler and Thread, therefore, it is necessary to learn this knowledge.

The general idea of this article is as follows:Draw Android message processor graphics-> source code analysis components in the message processing mechanism-> implement an asynchronous image loading Demo. The final Demo is as follows:

Analysis of the Message Processing Model of Android Message Processing Mechanism

Let's first think about what a message processing mechanism needs? Of course:

Source Message Queue message processor message Manager

The message manager is divided into three sub-modules: Message acquisition, message distribution, and message loop. No matter how the Android system implements the Message Processing Mechanism (because the abstract structure of the processing mechanism must be the same, but the specific implementation is different ), follow the four modules we listed to draw a simple message processing model:

Android message processing component

Now that we know which components are required for the message processing model, go to the Android SDK and find the corresponding classes ~ Then we will find the following four classes:

The Message class represents the MessageQueue class. The Handler of the Message Queue represents the Message acquisition, the Logoff of Message processing, the Message loop, and the Message distribution.

Someone may suspect that I am playing nb and lie to everyone. At this time, I can only choose to read the source code ...... For your convenience, I will start from the logoff class, because the logoff class is a "starting and ending" function module in the message processing mechanism.

Logoff

Before parsing the logoff, let's take a look at why logoff is needed.

During Android development, in order not to block the main thread (UI thread), we often need to open another thread to complete some operations, and some of these operations are completed once, some may need to be executed several times, dozens of times, or even as long as the program process is alive, it will continue to execute this operation. The normal thread can only execute the relevant action once through the start () method. To meet the needs of multiple executions, logoff is available.

Then we will go to the logoff source code to see which members are there:

public final class Looper {    private static final String TAG = Looper;    static final ThreadLocal
  
    sThreadLocal = new ThreadLocal
   
    ();    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 logoff is a message queue, which corresponds to the thread, ThreadLocal object, and a reference of the Logoff of the main thread. We will analyze their functions based on the logoff usage process:

To use logoff, you must call the logoff prepare () method:

    private static void prepare(boolean quitAllowed) {        if (sThreadLocal.get() != null) {            throw new RuntimeException(Only one Looper may be created per thread);        }        sThreadLocal.set(new Looper(quitAllowed));    }

We can see that after calling the prepare method, a logoff object will be created through the ThreadLocal set method, and only one logoff can be created for one thread, let's take a look at what ThreadLocal does to the logoff object through the set method:

    public void set(T value) {        Thread currentThread = Thread.currentThread();        Values values = values(currentThread);        if (values == null) {            values = initializeValues(currentThread);        }        values.put(this, value);    }

Actually, the logoff object is returned by the values () method.

Values values(Thread current) {        return current.localValues;    }

The objects returned by the values () method are internal variables of a Thread. We can see that the localValues-ThreadLocal.Values localValues. That is to say, the actual operation completed by the set method is to bind the logoff object to the thread, and The logoff object is only valid within the thread, and other threads cannot access the logoff object.

After executing the prepare () method, we need to call the logoff loop () method to implement 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 this thread is that of the local process,        // and keep track of what that identity token actually is.        Binder.clearCallingIdentity();        final long ident = Binder.clearCallingIdentity();        for (;;) {            Message msg = queue.next(); // might block            if (msg == null) {                // No message indicates that the message queue is quitting.                return;            }            // This must be in a local variable, in case a UI event sets the logger            Printer 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 long newIdent = 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 little long, but the actual logic is relatively simple: first, determine whether the prepare () method is called to ensure that the logoff is bound to a thread (Note: by default, the newly created logoff object is bound to the main thread), then obtains the message queue of the corresponding thread, and then continuously reads messages in the queue cyclically. If there is no message in the queue, Loop () the method ends (this is generally not the case). Otherwise, the message is sent to the msg.tar get object for distribution.

The above is the core code of logoff. Through analysis, we can understand the relationship between logoff and thread and Its Role in message distribution mechanism,

Message

Since we mentioned the Message class at the end of logoff source code analysis, Let's first look at the Message class ~ So what does the Message class store as the Message carrier and what does it do?

public final class Message implements Parcelable {    public int what;    public int arg1;     public int arg2;    public Object obj;    public Messenger replyTo;    public int sendingUid = -1;    /*package*/ static final int FLAG_IN_USE = 1 << 0;    /** If set message is asynchronous */    /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;    /** Flags to clear in the copyFrom method */    /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;    /*package*/ int flags;    /*package*/ long when;    /*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 for processing the message is the Handler class object. In the Message class, what is used to identify the purpose of Message. arg1 and arg2 are used to transmit some simple data, obj is used to transmit objects, and data is used to transmit complex data.

I don't think there is anything to say about the methods of the Message class. Basically, they are all get/set methods, and of course there are recycling methods, for example, in the logoff loop () method, at the end of each loop, the Message recycleUnchecked () method is executed to recycle the distributed Message objects.

Some people may wonder if a message is recycled if it is not processed, will it be lost? Don't panic. I will explain it to you when analyzing Handler's message processing.

Handler

As we can see in the previous analysis, the Handler's dispatchMessage () method is used to process messages, so let's start with this method to analyze Handler:

    public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }

In the dispatchMessage () method, if the callback of msg is not null, The handleMessage () method of Handler is called to process the message. That is to say, as long as the callback of a message is not null, The handleCallback () method is called. Will unprocessed messages return to the message queue?

    private static void handleCallback(Message message) {        message.callback.run();    }

Do you feel suddenly enlightened here? When analyzing the Message source code, we know that callback is an instance of the Runnable interface. That is to say, if the Message is not processed, it will be returned to the Message queue. So how does Handler process messages?

    public void handleMessage(Message msg) {    }

It turned out to be an empty method ...... But it is also normal, because the Handler class only needs to provide abstraction, and the specific processing logic should be determined by the developer. So far we have analyzed it? No! We have not analyzed the reason why Handler can implement asynchronous event processing. Return to the Handler source code and we will see the following code snippet:

    final MessageQueue mQueue;    final Looper mLooper;    final Callback mCallback;    final boolean mAsynchronous;    IMessenger mMessenger;

I rely on ...... Handler has the message queue, logoff, and asynchronous flag. Let's look back at what we have just learned: A logoff can only belong to one thread, and a logoff has a message queue corresponding to the thread. Let's take a look at the Handler constructor. Just pick one:

    public Handler(Callback callback, boolean async) {        if (FIND_POTENTIAL_LEAKS) {            final Class
   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 occur:  +                    klass.getCanonicalName());            }        }        mLooper = Looper.myLooper();        if (mLooper == null) {            throw new RuntimeException(                Can't create handler inside thread that has not called Looper.prepare());        }        mQueue = mLooper.mQueue;        mCallback = callback;        mAsynchronous = async;    }

We can see that the message queue in the Handler is the message queue in the logoff, that is, the Handler can communicate with the message queue of any thread and process the messages, or send information to the Message Queue of another thread!

Asynchronous processing Demo

After the above analysis, we will know the Message Processing Mechanism in Android. Now let's implement an asynchronous processing Demo. The Demo is very simple, that is, downloading an image asynchronously and displaying it in an ImageView. If there are many codes, only the core code is provided. Below are:

public class DownloadTask implements Runnable{…………        private void updateMsg(Message msg){        Bundle data = new Bundle();        Bitmap img = downImage(url);        data.putString(url, url);        data.putParcelable(img, img);        msg.setData(data);    }    public Bitmap downImage(String url) {        Bitmap img = null;        try {            URL mUrl = new URL(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);        }        return img;    }}

The DownloadTask class is responsible for processing download tasks. After the download task is completed, the image address and image are saved in msg and sent to DownloadHandler:

public class DownloadHandler extends Handler{……    @Override    public void handleMessage(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 through the ILoader interface:

    public interface ILoader {        public void update(Bitmap img);    }}
 

Related Article

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.