android线程间消息处理机制(Handler,Looper,MessageQueue,Message)

线程

文章目录

  • 线程
    • Looper
    • MessageQueue
      • IdleHandler
        • queueIdle()
    • Message
    • Handler

这几个类中核心函数如下,通过他们我们就可以简单了解线程间消息通信原理
android线程间消息处理机制(Handler,Looper,MessageQueue,Message)_第1张图片

Looper

对线程中消息队列(MessageQueue)进行循环获取到Message,然后对Message进行分发处理,在Handler中调用Callback或handleMessage()来处理事件
Thread在默认情况下(主线程除外,主线程在程序启动时就已经创建了Looper对象)并没有与它关联的Looper,所以在handler对象生成之前需要调用Looper.prepare()来创建与线程关联的Looper对象,在线程中任务开始执行时,调用Looper.loop()来循环对应线程中的消息,

 class LooperThread extends Thread {
     public Handler mHandler;
     public void run() {
         Looper.prepare();
         mHandler = new Handler() {
             public void handleMessage(Message msg) {
                 // do something()
             }
         };
         Looper.loop();
     }
  • static final ThreadLocal sThreadLocal
    通过ThreadLocal持有Looper对象保证了线程之间的Looper对象互不影响
  • final MessageQueue mQueue;
    需要执行循环的消息队列
  • final Thread mThread;
    持有该Looper的Thread对象
  • private static Looper sMainLooper
    App开启动时就调用如下函数生成了sMainLooper对象,它处于主线程中
public static void main(String[] args) {
	Looper.prepareMainLooper();
}
  • Looper.prepare(boolean quitAllowed)
    quitAllowed:表示是否可以使用quit()
    子线程中quitAllowed一般都是默认为true,我们可以控制;但是在主线程中为false.不可更改,它由系统控制
private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {// 每个线程中都只有一个Looper,如果重复调用该函数就会抛出异常
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
  • 构造方法 Looper()
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

初始化Looper时,创建MessageQueue,同时获取与该Looper对象对应的线程

  • loop()
    public static void loop() {
        final Looper me = myLooper();// 获取当前线程对应的Looper对象
        if (me == null) { // 该线程没有初始化Looper
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue; //获取指定线程中Looper的消息队列
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // 消息队列中没有消息队列已经调用了quit函数.
                return;
            }
            // 调用handler的dispatchMessage()对消息进行处理
            msg.target.dispatchMessage(msg);
            // 回收消息,将消息内部数据置为空
            msg.recycleUnchecked();
        }
    }

注意:

  1. loop()执行之前必须确定它所在的线程中已初始化Looper对象
  2. Looper中的MessageQueue调用quit()后,清空消息队列,Message回收处理
  • quit()
  • quitSafely()
    它们都会调用MessageQueue.quit();
    void quit(boolean safe) {
            if (safe) {
                removeAllFutureMessagesLocked();// 清空MessageQueue消息池中所有的延迟消息(通过postDelay()...发送的消息)并将消息池中所有的非延迟消息派发出去让Handler去处理
            } else {
                removeAllMessagesLocked();// 清空MessageQueue中的所有消息
            }
        }
    }

quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息,调用quit之后,Looper就不再接收新的消息,因为消息队列已经退出了.
ActivityThread.java中,在退出程序时会调用主线程中Looper.quit()

case EXIT_APPLICATION:
        if (mInitialApplication != null) {
           mInitialApplication.onTerminate();
         }
         Looper.myLooper().quit();
 break;

通过上述介绍,我们可以知道:

  1. 每个线程Thread都有与之对应的一个Looper对象
  2. 该Looper对象中含有一个MessageQueue,Looper通过loop()不断循环得到位置的Message,Message通过他的属性target(Hanlder对象)调用dispatchMessage将消息传入到handleMessage()中
  3. 在创建Handler之前应该先创建对应线程的Looper对象,(Looper中通过ThreadLocal存储looper对象),ThreadLocal可以使不同线程持有不同的Looper对象,即实现了多线程之间数据安全性

ThreadLocal
为每一个使用该变量的线程提供一个变量值的副本,是java中一种较为特殊的线程绑定机制,即:每一个线程都可以独立的改变自己的副本,而不会和其他线程的副本发生冲突.在ThreadLocal中有一个Map,用于存储每一个线程的变量副本.其中,key为当前线程,value为数据,不同的线程的key是不同的,所以它们之间的数据不会发生错误.

MessageQueue

保存线程通信时用到的消息,对消息进行插入,获取,移除回收操作;
我们一般说他是消息队列包含所有的消息,但是实际上它只包含一个Message,而在每Message中有一个Message对象next,就这样每个Message都有一个next指向下一个Message对象,从而形成了一条数据链.
android线程间消息处理机制(Handler,Looper,MessageQueue,Message)_第2张图片

  • Long mPtr; // MessageQueue对象地址
  • Message mMessages:当前消息队列头部消息
    …其他的在下面的函数介绍中会介绍到这里就不说了

