zoukankan      html  css  js  c++  java
  • Android Handler MessageQueue Looper 消息机制原理

    提到Android里的消息机制,便会提到Message、Handler、Looper、MessageQueue这四个类,我先简单介绍以下这4个类
    之间的爱恨情仇。

    Message

    消息的封装类,里边存储了消息的详细信息,以及要传递的数据

    Handler

    主要用在消息的发送上,有即时消息,有延迟消息,内部还提供了享元模式封装了消息对象池,能够有效的减少重复对象的创建,留更多的内存做其他的事,

    Looper

    这个类内部持有一个MessageQueue对象,当创建Looper的时候,同时也会创建一个MessageQueue,然后Looper的主要工作就不断的轮训MessageQueue,轮到天荒地老的那种

    MessageQueue

    内部持有一个Message对象,采用单项链表的形式来维护消息列队。并且提供了入队,出队的基础操作

    举个现实中的栗子,Message就相当于包装好的快递盒子,Handler就相当于传送带,MessageQueue就相当于快递车,Looper就相当于快递员,联想一下,来个快递盒子,biu丢到传送带上,传送带很智能,直接传送到快递三轮车里,然后快递小哥送一波~,日夜交替,不分昼夜的工作,好家伙,007工作制

    消息机制的初始化

    好,我们把这4个家伙从头到位分析一遍,要想使用Android的消息,首先要创建Looper对象,Android系统已经帮我们在UI线程内创建好了一个,我们可以看一下

    public final class ActivityThread extends ClientTransactionHandler {
        /**
         * The main entry point from zygote.
         */
        public static void main(String[] args) {
            Looper.prepareMainLooper();
    
            ActivityThread thread = new ActivityThread();
            thread.attach(false, startSeq);
    
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
    
            if (false) {
                Looper.myLooper().setMessageLogging(new
                        LogPrinter(Log.DEBUG, "ActivityThread"));
            }
    
            // End of event ActivityThreadMain.
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            Looper.loop();
    
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
    }
    

    ActivityThread这个类大家应该不陌生吧,没错,他就是我们App的主线程管理类,我们看到他调用了 prepareMainLooper 来初始化,然后 loop,天荒地老的那种loop,这个loop,我们最后聊

    我们看一下Looper内部提供的 prepareMainLooper 实现

    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 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));
    }
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    

    上边涉及到了3个方法,我都贴出来了,首先 quitAllowed 这个参数代表该Looper是否可以退出,我们主线程内的Looper是不允许退出的,所以封装了 prepareMainLooper 方法和 prepare 方法已做区分,我们项目中平时用的都是 prepare 方法,因为是子线程,所以允许退出Looper,大家在子线程内用完记得调用quit哦~
    这里我们看Looper内部是通过ThreadLocal维护的Looper对象,也就是说每个线程都是相互独立的。而且Looper做了限制,每个线程内部只能存在一个Looper对象,等同于每个线程内只能有一个MessageQueue
    最后在Looper的构造方法内,创建了一个MessageQueue对象,整个Looper的初始化就结束了

    创建消息

    我们准备好了Looper和MessageQueue后,就可以创建消息啦,接下来我们创建一个消息吧

    //直接new对象,不推荐的方式
    Message msg = new Message();
    //推荐:内部是一个复用对象池
    Message message = handler.obtainMessage();
    message.what = 1;
    message.obj = "hello world";
    

    发送消息(入队)

    我们发送消息的时候,都是会借助Handler的sendMessage就可以把消息发送到列队里了,我们往下看是如何完成的入队操作吧,首先我们平时都是创建一个Handler,然后调用sendMessage就可以了

    Handler handler = new Handler();
    handler.sendMessage(message);
    

    我们先看一下Handler的构造方法

    public Handler() {
        this(null, false);
    }
    public Handler(@Nullable Callback callback, boolean async) {
        //FIND_POTENTIAL_LEAKS一直都是false,所以不用关心这个逻辑
        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());
            }
        }
        //得到当前线程下的Looper对象
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        //从Loopper内部获取一个列队
        mQueue = mLooper.mQueue;
        // 回调对象,我们平时写的时候,一般都是用类集成的方式重写 handleMessage 方法
        mCallback = callback;
        //标示当前Handler是否支持异步消息
        mAsynchronous = async;
    }
    

    其实构造方法很简单呐,就是获取Looper对象,然后初始化列队和回调对象就完事了,我们继续看sendMessage然后看消息的入队吧

    public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0);
    }
    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    public boolean sendMessageAtTime(@NonNull 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);
    }
    

    通过内部的重载方法,一直调用到sendMessageAtTime方法,在这里得到Handler内部的MessageQueue对象,然后调用了 enqueueMessage 方法准备入队

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

    这里调用了MessageQueue的enqueueMessage方法真正入队,我们继续看一下

    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;
            //如果链表是空的,或者当前消息的when小于表头的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 {
                // 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放到链表最后
                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;
    }
    

    通过这个方法,我们了解到MessageQueue是通过Message的单链结构存储的,然后每次入队的时候,都会
    通过这个enqueueMessage方法向链表的最末尾添加数据。

    最后我们聊一下Looper下的loop方法吧

    接下来我们看一下

    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 (;;) {
            //queue的next会阻塞
            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);
            }
            // Make sure the observer won't change while processing a transaction.
            final Observer observer = sObserver;
    
            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;
            Object token = null;
            if (observer != null) {
                token = observer.messageDispatchStarting();
            }
            long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
            try {
                //派发消息,执行回调handleMessage
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                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);
            }
    
            // 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.recycleUnchecked();
        }
    }
    

    Looper内的loop方法别看这么多,大多数都是日志相关的处理。其实他就两件事
    第一件事就是从列队中通过next取出Message对象
    第二件事就是通过Message对象上绑定的target对象dispatchMessage方法,来分发消息
    我们接下来看一下dispatchMessage方法,然后在看MessageQueue的next

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

    灰常简单,判断CallBack对象。然后调用handleMessage就完事了,我们的Activity就收到数据了。
    接下来我们看看MessageQueue的next是怎么获取列队内的消息的把。

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

    首先MessageQueue的消息是用单链表的形式存储,然后next函数做的事情就是死循环获取消息,
    在获取消息的时候判断一下消息是否符合执行时间,如果不符合执行时间,就进入睡眠状态等待消息。
    如果符合执行时间就直接返回Message给Looper进行分发,如果Message链表都为空。则睡眠时间是-1
    代表无休止的睡眠。在无休止睡眠的状态下,enqueueMessagenativeWake方法,会进行一次唤醒,唤醒后next函数继续执行,判断返回消息给Looper执行消息分发

    一个被程序耽误的画手
  • 相关阅读:
    影响指令流水线的因素
    硬布线控制器和微程序控制器 
    总线仲裁
    计算机专业面试
    P、NP、NPC、NPH问题的区别和联系
    多态
    软件生命周期
    你所不知道的js的小知识点(1)
    html span标签 不换行(有时span带中文时候是可以自动换行的)
    form的onsubmit事件--表单提交前的验证最佳实现方式
  • 原文地址:https://www.cnblogs.com/kezhuang/p/13759497.html
Copyright © 2011-2022 走看看