Written in Jan 2018-07-20 13:27

Hi~ Let’s talk about Android’s message handling mechanism


Message handling mechanism

Related classes

  • Message: the Message, which contains the Message ID, the processing object, and the processing data, managed by the MessageQueue unified queue, and finally processed by the Handler.
  • Handler: Is responsible for sending and processing messages.
  • MessageQueue: MessageQueue, which stores messages according to FIFO.
  • Looper: Message pump to process messages in MessageQueue.
The Message, the MessageQueue, Handler, the relationship between the stars

Look at the code

Looper.class
variable
/ static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); /** * hold mainlooper */ private static Looper sMainLooper; // guarded by looper. class /** * each Looper has a MessageQueue */ final MessageQueue; /** * Each Looper holds a Thread */ final Thread mThread;Copy the code
methods
/** * Create a Looper */ public static void prepare() {prepare(true); } @param quitAllowed */ 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)); } /** * A Looper that initializes a main thread can only be created with one that has already been called and created when the application is started. So we don't need to create it ourselves, otherwise we'll throw an exception. */ public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper ! = null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); }} public static Looper getMainLooper() {synchronized (looper.class) {return sMainLooper; } /** * 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); . }} /** * public static @looper myLooper() {return sthreadLocal.get (); } public static @nonnull MessageQueue myQueue() {return myLooper().mqueue; } private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } @return */ public Boolean isCurrentThread() {return thread.currentThread () == mThread; }Copy the code
MessageQueue.class
** @param MSG Message entity * @param when Message is sent * @return ** / Boolean enqueueMessage(Message MSG, long when) { ...... synchronized (this) { ..... msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; If (p = = null | | the when = = 0 | | the when < p.w hen) {/ * * * put the message in the message queue, to awaken * / MSG. Next = p; mMessages = msg; needWake = mBlocked; } else {/** * insert the message to the appropriate location */ 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 ()) {// If MSG is asynchronous, it is not the first asynchronous message in the list, so needWake = false is not necessary; } } 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; }Copy the code

There’s a syncBarrier, but it’s all @hide, and you still need Java reflection. A barrier blocks non-asynchronous messages at some point and must be removed if messages are to be retrieved.

Message.class
/** * public int what; /** * message entity argument */ public int arg1; /** * message entity argument */ public int arg2; /** * public Object obj; /** *package*/ long when; /** *package*/ Bundle data; /*package*/ Handler target; / /*package*/ Runnable callback; /** * private static Message sPool; /** * private static int sPoolSize = 0; Private static final int MAX_POOL_SIZE = 50; private static final int MAX_POOL_SIZE = 50;Copy the code

Message objects should be obtained through the obtain method, because to avoid mass creation of Message objects, Message obtains them from a pool of objects it maintains.

Obtain method
  • public static Message obtain()
  • public static Message obtain(Message orig)
  • public static Message obtain(Handler h)
  • public static Message obtain(Handler h, Runnable callback)
  • public static Message obtain(Handler h, int what)
  • public static Message obtain(Handler h, int what, Object obj)
  • public static Message obtain(Handler h, int what, int arg1, int arg2)
  • public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj)
Set handler
  • public void setTarget(Handler target)
Send a message to the handler
  • public void sendToTarget()
Working with bundled data
  • public Bundle getData()
  • public Bundle peekData()
  • public void setData(Bundle data)
Handler.class
/** * public interface Callback {public Boolean handleMessage(Message MSG); Public void handleMessage(Message MSG) {} @param Message */ when mssage.callback is not empty private static void handleCallback(Message message) { message.callback.run(); } public void dispatchMessage(MSG) {if (MSG. Callback! HandleMessage */ handleCallback(MSG); } else {/** * if mCallback is defined in this Handler, execute the contents of the mCallback first. * If mCallback returns true, the handleMessage in mCallback does not need to be processed by subsequent handleMessage handlers. * If mCallback returns false, */ If (mCallback! = null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}Copy the code

There are two handlecallbacks, a static method and a normal method; The timing of the call varies; static methods are called when the callback is defined in Message.

Processing message logic for the dispatchMessage() method

  • Step 1: If there is no callback function in Message, we simply execute the static method handleMessage in handler
  • Step 2: If the mCallback is defined in Handler, execute the mCallback first.
  • Step 3: If the mCallback returns true, the handleMessage processing in the mCallback is complete and does not need to be handled by subsequent handleMessages in the Handler

– Step 4: If mCallback returns false, the handleMessage in mCallback has not finished processing the message, and the handleMessage in Handler has continued processing

Corresponding to the method in Message that gets the Message object
  • public final Message obtainMessage()
  • public final Message obtainMessage(int what)
  • public final Message obtainMessage(int what, Object obj)
  • public final Message obtainMessage(int what, int arg1, int arg2)
  • public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
Receive messages with Runnable, which is a normal method that runs in the current thread, unlike Runnable in threads.
  • public final boolean post(Runnable r)
  • public final boolean postAtTime(Runnable r, long uptimeMillis)
  • public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
  • public final boolean postDelayed(Runnable r, long delayMillis)
  • public final boolean postAtFrontOfQueue(Runnable r)
Send message directly
  • public final boolean sendMessage(Message msg)
  • public final boolean sendEmptyMessage(int what)
  • public final boolean sendEmptyMessageDelayed(int what, long delayMillis)
  • public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
  • public final boolean sendMessageDelayed(Message msg, long delayMillis)
  • public boolean sendMessageAtTime(Message msg, long uptimeMillis)
  • public final boolean sendMessageAtFrontOfQueue(Message msg)



My name is Lu Daxu.

A boring programmer who knows something about psychology. Tip, follow and like the article whether or not you get anything from it!