Use Handler in Android to implement message delivery mechanism (1)

Source: Internet
Author: User

Use Handler in Android to implement message delivery mechanism (1)

In the previous article, when we call the sendMessage method of Handler, we will eventually enter a method called sendMessageAtTime, as shown below:

    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);    }

Here, we see variables and methods such as MessageQueue and enqueueMessage. We can think that there must be a message queue in the implementation mechanism of Handler, it stores many Message objects we have created.

From here, we will start to explore the implementation mechanisms we want to know hidden behind the Handler object.

First, let's start with Handler's creation. in the previous article, we created it using the new Handler method. The Code is as follows:

    private Handler mHandler = new Handler() {        public void handleMessage(Message msg) {            switch (msg.what) {            case MSG_ID_1:                Log.v("Test", "Toast called from Handler.sendMessage()");                break;            case MSG_ID_2:                String str = (String) msg.obj;                Log.v("Test", str);                break;            }                    }    };

Obviously, let's take a look at the Handler constructor as follows:

    public Handler() {        this(null, false);    }    public Handler(Callback callback) {        this(callback, false);    }    public Handler(Looper looper) {        this(looper, null, false);    }        public Handler(Looper looper, Callback callback) {        this(looper, callback, false);    }       public Handler(boolean async) {        this(null, async);    }    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;    }        public Handler(Looper looper, Callback callback, boolean async) {        mLooper = looper;        mQueue = looper.mQueue;        mCallback = callback;        mAsynchronous = async;    }

We can see that the actually implemented constructor only has the following two:

    public Handler(Callback callback, boolean async) {        ....    }        public Handler(Looper looper, Callback callback, boolean async) {        ...    }

The difference between the two lies in whether there is a parameter logoff, and logoff is a thread-related object.

What are thread-related variables? It is an object that cannot be shared between threads. It only works in this thread.

So what is the role of the logoff object?

In my personal understanding, The logoff class is the encapsulation of MessageQueue. It mainly does two things:

1) construct a logoff object and initialize MessageQueue. We can see from its constructor:

    private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mThread = Thread.currentThread();    }


Apparently, MessageQueue is initialized during logoff.

We also noticed that this constructor is private, and it is called by the logoff. prepare method, as shown below:

    public static void prepare() {        prepare(true);    }    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));    }

As you can see, after a Loop object is created, it will be put into the ThreadLocal variable, and ThreadLocal is the local variable of the thread. This shows a feature of Logoff:

Each thread can have only one logoff object.

2) Call the loop () method to process messages cyclically. The specific code is as follows:

    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;       ....        for (;;) {            Message msg = queue.next(); // might block            if (msg == null) {                // No message indicates that the message queue is quitting.                return;            }           ...            msg.target.dispatchMessage(msg);           ....            msg.recycle();        }    }

From the complete code, we can see that in an infinite loop, messages will be sent from messagequeue. then, the msg.tar get. dispatchMessage (msg) method is called to process messages, and finally the messages will be recycled.

Here, we don't care about the dispatchMessage method first. Let's take a look at what is done in the recycle method, as shown below:

    private static Message sPool;    private static int sPoolSize = 0;    private static final int MAX_POOL_SIZE = 50;    /**     * 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();    }        public void recycle() {        clearForRecycle();        synchronized (sPoolSync) {            if (sPoolSize < MAX_POOL_SIZE) {                next = sPool;                sPool = this;                sPoolSize++;            }        }    }

From the code above, we can see that the Message object itself has a next field pointing to another Message, that is, a large number of messages can be serialized by means of a linked list, it becomes the sPool of the message pool of a linked list.

Here, when the recycle method is called, the current Message object will be added to the sPool header after clearForRecycle. When we use the Message obtain method, in fact, we also get an empty Message object from sPool.

I believe that here, you will understand why I have mentioned in the previous article that we recommend that you use the Message. obtain method to obtain the Message object.

Next, let's go back to the subject.

From the above descriptions of Handler creation and logoff, we can draw the following conclusion:

In every thread, if we want to create Handler, there must be a logoff object in this thread, and this logoff object encapsulates a MessageQueue object to manage the Message.

Therefore, to use handler in a new thread, we must call the Loop. prepare () method to create a logoff object for this thread. The official code is as follows:

  class LooperThread extends Thread {      public Handler mHandler;        public void run() {          Looper.prepare();            mHandler = new Handler() {              public void handleMessage(Message msg) {                  // process incoming messages here              }          };            Looper.loop();      }  }

Of course, the theory alone does not work. We have to take an example to see the specific effect, as shown below:

public class MainActivity extends ActionBarActivity {    private static final int MSG_ID_1 = 1;    private static final int MSG_ID_2 = 2;    class LooperThread extends Thread {        public Handler mHandler;        public void run() {            Looper.prepare();                mHandler = new Handler() {                                public void handleMessage(Message msg) {                    Log.v("Test", "Id of LooperThread : " + Thread.currentThread().getId());                    switch (msg.what) {                    case MSG_ID_1:                        Log.v("Test", "Toast called from Handler.sendMessage()");                        break;                    case MSG_ID_2:                        String str = (String) msg.obj;                        Log.v("Test", str);                        break;                    }                }            };            Looper.loop();        }    }        protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Log.v("Test", "Id of MainThread : " + Thread.currentThread().getId());                LooperThread looperThread = new LooperThread();        looperThread.start();                while(looperThread.mHandler == null){                                }                Message message = Message.obtain();        message.what = MSG_ID_1;        looperThread.mHandler.sendMessage(message);        Message msg2 = Message.obtain();        msg2.obj = "I'm String from Message 2";        msg2.what = MSG_ID_2;        looperThread.mHandler.sendMessage(msg2);    }}

The result is as follows:

10-27 16:48:44.519: V/Test(20837): Id of MainThread : 110-27 16:48:44.529: V/Test(20837): Id of LooperThread : 6842110-27 16:48:44.529: V/Test(20837): Toast called from Handler.sendMessage()10-27 16:48:44.529: V/Test(20837): Id of LooperThread : 6842110-27 16:48:44.529: V/Test(20837): I'm String from Message 2

In fact, there is a problem here. Why can we directly create Handler objects without calling the logoff. prepare and loop methods of the UI thread?

Of course, this is also necessary, but this step is done by the Android system, so the default main thread does not need to perform these initialization.

Well, this article will show you some associations between threads, logoff, Handler, MessageQueue, and Message, but mainly about logoff objects.

End.

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.