Java Foundation - Handle

Posted by jdnet on Thu, 16 May 2019 01:07:54 +0200

The role of Handler:

When we need to deal with time-consuming operations (such as accessing the network, database operations) in sub-threads, and when the time-consuming operations are completed, we need to update the UI, which requires the use of Handler, because sub-threads can not do the operation of updating the UI. Handler can help us easily switch tasks (in sub-threading) back to their threads. Simply understood, Handler is to solve the communication between threads.

Use of Handler:

Two forms of handler are used:
1. Use handler in the main thread;
2. Use handler in sub-threads.

An example of using handler in the main thread:

    public class TestHandlerActivity extends AppCompatActivity {


        private static final String TAG = "TestHandlerActivity";

        private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //Get the Message object you just sent, and then do the UI operation here.
                Log.e(TAG,"------------> msg.what = " + msg.what);
            }
        };


        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_handler_test);
            initData();
        }

        private void initData() {

            //Open a thread to simulate time-consuming operations
            new Thread(new Runnable() {
                @Override
                public void run() {

                    SystemClock.sleep(2000);
                    //Send a message through Handler to switch back to the main thread (the thread where mHandler is)
                    mHandler.sendEmptyMessage(0);
                }
            }).start();

        }   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

Using handler in the main thread is simple. It only needs to create a handler object in the main thread, send a Message through the handler object created by the main thread in the sub-thread, and accept the Message object for processing in the handleMessage () method. It's easy to switch back from the sub-thread to the main thread through handler.

