Android: Handler message mechanism

Foreword

AndroidThe principle message mechanism is Androidone of the advanced knowledge must point in Androidthe interview is one of the frequently asked questions. In Androidthe sub-thread operation you are not directly Viewneed to switch to the main thread. This action then switches to relate to the Androidmessage mechanism, which is paper talk about Handler, Looper, MessageQueue, Message relations between them.

Handler

HandlerPlaying in the message mechanism to send messages and process messages role, which we usually have the most contact classes.

How Handler process the message?

The following code shows Handlerhow to handle the message. New Handlerobjects, and rewrite the handleMessage, the processing logic within the associated methods, and in general the processing logic associated with the main thread. There are many constructors Handler, the following configuration is used in the main thread.

  private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if (msg.what==1){
                Toast.makeText(MainActivity.this,"handle message",Toast.LENGTH_LONG).show();
            }
            
        }
    };
    
复制代码

Handler is how to send a message it?

Can understand the following code, Handlerobject supports sending Messageand Runable. Runable finally packed into Messagethe callbackinstance variable ( Handlerobject processing message priority callbacklogic), and Messagein the same way into the message queue. And each method has an associated deformation, supports delayed transmission, or some time in the future to send more.

    //在消息池获取消息体,能达到消息重用,如果消息池没有消息,则新建消息
        Message msg = handler.obtainMessage();
        msg.what = 1;
        //发送消息
        handler.sendMessage(msg);
        //发送空消息,参数会自动被包装msg.what=1
        handler.sendEmptyMessage(1);
        //未来的时间里发送消息
        handler.sendEmptyMessageAtTime(1, 1000);
        //延迟发送消息
        handler.sendEmptyMessageDelayed(1, 1000);

        msg = handler.obtainMessage();
        msg.what = 2;
        //将消息发送消息队列前面
        handler.sendMessageAtFrontOfQueue(msg);
        //发送任务,run方法内容将handler被处理。
        handler.post(new Runnable() {
            @Override
            public void run() {
                Log.i("Handler", "Runnable");
            }
        });
复制代码

If you normally use, we need only main process definition Hanlderprocess contents of the message, the message is sent in sub-thread can be achieved by switching processes.

Looper

LooperIs responsible for taking the message from the message queue cycle, is sent to Handlerthe processing. Since only the message queue for storing messages, it is necessary to Loopercontinue to take a message from the message queue Handler. By default, all the threads do not own Looper. If executed directly in the sub-thread Looper.loopmethod, an exception occurs. Why is that the main thread without error? App start the process of creating ActivityThread, the call will Looper.preparebe created Looperand MessageQueue, and Looper.loopopen loop. The system is created in the main thread for us Looperand MessageQueue. So, to create a sub-line Handlerbefore, you need to call the Looper.preparemethod, created Looperand MessageQueue. IntentServiceIs one such implementation. Click to see the point IntentService of knowledge.

MessageQueue

MessageQueue list in the form of internal organization, the main role is to store Message. Creating Loopertime, it is automatically created MessageQueue.

The relations between the formation mechanism of the Android news

HandlerWhen sending a message sends a message is inserted into MessageQueue, and Loopercontinued from the MessageQueuetake message distributed Handlerprocessing.

Source resolve

The building Handler

We look at Handler constructor code.

    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 " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        //分析二
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    
复制代码

HandlerThere are many overloaded constructor, we often use the default constructor, it will eventually call the above constructor.

Analysis of a

By Looper.myLooper(), by the Looperinstance. In the myLooperimplementation, by ThreadLocalthe getway to get the. If ThreadLocalnot present Looper, then the back null. ThreaLocalHere you can save the current thread simply understood as the private independent instances, other threads can not access. If ThreadLocalthere is no Looperinstance of the return null. This is said earlier in the child thread is created Handlerbefore, we need to call the Looper.preparemethod. Otherwise it will be thrown RuntimeException.

 public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
复制代码

Analysis II

mQueueIs a Loopermessage queue, mCallBackan interface is defined, for the callback message processing.

  public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        public boolean handleMessage(Message msg);
    }
复制代码

Send Message Handler

Handler sends a message to all variants of the method eventually put in the following way to the message queue.

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

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

