[Reprint] From Handler. post (Runnable r), Handler. sendEmptyMessage (), sort out the Android message mechanism (and handler Memory leakage ),

Source: Internet
Author: User

[Reprint] From Handler. post (Runnable r), Handler. sendEmptyMessage (), sort out the Android message mechanism (and handler Memory leakage ),
Handler

Every beginner in Android development cannot bypass the Handler "Hurdle". Why is it a hurdle? First, this is one of the essence of the Android architecture, and most people know it, but do not know why. Today, I saw the Handler. post method and decided to repeat the source code to sort out the implementation mechanism of Handler.

Asynchronously update the UI

First, you have to say"The main thread does not perform time-consuming operations, and the sub-thread does not update the UI.", This rule should be a must for beginners. How can we solve the problem in our vocabulary? At this time, Handler will appear in front of us (AsyncTask also works, however, it is essentially an encapsulation of Handler. Here, we will discuss a piece of typical Common Code (ignore the memory leakage problem, which will be discussed later ):

First, create a new handler in Activity:

Private Handler mHandler = new Handler () {@ Override public void handleMessage (Message msg) {super. handleMessage (msg); switch (msg. what) {case 0: mTestTV. setText ("This is handleMessage"); // update UI break ;}}};

Then, send the message in the sub-thread:

New Thread (new Runnable () {@ Override public void run () {try {Thread. sleep (1000); // There is a time-consuming operation in the Child thread, such as requesting the network mHandler. sendEmptyMessage (0);} catch (InterruptedException e) {e. printStackTrace ();}}}). start ();

At this point, the UI is updated asynchronously in the main thread after the time-consuming operation of the sub-thread is completed, but the post with the title is not used. Let's take a look at the post version:

Private Handler mHandler; // global variable @ Overrideprotected void onCreate (Bundle savedInstanceState) {mHandler = new Handler (); new Thread (new Runnable () {@ Override public void run () {try {Thread. sleep (1000); // There is a time-consuming operation in the Child thread, such as requesting the network mHandler. post (new Runnable () {@ Override public void run () {mTestTV. setText ("This is post"); // update UI});} catch (InterruptedException e) {e. printStackTrace ();}}}). start ();}

On the surface, a Runnable is passed to the post method, like a sub-thread is opened, but the UI cannot be updated in the sub-thread. So the question is, what is the situation? With this doubt, let's look at the Handler source code:

Let's take a look at what a common sendEmptyMessage looks like:

public final boolean sendEmptyMessage(int what)    {        return sendEmptyMessageDelayed(what, 0);    }
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {        Message msg = Message.obtain();        msg.what = what;        return sendMessageDelayed(msg, delayMillis);    }

Encapsulate the input parameters into a message and call sendMessageDelayed:

public final boolean sendMessageDelayed(Message msg, long delayMillis)    {        if (delayMillis < 0) {            delayMillis = 0;        }        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);    }

Call sendMessageAtTime again:

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

Now let's look at post ():

Public final boolean post (Runnable r) {return sendMessageDelayed (getPostMessage (r), 0); // The getPostMessage method is different between the two methods for sending messages}

The method has only one sentence. The internal implementation is the same as the ordinary sendMessage, but there is only one difference, that isGetPostMessage (r)This method:

private static Message getPostMessage(Runnable r) {        Message m = Message.obtain();        m.callback = r;        return m;    }

This method also encapsulates the input parameters into a message, but this time isM. callback = r, Just nowMsg. what = whatAs for the Message attributes, I will not read them.

Android message mechanism

Here, we only know that the principles of post and sendMessage are encapsulated into messages, but we still don't know what the overall mechanism of Handler looks like. Continue to explore.

The two methods are called at the end.SendMessageAtTime

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

This method is called again.EnqueueMessageThe name indicates that the message is added to the queue. Click here to view the message:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }

The Asynchronous Method of mAsynchronous does not matter, and the parameter is passed to the queue's enqueueMessage method.MsgOfTargetNow we can continue to enter the enqueueMessage method of the MessageQueue class. The method is long. Let's look at the key lines:

Message prev;for (;;) {    prev = p;    p = p.next;    if (p == null || when < p.when) {        break;    }    if (needWake && p.isAsynchronous()) {        needWake = false;    }}msg.next = p; // invariant: p == prev.nextprev.next = msg;

As the method name says, an infinite loop adds a message to the Message Queue (in the form of a linked list), but the message can be obtained when it is put. How can this message be obtained?

Looking at the MessageQueue method, we foundNext ()The code is too long to go into details. We know it is used to retrieve messages. But where did this method be called? We did not find it in Handler.LogoffMy name is him.Ring messengerSpecifically responsible for Retrieving messages from the message queue. The key code is as follows:

for (;;) {     Message msg = queue.next(); // might block     ...     msg.target.dispatchMessage(msg);     ...     msg.recycleUnchecked();}

Simple and clear, we see what we just saidMsg.tar getIn Handler.Msg.tar get = thisSo let's look at dispatchMessage in Handler:

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

Msg callback should have been thought of, that is, we use Handler. post (Runnable r) is passed into the Runnable run method. here we need to mention the Java basics. Directly calling the thread's run method is equivalent to calling the method in a common class, it is still executed in the current thread and will not start a new thread.

So here, we solve the problem at the beginning, why does a Runnable in post still update the UI in the main thread?.

Continue to seeIf msg. callbackIf it is nullMCallbackTo see the constructor:

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

Only the last two implementations are available.MCallbackIn the constructor.

Finally, if the two Callbacks are empty, execute the handleMessage (msg) method of Handler itself, that is, the handleMessage method that we are familiar with when creating Handler to override.

Logoff

There is a doubt here, that is, when we create a Handler, we did not input any parameters, nor did we show that the logoff method was called, where can I create a logoff and call a method? In fact, Android has already helped us with these things.ActivityThreadIn the main method, we can find:

 public static void main(String[] args) {    ...    Looper.prepareMainLooper();    ...    Looper.loop();    ...

 

Summary

I have summarized the Handler message mechanism and the difference between the post method and our commonly used sendMessage method. To sum up, it mainly involves four typesHandler, Message, MessageQueue, Logoff:

NewHandler, Send messages through sendMessage or post,HandlerCallSendMessageAtTimeSetMessageToMessageQueue

MessageQueue. enqueueMessageMethodMessagePut in the queue as a linked list

LogoffOfLoopMethod loop callMessageQueue. next ()Retrieve the message and callHandlerOfDispatchMessageTo process messages

InDispatchMessageMedium, separate judgmentMsg. callback, mCallbackThat is, when the post method or constructor is passed in, their callback is executed without being empty. If they are empty, the most commonly used rewriteHandleMessage.

Finally, let's talk about handler Memory leakage.

Let's take a look at our new Handler code:

private Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            ...        }    };

When an internal class (including an anonymous class) is used to create a Handler, the Handler object will implicitly hold the reference of the Activity.

Handler usually appears along with a time-consuming background thread, which sends a message to update the UI after the task is executed. However, if a user disables an Activity during a network request, normally the Activity will not be used, and it may be recycled during GC check, however, since the thread has not been completed yet, and the thread holds the reference of Handler (otherwise, how does it send a message to Handler ?), The Handler holds the reference of the Activity, and thus the Activity cannot be recycled (Memory leakage) until the network request ends.

In addition, if the postDelayed () method of Handler is executed, there will beMessageQueue-> Message-> Handler-> ActivityAs a result, your Activity is referenced and cannot be recycled.

One solution is to use weak references:

Static class MyHandler extends Handler {WeakReference <Activity> mActivityReference; MyHandler (Activity activity) {mActivityReference = new WeakReference <Activity> (activity) ;}@ Override public void handleMessage (Message msg) {final Activity activity = mActivityReference. get (); if (activity! = Null) {mImageView. setImageBitmap (mBitmap );}}}

Reprinted from: http://blog.csdn.net/ly502541243/article/details/52062179/

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.