So let's see if that's the case with sub-threads.

 public class TestHandlerActivity extends AppCompatActivity {


        private static final String TAG = "TestHandlerActivity";
        //handler in the main thread
        private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //Get the Message object you just sent, and then do the UI operation here.
                Log.e(TAG,"------------> msg.what = " + msg.what);
            }
        };
        //handler in Subthread
        private Handler mHandlerThread = null;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_handler_test);
            initData();
        }

        private void initData() {

            //Open a thread to simulate time-consuming operations
            new Thread(new Runnable() {
                @Override
                public void run() {

                    SystemClock.sleep(2000);
                    //Send a message through Handler to switch back to the main thread (the thread where mHandler is)
                    mHandler.sendEmptyMessage(0);
                    //Create Handler in Subthread
                    mHandlerThread = new Handler(){
                        @Override
                        public void handleMessage(Message msg) {
                            super.handleMessage(msg);
                            Log.e("sub thread","---------> msg.what = " + msg.what);
                        }
                    };

                    mHandlerThread.sendEmptyMessage(1);
                }
            }).start();

        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

The program crashed. The error reported is that the method Looper.prepare() is not called in the subthread. And why is there no error in using it in the main thread? This problem can be solved by source code analysis.

The correct use of Handler in sub-threads should be like this.

 public class TestHandlerActivity extends AppCompatActivity {


        private static final String TAG = "TestHandlerActivity";

        //Hadler of the main thread
        private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //Get the Message object you just sent, and then do the UI operation here.
                Log.e(TAG,"------------> msg.what = " + msg.what);
            }
        };
        //Handler in Subthread
        private Handler mHandlerThread = null;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_handler_test);
            initData();
        }

        private void initData() {

            //Open a thread to simulate time-consuming operations
            new Thread(new Runnable() {
                @Override
                public void run() {

                    SystemClock.sleep(2000);
                    //Send a message through Handler to switch back to the main thread (the thread where mHandler is)
                    mHandler.sendEmptyMessage(0);

                    //Call the Looper.prepare () method
                    Looper.prepare();

                    mHandlerThread = new Handler(){
                        @Override
                        public void handleMessage(Message msg) {
                            super.handleMessage(msg);
                            Log.e("sub thread","---------> msg.what = " + msg.what);
                        }
                    };

                    mHandlerThread.sendEmptyMessage(1);

                    //Call the Looper.loop () method
                    Looper.loop();
                }
            }).start();

        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

As you can see, by calling Looper.prepare() to work properly, the handleMessage method can receive the sent Message.

Why call this method? Look at the source code.

Source Code Analysis of Handler

Handler's message processing mainly consists of five parts: Message, Handler, Message Queue, Looper and ThreadLocal. First, a brief understanding of the concepts of these objects

Message: Message is a message passed between threads. It can carry a small amount of data internally for exchanging data between threads. Message has four commonly used fields: what field, arg1 field, arg2 field and obj field. What, arg1, arg2 can carry integer data, and OBJ can carry object objects.

Handler: It is mainly used to send and process messages. SendMessage () method is generally used and a series of other sendXXX methods are used. But eventually, sendMessage AtTime method is called. In addition to sendMessage AtFrontOfQueue (), the message sent by this method is eventually passed to Handler's handleMessage method after a series of rounds and turns. Medium.

Message Queue: Message Queue is the meaning of message queue. It is mainly used to store all messages sent through Handler. This part of the message will always exist in the message queue, waiting to be processed. There will only be one MessageQueue object per thread.

Looper: Messages sent by each thread through Handler are stored in Message Queue. Looper calls loop () to enter an infinite loop, and whenever a message is found in Message Queue, it is taken out and passed to Handler's handleMessage () method. There will only be one Looper object per thread.

ThreadLocal: MessageQueue objects, and Looper objects have only one object in each thread. How can we guarantee that it has only one object, which is saved through ThreadLocal? Thread Local is a data storage class within a thread, through which data can be stored in a specified thread. After data storage, data can be stored only in a specified thread, but not in other threads.

Having understood these basic concepts, we went deep into the source code to understand how Handler works.

How MessageQueue works

MessageQueue message queues maintain message lists through a single linked list data structure. The following are the enqueueMessage method and the next () method. As follows:

    boolean enqueueMessage(Message msg, long when) {
            if (msg.target == null) {
                throw new IllegalArgumentException("Message must have a target.");
            }
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }

            synchronized (this) {
                if (mQuitting) {
                    IllegalStateException e = new IllegalStateException(
                            msg.target + " sending message to a Handler on a dead thread");
                    Log.w(TAG, e.getMessage(), e);
                    msg.recycle();
                    return false;
                }

                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                boolean needWake;
                if (p == null || when == 0 || when < p.when) {
                    // New head, wake up the event queue if blocked.
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked;
                } else {
                    // Inserted within the middle of the queue.  Usually we don't have to wake
                    // up the event queue unless there is a barrier at the head of the queue
                    // and the message is the earliest asynchronous message in the queue.
                    needWake = mBlocked && p.target == null && msg.isAsynchronous();
                    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.next
                    prev.next = msg;
                }

                // We can assume mPtr != 0 because mQuitting is false.
                if (needWake) {
                    nativeWake(mPtr);
                }
            }
            return true;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

It can be seen that in this method, a message is inserted into the single linked list mainly according to the order of time.

next () method. as follows

    Message next() {
            // Return here if the message loop has already quit and been disposed.
            // This can happen if the application tries to restart a looper after quit
            // which is not supported.
            final long ptr = mPtr;
            if (ptr == 0) {
                return null;
            }

            int pendingIdleHandlerCount = -1; // -1 only during first iteration
            int nextPollTimeoutMillis = 0;
            for (;;) {
                if (nextPollTimeoutMillis != 0) {
                    Binder.flushPendingCommands();
                }

                nativePollOnce(ptr, nextPollTimeoutMillis);

                synchronized (this) {
                    // Try to retrieve the next message.  Return if found.
                    final long now = SystemClock.uptimeMillis();
                    Message prevMsg = null;
                    Message msg = mMessages;
                    if (msg != null && msg.target == null) {
                        // Stalled by a barrier.  Find the next asynchronous message in the queue.
                        do {
                            prevMsg = msg;
                            msg = msg.next;
                        } while (msg != null && !msg.isAsynchronous());
                    }
                    if (msg != null) {
                        if (now < msg.when) {
                            // Next message is not ready.  Set a timeout to wake up when it is ready.
                            nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                        } else {
                            // Got a message.
                            mBlocked = false;
                            if (prevMsg != null) {
                                prevMsg.next = msg.next;
                            } else {
                                mMessages = msg.next;
                            }
                            msg.next = null;
                            if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                            msg.markInUse();
                            return msg;
                        }
                    } else {
                        // No more messages.
                        nextPollTimeoutMillis = -1;
                    }

                    // Process the quit message now that all pending messages have been handled.
                    if (mQuitting) {
                        dispose();
                        return null;
                    }

                    // If first time idle, then get the number of idlers to run.
                    // Idle handles only run if the queue is empty or if the first message
                    // in the queue (possibly a barrier) is due to be handled in the future.
                    if (pendingIdleHandlerCount < 0
                            && (mMessages == null || now < mMessages.when)) {
                        pendingIdleHandlerCount = mIdleHandlers.size();
                    }
                    if (pendingIdleHandlerCount <= 0) {
                        // No idle handlers to run.  Loop and wait some more.
                        mBlocked = true;
                        continue;
                    }

                    if (mPendingIdleHandlers == null) {
                        mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                    }
                    mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
                }

                // Run the idle handlers.
                // We only ever reach this code block during the first iteration.
                for (int i = 0; i < pendingIdleHandlerCount; i++) {
                    final IdleHandler idler = mPendingIdleHandlers[i];
                    mPendingIdleHandlers[i] = null; // release the reference to the handler

                    boolean keep = false;
                    try {
                        keep = idler.queueIdle();
                    } catch (Throwable t) {
                        Log.wtf(TAG, "IdleHandler threw exception", t);
                    }

                    if (!keep) {
                        synchronized (this) {
                            mIdleHandlers.remove(idler);
                        }
                    }
                }

                // Reset the idle handler count to 0 so we do not run them again.
                pendingIdleHandlerCount = 0;

                // While calling an idle handler, a new message could have been delivered
                // so go back and look again for a pending message without waiting.
                nextPollTimeoutMillis = 0;
            }
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105

The next method is an infinite loop method, where if a message returns the message and is removed from the list, no message is blocked all the time.

Principle of Looper

Every program has an entry, and Android program is based on java. The entry of Java program is a static main function, so the entry of Android program should also be a static main function. In Android program, the static main is in the ActivityThread class. Let's look at the main method, as follows:

     public static void main(String[] args) {
            SamplingProfilerIntegration.start();

            // CloseGuard defaults to true and can be quite spammy.  We
            // disable it here, but selectively enable it later (via
            // StrictMode) on debug builds, but using DropBox, not logs.
            CloseGuard.setEnabled(false);

            Environment.initForCurrentUser();

            // Set the reporter for event logging in libcore
            EventLogger.setReporter(new EventLoggingReporter());

            Security.addProvider(new AndroidKeyStoreProvider());

            // Make sure TrustedCertificateStore looks in the right place for CA certificates
            final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
            TrustedCertificateStore.setDefaultUserDirectory(configDir);

            Process.setArgV0("<pre-initialized>");
            //######
            Looper.prepareMainLooper();

            ActivityThread thread = new ActivityThread();
            thread.attach(false);

            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }

            if (false) {
                Looper.myLooper().setMessageLogging(new
                        LogPrinter(Log.DEBUG, "ActivityThread"));
            }

            Looper.loop();

            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

In the main method, Looper.prepareMainLooper() is called by the system to create the Looper and MessageQueue of the main thread and to open the message loop of the main thread through Looper.loop (). Let's see how Looper.prepareMainLooper() created these two objects. As follows:

     public static void prepareMainLooper() {
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

As you can see, prepare(false); method and myLooper() are called in this method; method, I'm going into these two methods, as follows:

     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));
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

As you can see here, the sThreadLocal object saves a Looper object. First, we determine whether there is a Looper object to prevent it from being called twice. The sThreadLocal object is of ThreadLocal type, thus guaranteeing that there is only one Looper object per thread. What is the Looper object created? Let's go and see, as follows:

  private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
  • 1
  • 2
  • 3
  • 4

As you can see, a MessageQueue object is created in the Looper constructor and the current thread is saved. As you can see from the above, there is only one Looper object in a thread, and the MessageQueue object is created by the Looper constructor, so there will only be one MessageQueue object per thread.

There is another overloaded method for the prepare method:

  public static void prepare() {
            prepare(true);
        }
  • 1
  • 2
  • 3

prepare() is just a package of prepare(boolean quitAllowed), which explains why the Looper.prepare() method is not called in the main thread. Because the system has automatically called the Looper.prepare() method for us when the main thread starts.

A method myLooper() is also called in the Looper.prepareMainLooper () method. Let's go in and see, as follows:

        /**
         * 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();
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

In the call prepare () method, a Looper object sThreadLocal. set (new Looper (quitAllowed)) is saved in the current thread; my Looper () method is to take out the Looper object of the current thread and save it in the sMainLooper reference.

The Looper.loop() method is also called in the main () method, 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;

            // 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.recycle();
            }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

In this method, we enter an infinite loop and constantly get messages from the next method of MessageQueue, which is a blocking operation. When there is no message, the next method is always blocking, when there is a message through msg.target.dispatchMessage(msg); here msg.target is actually the Handler object sent to this message.

Operating mechanism of Handler

Look at how Handler is constructed. As follows:

    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);
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

Let's look at the construction method without Looper objects:

     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;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

You can see that the looper object is null, throwing an exception of "Can't create Handler in thread that has not called Looper.prepare()" from here, we can know that when we use Handler in sub-threads, we need to call Looper.prepare() manually to create a Looper object. The reason why the main thread is not used is that we call Looper.prepare() automatically when the system starts. Method.

The work of handler mainly includes sending and receiving process. The sending of messages is mainly through a series of methods of post and send, and a series of methods of post are ultimately realized through a series of methods of send. The sendMessageAtTime method is used to implement a series of sendMessageAtTime methods, except sendMessageAtFrontOfQueue (). Take a look at these series of send methods, as follows:

    public final boolean sendMessage(Message msg)
        {
            return sendMessageDelayed(msg, 0);
        }

        public final boolean sendEmptyMessage(int what)
        {
            return sendEmptyMessageDelayed(what, 0);
        }  

        public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
            Message msg = Message.obtain();
            msg.what = what;
            return sendMessageAtTime(msg, uptimeMillis);
        }

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

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

        public final boolean sendMessageAtFrontOfQueue(Message msg) {
            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, 0);
        }

        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

As you can see, handler sending a message is actually inserting a message into the message queue. In Looper's loop method, take out the message from Message Queue and call msg.target.dispatchMessage(msg); in fact, this is the dispatchMessage(msg) method of Handler, go in and see as follows:

      /**
         * Handle system messages here.
         */
        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

Judge whether msg.callback is empty or not, and do not use handleCallback(msg) for air conditioning to process messages. In fact, callback is a Runnable object, which is the object Handler sends a post message to.

     public final boolean post(Runnable r)
        {
           return  sendMessageDelayed(getPostMessage(r), 0);
        }

         public final boolean postAtTime(Runnable r, long uptimeMillis)
        {
            return sendMessageAtTime(getPostMessage(r), uptimeMillis);
        }

        public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
        {
            return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
        }


        public final boolean postDelayed(Runnable r, long delayMillis)
        {
            return sendMessageDelayed(getPostMessage(r), delayMillis);
        }


        public final boolean postAtFrontOfQueue(Runnable r)
        {
            return sendMessageAtFrontOfQueue(getPostMessage(r));
        }

        private static Message getPostMessage(Runnable r) {
            Message m = Message.obtain();
            m.callback = r;
            return m;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

Go in to the handleCallback method to see how to process the message, as follows:

      private static void handleCallback(Message message) {
            message.callback.run();
        }
  • 1
  • 2
  • 3
  • 4

As you can see, it's actually the run method that calls back the Runnable object. Activity's runOnUiThread, View's postDelayed method is the same principle. Let's first look at the runOnUiThread method, as follows:

    public final void runOnUiThread(Runnable action) {
            if (Thread.currentThread() != mUiThread) {
                mHandler.post(action);
            } else {
                action.run();
            }
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

View's postDelayed method. As follows:

 public boolean postDelayed(Runnable action, long delayMillis) {
            final AttachInfo attachInfo = mAttachInfo;
            if (attachInfo != null) {
                return attachInfo.mHandler.postDelayed(action, delayMillis);
            }
            // Assume that post will succeed later
            ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
            return true;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Essentially, Runnable's run method is executed in the UI thread.

If msg.callback is null, judge whether mCallback is null? MCallback is an interface, as follows:

       /**
         * Callback interface you can use when instantiating a Handler to avoid
         * having to implement your own subclass 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);
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

CallBack actually provides another way to use Handler, either by deriving subclasses to override handleMessage () methods or by setting CallBack.

Let's sort out the process of using Handler in the main thread.

First, create a Handler object in the main thread and override the handleMessage () method. Then when we need to update the UI in the sub-thread, we create a Message object and send the message through handler. This message is then added to the MessageQueue queue to be processed. Through the Looper object, the message to be processed is always attempted to be retrieved from the MessageQueue, and finally handler Message () method for Handler is distributed.

END...


Reproduced from http://blog.csdn.net/u012827296/article/details/51236614

Topics: Android Java network Database