• Android线程的消息队列


    一、子线程创建Handler的前题条件是什么

      子线程创建Handler必须先创建子线程Looper。

      假如,在子线程不先创建Looper会怎样?

    Thread {
        _handler = Handler()
    }.start()

      结果:

    E/AndroidRuntime: FATAL EXCEPTION: Thread-7
        Process: com.example.demoapp, PID: 8456
        java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-7,5,main] that has not called Looper.prepare()
            at android.os.Handler.<init>(Handler.java:205)
            at android.os.Handler.<init>(Handler.java:118)
            at com.example.demoapp.MainActivity2.onCreate$lambda-0(MainActivity2.kt:17)
            at com.example.demoapp.MainActivity2.lambda$Qqlg1xygUvXBrkBzlVLkUUn-c9c(Unknown Source:0)
            at com.example.demoapp.-$$Lambda$MainActivity2$Qqlg1xygUvXBrkBzlVLkUUn-c9c.run(Unknown Source:2)
            at java.lang.Thread.run(Thread.java:764)

       原因,从Handler构造函数源码:

    public Handler(@Nullable 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;
    }

      创建Handler的线程没有创建Looper,调用Looper.prepare()就是抛出异常。Looper是Android提供线程间通信Handler最重要的组成部分,Handler发送的消息都需要Looper进行处理。

    二、主线程Looper与子线程Looper区别

       主线程启动Looper调用Looper.prepareMainLooper()函数:

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

      Looper.prepareMainLooper()函数实现:

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. See also: {@link #prepare()}
     *
     * @deprecated The main looper for your application is created by the Android environment,
     *   so you should never need to call this function yourself.
     */
    @Deprecated
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

      子线程创建Looper调用prepare()函数:

    /** Initialize the current thread as a looper.
     * This gives you a chance to create handlers that then reference
     * this looper, before actually starting the loop. Be sure to call
     * {@link #loop()} after calling this method, and end it by calling
     * {@link #quit()}.
    */
    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));
    }

      1. 主线程和子线程最终调用函数prepare(boolean quitAllowed),主线程调用prepare(false),Looper不允许退出,子线程调用prepare(true),Looper允许退出。

      2. 主线程Looper除了存储在sThreadLocal中,还赋值给了静态变量sMainLooper,通过getMainLooper()函数返回的就是这个静态变量。

    /**
     * Returns the application's main looper, which lives in the main thread of the application.
     */
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

    三、消息队列是怎么创建的

      在调用prepare(boolean quitAllowed)函数时,创建Looper对象实例,在Looper构造函数中创建消息队列MessageQueue。

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

    四、Looper与消息队列的关系

      

    五、Hnadler延迟消息实现原理

      Handler中发送延迟消息函数sendMessageDelayed():

    /**
     * Enqueue a message into the message queue after all pending messages
     * before (current time + delayMillis). You will receive it in
     * {@link #handleMessage}, in the thread attached to this handler.
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    
    /**
     * Enqueue a message into the message queue after all pending messages
     * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
     * Time spent in deep sleep will add an additional delay to execution.
     * You will receive it in {@link #handleMessage}, in the thread attached
     * to this handler.
     * 
     * @param uptimeMillis The absolute time at which the message should be
     *         delivered, using the
     *         {@link android.os.SystemClock#uptimeMillis} time-base.
     *         
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    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);
    }

      sendMessageDelayed()函数中调用sendMessageAtTime(, uptimeMillis)函数,参数uptimeMillis是消息添加的时间,消息队列是一个单向链表,按消息发送时间(ms)排序。发送延迟消息是当前时间+延迟时间,最后调用qnqueueMessage(queue, msg, uptimeMillis)函数向队列中添加消息。

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

      在Handler.enqueueMessage()函数中调用到MessageQueue.enqueueMessage()函数:

    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
    
        synchronized (this) {
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }
    
            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;
            // (1)
            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;
            }
    
            // (2)
            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

      在enqueueMessage()函数中标识(1)的部分逻辑,when是消息添加时间,新消息添加时间是否是0或者添加小于当前队列头部消息时间,将新消息添加到队列头,当前新消息时间大于队列头消息,在队列中找到一个适合的位置添加消息。

      在Handler中发送延迟消息,消息是延迟处理,而非延迟添加。

    六、线程间消息传递机制

      Looper.loop是消息循环重要的函数:

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        final Looper me = myLooper();
        me.mInLoop = true;
        final MessageQueue queue = me.mQueue;
        ……
        for (;;) {
            // (1)
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ……
            try {
                // (2)
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } 
            ……
            msg.recycleUnchecked();
        }
    }

      在Looper.loop()函数中做了两件事:

      • 从队列中获取将要处理的消息。
      • 消息分发。

      获取消息:

    Message msg = queue.next(); // might block

        通过queue.next()函数获取要处理的消息。

    @UnsupportedAppUsage
    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();
            }
            // (1)
            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;
                // (2)
                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) nativePollOnce()函数,堵塞队列,等待有新消息加入队列,或者堵塞时间超时,检查队列是否有消息。nativePollOnce()函数是Native层函数:

          JNI层函数,android_os_MessageQueue_nativePollOnce():

    static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
            jlong ptr, jint timeoutMillis) {
        NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
        nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
    }

        Native层函数,NaitveMessageQueue.pollOnce()函数:

        源码:frameworks/base/core/jni/android_os_MessageQueue.cpp

    class NativeMessageQueue : public MessageQueue, public LooperCallback {
    
    }
    
    class MessageQueue : public virtual RefBase {
        public:
            /* Gets the message queue's looper. */
            inline sp<Looper> getLooper() const {
                return mLooper;
            }
            ……
        protected:
            MessageQueue();
            virtual ~MessageQueue();
    
        protected:
            sp<Looper> mLooper;
    };

         NativeMessageQueue继承自Native层的MessageQueue,Native层的MessageQueue中mLooper变量。

        (2) 在消息队列中获取消息。

      消息分发:

      分发消息通过msg.target.dispatchMessage(msg)函数,msg.target是发送消息的Handler对象。在Handler.dispatchMessage()函数:

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(@NonNull Message msg) {
        // (1)
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            // (2)
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // (3)
            handleMessage(msg);
        }
    }

      (1) msg.callback != null,通过handleCallback()函数执行消息,msg.callback是Handler.post(runnable)的runnable函数体。Handler.post()函数将Runnable包装成一个Message,callback就是Runnable。

      Handler.post()函数实现:

    /**
     * Causes the Runnable r to be added to the message queue.
     * The runnable will be run on the thread to which this handler is 
     * attached. 
     *  
     * @param r The Runnable that will be executed.
     * 
     * @return Returns true if the Runnable was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean post(@NonNull Runnable r) {
        return  sendMessageDelayed(getPostMessage(r), 0);
    }
    
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

      m.callback = r,给runnable callback包装成Message结构。

      (2) mCallback != null,mCallback是Handler全局的Callback,在实例化Handler时传入的Callback。通过mCallback.handleMessage(msg),在Callback.handleMessage() == false,继续执行Handler类中重写的Handler.handleMessage()函数,这个函数前并消息清楚Message可能被修改了。通常Hook Handler的消息处理就是在这里,将Callback.handleMessage()的返回值设置false。当前返回值为ture处理消息后return。

      (3) Handler.handleMessage()函数

  • 相关阅读:
    Maven插件系列之spring-boot-maven-plugin
    Java中getResourceAsStream的用法
    【redis】【linux】-bash: redis-cli: 未找到命令
    【bat】杀死指定端口
    【bat】查看端口占用情况
    【java】【springboot】nohup: 无法运行命令'java': 没有那个文件或目录
    【springboot】启动指定内存大小
    【java】【spring】源码分析,@AliasFor互为别名
    【idea】设置安装插件位置
    【记录】【springboot】java.lang.NoClassDefFoundError: org/w3c/dom/ElementTraversal解决
  • 原文地址:https://www.cnblogs.com/naray/p/15367241.html
Copyright © 2020-2023  润新知