复制代码

The most important thing is this enqueueMessagemethod, the current Handlerobject is set to Messagethe targetvariable. Then call queue queueof enqueueMessagemethods.

    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;
            //如果队列为空或者插入message未来处理时间小于当前对头when
            //则将当前消息设为队列头
            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 {
                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;
    }

复制代码

In MessageQueuethe enqueueMessagemethod, it will first check targetwhether null, messagewhether it should be in use, whether to exit the current thread, the state of death. If an exception is thrown. If the current queue is empty or blocked, direct current Messagetarget is set to wake up the thread and the head of the queue. If not, then in accordance with Messagethe object wheninserted into the appropriate queue position. Thus you can see, Handlerwhen the message is sent to the message on the queue.

Looper and create MessageQueue

Recall that child threads Handler, you need to call Looperthe static preparemethod.

 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));
    }
复制代码

If the current thread already Looper, alternative Looper will error. If not, new Looperand to save ThreadLocalin. new LooperVery simple, just create a new one MessageQueue, and holds the current thread.

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

Looper is how to achieve the cycle of

In the call to the Looper.preparecreation Looperand MessageQueuethe object to be called Loop.loopto start the cycle of distribution message queue messages.

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

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);

        boolean slowDeliveryDetected = false;
        //分析一
        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
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long traceTag = me.mTraceTag;
            long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
            long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
            if (thresholdOverride > 0) {
                slowDispatchThresholdMs = thresholdOverride;
                slowDeliveryThresholdMs = thresholdOverride;
            }
            final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
            final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

            final boolean needStartTime = logSlowDelivery || logSlowDispatch;
            final boolean needEndTime = logSlowDispatch;

            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }

            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            try {
                //分析二:
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (logSlowDelivery) {
                if (slowDeliveryDetected) {
                    if ((dispatchStart - msg.when) <= 10) {
                        Slog.w(TAG, "Drained");
                        slowDeliveryDetected = false;
                    }
                } else {
                    if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                            msg)) {
                        // Once we write a slow delivery log, suppress until the queue drains.
                        slowDeliveryDetected = true;
                    }
                }
            }
            if (logSlowDispatch) {
                showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }


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

复制代码

Analysis of a: by unlimited forloop to read the message queue. And MessageQueuethe nextinternal method, according to the form of the linked list whento return the order of attributes message.

: Two analysis calling Messagethe object target's dipatchMessagemethods. Here targetit is to send a message Handlerobject. In the Handlerobject dipatchMessagemethod, priority execution Messageobject callbackmethod, that takes precedence when we send a message to Runablethe task of sending, if any. Otherwise, the detection Callbackobject handleMessagemethods, and finally we rewrite the Hanlderobject's handleMessagemethods. Because Handlernot only have a default constructor, and can be passed Callback, Looperthe constructor and the like.

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

Message reuse

By handler.obtainMessagenot newget the message by way of example. Because the obtainMessagemethod will first detect whether there is a message cell multiplexed message may not go to newa message instance. The following is a Message class obtainmethod.

public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
复制代码

sPoolType is Message, by members of the internal variables next, maintains a news pool. Although called the news pool, but through internal nextnext constantly pointing Messageto the list maintained by the news pool, the default size is 50. In the chain sPoolcase is not empty, to take the header Messageelement and associated attributes are initialized.

Message objects are then put into the pool in a message when?

In Looperthe loopmethod, and finally call Messagethe recycleUncheckedmethod

void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }
复制代码

Synchronization code block can be seen that the sPoolpoint to be currently recycled Messageobjects, and Messagethe nextpoint before the header.

to sum up

  1. In sub-threads Handler, you need to call the Looper.preparemethod, and then call the Looper.loopmethod.
  2. It maintains a message queue in the form of a linked list, and storing the message in accordance with the order of acquisition whenin order of priority time.
  3. By Handler, time-consuming operation in the child thread, the main thread update the UI. IntentServiceScenario: HandlerTread, AsyncTack, .

Sharing knowledge

HandlerThread must know will be

IntentService must know will be

If you feel that a useful article, the article points to a praise, iron son

Guess you like

Origin blog.csdn.net/weixin_34010949/article/details/91372140