MessageQueue是通过enqueueMessage()和next()来执行Message的插入和取出

  • 构造函数
    MessageQueue数据实际上是在native底层初始化的,从代码中就可以看出来
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }
	// mPtr是从android_os_MessageQueue_nativeInit返回的MessageQueue的地址 
	static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
	    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
	    if (!nativeMessageQueue) { 
	        return 0;// MessageQueue初始化失败
	    }
	    nativeMessageQueue->incStrong(env);
	    return reinterpret_cast<jlong>(nativeMessageQueue);
	}
  • enqueueMessage(Message msg, long when)
    将数据插入队列中
    android线程间消息处理机制(Handler,Looper,MessageQueue,Message)_第3张图片
boolean enqueueMessage(Message msg, long when) {
		// 1.判断消息是否可用
		// 2.开始循环MessageQueue中的消息,将Message插入到对应的位置
        synchronized (this) {
            if (mQuitting) {// 该消息队列已结束循环,Message插入失败,回收message
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                //将Message插入到MessageQueue队列的头部
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;// 如果阻塞,则需要唤醒.
            } else {
                //通过循环找出msg在队列中的位置(按照时间顺序(从小到大)),然后将Message插入到队列中对应的位置
                // 判断是否需要唤醒队列:如果队列阻塞,头部消息是Barrier并且插入的消息是异步消息则有可能需要唤
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    // p.next = msg,判断是否需要唤醒:消息队列中有异步消息并且执行时间在新消息之前,所以不需要唤醒。
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }// 将数据插入到数据链中
                msg.next = p;
                prev.next = msg;
            }
            // 如果需要唤醒Looper,调用nativeWake取消阻塞
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

从上面我们可以看出

  1. MessageQueue中的数据结构为单链表,顺序是Message被执行的时间大小,(从小到大)
  2. 如果队列头部消息为空或者插入的消息可以立即执行的话,将数据插入到队列头部时
  3. 否则队列会进行循环,将指定的Message插入到合适位置.
  4. 消息插入到队列时,可能需要调用nativeWake来唤醒Looper
  • next()

android线程间消息处理机制(Handler,Looper,MessageQueue,Message)_第4张图片

```java
Message next() {
		//消息队列已不存在 
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
        int nextPollTimeoutMillis = 0; // 阻塞时长
        for (;;) {
 			//阻塞操作,等待唤醒或者等待nextPollTimeoutMillis时长
            nativePollOnce(ptr, nextPollTimeoutMillis);// 阻塞操作
            synchronized (this) {// 同步操作为了数据安全和准确性
                // uptimeMillis用来判断时间(不受系统时间影响).
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // 这种情况是队列中当前消息是barrier类型的,寻找队列中下一条的异步消息
                    // Barrier同步障碍消息(用来阻塞队列的),异步消息不受该消息的影响,
                    // postSyncBarrier():向MessageQueue中添加同步障碍消息
                    // removeSyncBarrier():移除消息队列中对应的障碍消息;
                    do {
                        // 获取异步或null Message
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) { // 该消息处于阻塞状态
                        // 设置消息阻塞时间
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else { // 处于唤醒状态
                        // 将得到的next Message
                        return msg;
                    }
                } else {
                    // 队列中没有消息
                    nextPollTimeoutMillis = -1;
                }

                // 结束消息循环,清空消息队列,释放内存
                if (mQuitting) {
                    dispose();
                    return null;
                }
        }
    }

  • public int postSyncBarrier()
  • private int postSyncBarrier(long when)
  • public void removeSyncBarrier(int token)

Barrier: 它也是一种Message,在next()函数中就有简单介绍
消息的target为null就表示它是个Barrier,在MessageQueue中,有两种向消息队列中添加消息的函数,一种是enqueueMessage,另一种是enqueueBarrier,而enqueueMessage中如果mst.target为null是直接抛异常的,这个在源码中可以看到.
通过enqueueBarrier往消息队列中插入一个BarrierMessage,队列中消息执行时,在这个Barrier以后的同步消息都会被这个Barrier拦截阻塞住无法执行,直到我们调用removeBarrier移除了这个Barrier,或者异步消息. 因为异步消息不受它的影响,可以继续执行.

  • removeCallbacksAndMessages(Handler h, Object object)

  • removeMessages(Handler h, Runnable r, Object object)
    移除消息队列的Message,我们通过调用handler.remove()来调用它们.

  • boolean hasMessages()
    判断消息队列是否含有指定消息

IdleHandler

原文:Callback interface for discovering when a thread is going to block waiting for more messages
MessageQueue中的message处理完了或者是需要阻塞等待一段时间,这个时候会回调这个接口
作用:就是在线程中的消息处理完或消息处于阻塞时调用该函数.这时,我们就可以做一些我们自己需要的操作了.

queueIdle()

调用该接口时,如果该函数返回false,那么就会把它从消息队列的mIdleHandlers中移除,返回true就会在下次继续回调

  • public void addIdleHandler(@NonNull IdleHandler handler)
    向消息队列中添加IdleHandler对象

  • public void removeIdleHandler(@NonNull IdleHandler handler)
    从消息队列中移除IdleHandler对象

  • 该函数在源码中的调用

    Message next() {
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        for (;;) {
            synchronized (this) {
                if (pendingIdleHandlerCount <= 0) {
                    // 没有IdleHandler,继续循环
                    continue;
                }
            }
			// 循环调用MessageQueue中的mIdleHandlers,
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                }
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;
        }
    }

我们可以看到:在调用next()函数之后,我们先循环获取消息,如果队列中的消息为null或者是处于阻塞时,才会调用上面这段代码来循环mIdleHandlers,如果mIdleHandlers中含有数据,就会执行IdleHandler中的queueIdle()函数,最后判断是否需要从mIdleHandlers中移除IdleHandler对象

在源码ActivityThread中就使用到了IdleHandler ,通过下面代码我们可以更加直观的了解IdleHandler.

    final class GcIdler implements MessageQueue.IdleHandler {
        @Override
        public final boolean queueIdle() {
            doGcIfNeeded();
            return false;
        }
    }
    
    void scheduleGcIdler() {
        if (!mGcIdlerScheduled) {
            mGcIdlerScheduled = true;
            Looper.myQueue().addIdleHandler(mGcIdler);
        }
        mH.removeMessages(H.GC_WHEN_IDLE);
    }

    void unscheduleGcIdler() {
        if (mGcIdlerScheduled) {
            mGcIdlerScheduled = false;
            Looper.myQueue().removeIdleHandler(mGcIdler);
        }
        mH.removeMessages(H.GC_WHEN_IDLE);
    }

Message

  • what: 用来分辨消息内容的标识符
  • arg1,arg2:用来存储整形数据,表示消息内容
  • obj :用来保存我们需要的对象
  • when:发送消息的时间,以毫秒为单位
  • data(Bundle):用来存储数据
  • callback(Runnable):用来存储需要执行Runnable对象:具体可以查看handler.post(Runnable)和handler.getPostMessage(Runnable r),它不会生成新的线程
  • Message sPool:改线程对象池中的头部Message对象

该类中最重要的只有两个函数,就生成(obtain),回收(recycler),

生成

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

它主要是从Message对象池中取出一个Message对象
其他obtain()都是基于该函数,只是指定了Message的一些属性而已
android线程间消息处理机制(Handler,Looper,MessageQueue,Message)_第5张图片

回收

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

该函数把Message内的数据置为初始状态,然后存储对象池中

Handler

发送消息

  • sendMessage()
    android线程间消息处理机制(Handler,Looper,MessageQueue,Message)_第6张图片
  • post()
    android线程间消息处理机制(Handler,Looper,MessageQueue,Message)_第7张图片
    这些函数实际上最后调用的是:handler.enqueueMessage()
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }// 通过looper获取到messagequeue,然后调用messagequeue.enqueueMessage()将Message插入到队列中
        return queue.enqueueMessage(msg, uptimeMillis);
    }

接受消息

  • Looper.loop()
    public static void loop() {
        final Looper me = myLooper();
        final MessageQueue queue = me.mQueue;
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                return;
            }
            try {
                msg.target.dispatchMessage(msg);
            }
            msg.recycleUnchecked();
        }
    }

通过Looper.loop不断循环调用MessageQueue.next()获取队列头部的消息Message,因为message包含Handler对象,所以通过message.target.dispatchMessage(msg)来分发消息.

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

该函数判断消息中是否含有Runnable,如果有则执行handleCallback(msg);否则,如果handler中含有Callback 接口对象的话,就调用Callback.handleMessage(),handler没有callback对象的话则调用handler.handleMessage()来处理Message

  • handleCallback()
    private static void handleCallback(Message message) {
        message.callback.run();
    }

他实际上是回调Runnable的run()函数.

  • Callback.handleMessage()
    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);
    }

这个是我们在创建handler时,需要传入的接口对象,回调时使用

  • Handler.handleMessage()

我们在多线程消息接收处理中使用最多的就是这种模式了

消息移除

  • removeMessage()
    android线程间消息处理机制(Handler,Looper,MessageQueue,Message)_第8张图片
    它们最终调用的都是MessageQueue中的removeMessage()和removeCallbacksAndMessages()
    void removeMessages(Handler h, Runnable r, Object object) {
        if (h == null || r == null) {
            return;
        }

        synchronized (this) {
            Message p = mMessages;
            // Remove all messages at front.
            while (p != null && p.target == h && p.callback == r
                   && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();
                p = n;
            }
            // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && n.callback == r
                        && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycleUnchecked();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

在MessageQueue中,循环把队列中的全部消息都通过recycleUnchecked()回收到Message的对象回收池中.

  • Handler():创建handler对象
    android线程间消息处理机制(Handler,Looper,MessageQueue,Message)_第9张图片

他们的具体不同之处可以看源码了解

  • createAsync(@NonNull Looper looper)
  • createAsync(@NonNull Looper looper, @NonNull Callback callback)

创建一个Handler对象,它和正常Handler对象的区别就是它发布的Message或Runnable不会受到同步栅栏消息的阻塞
同步栅栏消息只阻塞同步消息.

你可能感兴趣的:(android,Handler,MessageQueue,Looper,Message)