欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android消息传递机制Handler完全解析之源码解析

程序员文章站 2022-05-13 22:05:16
...

Android消息传递机制Handler完全解析之源码解析

本文将帮助大家梳理分析Handler的源码过程。

分析Handler源码有很多种思路,网上也有很多优秀的文章,
有的是分析Handler、Looper、MessageQueue三者的主要源码,
有的是以其中一个问题分析里面的源码,比如主线问题,如何延时工作问题等等。

本文将带着下面几个最主要的问题,帮大家梳理Handler里面的源码过程:
1、主线问题,Handler从发送sendMessage到接收回调handlerMessage的实现过程是怎么样的?
2、Looper、MessageQueue是什么时候创建的?
3、Looper是循环者,它是如何实现循环的?
4、MessageQueue是如何实现消息管理的?
5、sendMessageDelay是如何实现消息延迟的?
Handler外部接触的关键类有Handler、Looper、MessageQueue、Message,他们的作用如下:

Looper 消息轮训器,负责消息取出
MessageQueue 消息暂存队列,负责消息存储
Message 消息对象,消息本身
Handler 收发消息工具,发送和接收消息

这里我们可以看出Handler收发过程并不能对Message进行处理,当然接收到后进行的处理不算。
MessageQueue负责接收管理消息,并对消息进行排序,真正负责取出消息的是Looper。

整个收发消息的流程其实就是:
1、Handler使用sendMessage发送消息
2、消息传递到MessageQueue,并排序
3、Looper不断从MessageQueue获取消息,并发送給Handler
4、Looper发送消息給Handler后,在handlerMessage回调中就能接收到消息。

完整流程图如下:

Android消息传递机制Handler完全解析之源码解析

一、主线问题,Handler从发送sendMessage到接收回调handlerMessage的实现过程是怎么样的?

1、Handler发送消息过程

Handler收发消息很简单就几句代码,比如:

        Handler handler = new Handler(looper) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //接收消息
            }
        };
		//发送消息
        handler.sendEmptyMessage(1);

但是Handler发送消息有很多api方法,他们有什么区别呢?

Handler发送消息大概有如下几种:
1.post(Runnable)
2.postAtTime(Runnable,long)
3.postDelayed(Runnable,long)
4.sendEmptyMessage(int)
5.sendMessage(Message)
6.sendMessageAtTime(Message,long)
7.sendMessageDelayed(Message,long)

总结起来就是有两大类,
一类是在需要传入Runnable,Runnable里面的线程其实就是类似handleMessage直接接收数据,
并且没有数据传递,发送和接收都在一个地方写了;

另一类就是是我们常用的,发送和接收分开写。

关于post和非post发送区别,可以看后面源码解析过程。

每一大类里面又有三种不同的方式,一种是立即发送,一种是在某段时间发送,一种是延时时间多久发送。

立即发送比较好理解:如果当前没有消息就立即发送,如果有消息放到正在处理的消息的后面
某段时间发送:使用绝对时间值,定义发送时机,比如2020-10-01 8:08:00有一个固定的毫秒数
延迟多久发送:在当前是时间延迟多少毫秒发送

下面是发送的源码:


public class Handler {

	// 这里先介绍post发送消息的三个方法,实际是把Runnable传入Message里面,最终也是调到非post方法中,代码如下:
    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

    public final boolean postAtTime(Runnable r, long uptimeMillis)
    {
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }

    public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
    {
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }

	//接着看常用的发送消息的过程:

	// 直接发消息,实际是调到延迟0秒发消息
    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }

	// 延迟发消息,实际是在当前时间加上延迟时间发消息,时间是使用绝对时间发消息
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

	// 固定时间发消息,最终把消息发送給MessageQueue处理
	//上面的五个方法,最终都是这个入口,把数据发给MessageQueue
	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);
    }

	//MessageQueue对消息就行排序处理,到这里Handler发送消息的过程就传递到MessageQueue了
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

    /**
     * Handle system messages here.
     * 回调处理
     * dispatchMessage是Handler給自己发送消息的方法,但是这个方法是在Looper中调用的
     * Looper调用dispatchMessage,经过handleMessage让自己得到回调消息。
     * Handler还有一个使用Callback对象监听Message回调信息的方式,这个也用得少。 
     */
    public void dispatchMessage(Message msg) {
		//callback一般是不用的,所有不会进入
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
			//mCallback默认为空,所有不会进这里
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
			//正常都是直接到这里,这里之后,Handler就能收到回调了
            handleMessage(msg);
        }
    }

	//Handler的内部接口对象,也可以接收Message,可以在创建Handler的时候传入
    public interface Callback {
        public boolean handleMessage(Message msg);
    }

}


到这里发送消息的过程已经非常清晰了吧,都是使用绝对时间来决定什么时候发送消息,

想想也是,MessageQueue要管理里面的Message,那肯定是需要一样绝对的元素,就是绝对时间啦。

接下来就是MessageQueue分析enqueueMessage方法里面如何对Message的处理了。

2、MessageQueue接收消息,管理消息、排序过程

下面就是以enqueueMessage方法实现的内容,展开逻辑分析,这个方法不是一下子就能看懂的,需要慢慢来分析。

这里要明确一点就是这里只是管理消息,并不负责消息发送給Handler,不然你找半天了找不到出去的路。

发送消息要到Looper对象中进行分析!

管理消息,就是说給当前传入的消息和以前的消息給他们重新排个序,就是这么简单。

MessageQueue有两个重要方法,一个是消息存储enqueueMessage,另一个是消息取出next()

public final class MessageQueue { // final类不能被继承哈

	// 核心方法,主要代码如下
    boolean enqueueMessage(Message msg, long when) {

            msg.markInUse(); //标志该Message已经处理
            msg.when = when; //获取该消息的绝对时间
            Message p = mMessages; //上一次处理过的消息对象,也是最前面的一个数据,首先要处理的数据
            boolean needWake; //是否需要唤醒线程,这个涉及到了底层硬件

			//看第一个条件和里面的逻辑,一下子就有点懵逼了,不过一步步来还是不难的
			//首先,p == null,mMessages第一个处理的消息是空的,所有会进入条件
			//其次,when ==0 这个条件一般不会符合,已经消息对应的时间不会为0
			//最后when <p.when 表示该Message的时间比上一个处理的时间还早,排在上一次处理的数据前面
            if (p == null || when == 0 || when < p.when) {
				//把上一个即将处理的数据,变成当前Message的next
                msg.next = p;
				//当前处理的对象为全局变量
                mMessages = msg;
				//判断是否需要唤醒线程
                needWake = mBlocked; //mBlocked表现线程是否在阻塞状态,如果是阻塞状态,并且有消息,需要马上唤醒线程
            } else {
				//上面把数据插在前面,如果是插在中间就要做循环判断,寻找合适的位置了

				//下面的才是大部分逻辑的数据,多个Message排序的主要代码
				//1、定义临时Message对象prev变量
                Message prev;
				//for循环判断每一个Message的时间,并把传入的Message放到合适的位置
				//这个过程可能有点绕,不懂算法的人可能看几遍都不懂,尽力而为吧
                for (;;) {
					//2、把上一个处理的数据放在最前面
                    prev = p;
					//3、把下一个信息放到自己位置
                    p = p.next;
					//4、如果p.next == null,说明没有下一个数据,不用继续循环排序了,放在最前面吧
					//5、如果when < p.when,说明将要处理的消息的时间比下一个消息短,说明这个消息要放在p消息前面
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
				//符合 when < p.when,说明先处理msg,再处理p,把p放到msg之后
                msg.next = p; // invariant: p == prev.next
				//把msg发到合适的位置,即p.next之前,p之后(上面循环中定义prev = p)
                prev.next = msg;
            }

			//上面的for里面的循环逻辑确实会让人头晕,举个例子希望大家能理解一点,
			//比如,有待处理的三个数据对应的时间是:n1 = 2; n2 =6; n3 = 8;数值表示延时的时长
			//n1.next =包含 n2和n3,n2的next包含n3
			//如何把时间为7插入到6和8直接?
			//即:6对应的Message.next包含7、8
			//简单的说就是从最短的时间开始比较,如果收集的消息的延时时长比这个消息小,这个消息就放在比较的消息前,并且结束循环
			//(1)把7先和2判断不符合,接着和6判断也不符合,接着和8判断
			//(2)发现7比8小,所以放在6后面,8前面
			//(3)即在当前判断的p和p.next 插入msg,
			//(4)变量替换过程为
			保存变量过程prev = p , p = p.next,
			替换变量过程msg.next = prev.next , p.next = msg
			//到这里就完成了2:6:7:8 的排序,Looper对象就会从第一个开始取数据
			//这个过程如果实在看不懂可以先跳过,以后慢慢理解也可以的


			//线程休眠后,要继续处理消息才需要唤醒
            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

}

由此可见,Message存储的数据,next表示下一个待处理的数据,
Message对象包含后面的所有数据,MessageQueue负责对数据进行管理,重新编排。

当然enqueueMessage方法,只是对Message排序的方法,
MessageQueue里面还有其他重要方法,后面数据取出也会调用到里面的方法,这个稍后介绍。

上面是Handler的发送和Message的管理,接下来就是分析Handler的回调是如何发生的了。

3、Looper管理消息以及选择发送消息过程

说到Looper,很多人可能对里面的过程并不了解,因为很多处理的开发者,甚至都没用过这个类。

这个looper类,在主线程会默认帮创建,不需要自己创建,如果是子线程回调数据,就要自己创建。

要分析Looper的管理消息和发送消息过程,就要分析Looper创建和工作的流程代码。

这里连带说一下Handler、Message、MessageQueue的创建。
Handler就不必描述了,直接new,如果需要接收回调消息,重写handlerMessage方法即可。
Message的创建可以new Message(),也可以使用Message.obtain(),第二种方法会多次复用同一个对象,减少内存使用。

MessageQueue的创建就麻烦多了,MessageQueue是在Looper中创建的。

Looper的创建使用的是Looper.prepare()方法;
Looper管理消息,回调消息的逻辑基本都在Looper.loop()里面。

Looper中最重要的两个方法就是对象创建prepare()和循环工作loop()了。

下面是Looper的主要代码:

public final class Looper { //不可继承的类哈

    final MessageQueue mQueue; //只能初始化一次的对象,说明一个线程只能有一个Looper并且对应一个MessageQueue
	final Thread mThread; //Handler内部数据处理,其实也是依赖线程在工作,只不过是一个固定的线程

	//Looper对外暴露的创建Looper方法
    public static void prepare() {
        prepare(true);
    }

	//参数表示是否允许强制退出,一般是允许的
    private static void prepare(boolean quitAllowed) {
		//sThreadLocal.get()返回的是跟Looper绑定的对象,如果该对象已经创建,不能再调用prepare方法,否则会抛出异常。
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
		//第一次调用prepare,就会创建Looper,并且把Looper跟ThreadLocal绑定
        sThreadLocal.set(new Looper(quitAllowed));
    }

	//这里是私有方法,只能自身调用
	//MessageQueue就是这里实例化的
	//一些耗时处理都是在Thread中实现的
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }


    /**
     * Looper的核心方法
     */
    public static void loop() {
        final Looper me = myLooper();
		//要先调用Looper.prepare(),否则会报错,因为Looper没有实例化
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // MessageQueue不必判空,以为创建Looper的时候,必定会创建MessageQueue

		//下面就是不断取出Message,并且判断什么时候发送給Handler的逻辑了
        for (;;) {
			//取出队列最前面的消息,并且已经到时间处理的消息
            Message msg = queue.next(); // might block,有可能在阻塞状态,比如待处理的都是延时消息

			//这里的阻塞是硬件级别的,让把这个线程停下来,把cpu让出給其他线程做事情
			//并不是普通的的Thread.sleep(XXX);这种是让整个线程数据在sleep过程啥都做不了,并且这个还占用cpu

			//这个硬件级别的阻塞,是native方法实现的,并且唤醒后,会接着往下走

			//如果没有消息,直接返回,退出循环
            if (msg == null) {
                return;
            }


			//...省去一些啰嗦的逻辑代码

			//mSlowDispatchThresholdMs是Looper中一个不太重要属性,用于检测哪个消息处理超过某个值
			//可以有set方法,设置一个标准值后,如果处理某个Message需要的时间比这个定义时间长,就会打印日志提示一下
			final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
				//消息分发,就这一句话
				//msg.target就是获取Message对应的Handler
				//调用Handler dispatchMessage,经过内部分发,然后就能在handleMessage收到Message消息了
                msg.target.dispatchMessage(msg);
				//记录发送该消息需要的时间
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            }
			
			//如果定义了消息处理时间标准值
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;//消息处理的时间
				//如果消息处理的数据比预定的标准值大,则打印日志提醒一下
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }
			//对该Message的缓存数据进行重置、清空
            msg.recycleUnchecked();
        }
    }
}

主线程创建Looper方法在哪里呢?

Android的主线程,指的是Activity的主线程,这个线程是在Activity创建的时候自动创建的。

ActivityThread的代码:


public class ActivityThread{
	public static void main(String[] args) {
	    /...
	    Process.setArgV0("<pre-initialized>");
	
	    Looper.prepareMainLooper(); //其实也是调用了prepare()方法,不过传入参数false,表示不可退出

	    Looper.loop();
	
	}
}

下面介绍的是Handler出现机制里面最复杂的一个方法,也是最重要的一个方法,线程休眠也是在里面操作的。

Message的next()方法,这个方法是在Looper中调用的,用来取出即将处理的Message。

这里的操作有下面几方面:

1、该方法返回的Message是单个Message,不是MessageQueue管理的那个消息队列,返回的Message的Message.next会被置空
2、如果下一个消息是延时消息,那么会调用nativePollOnce方法休眠固定的延时的时间后自动唤醒
3、如果Handler没有消息处理,nativePollOnce传入-1参数,表示长久休眠,
直到下一个消息发过来为止,同时mBlocked = true;表示线程在休眠状态
4、后面当Handler继续有消息,会停止休眠,继续工作

代码如下:

public final class MessageQueue {

	//该方法是取出,已经到时间处理的消息
	//如果是延时消息会在里面进行阻塞
 	Message next() {

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0; //处理下一个消息需要等待的时间
        for (;;) {

			//native方法,让线程休眠一段时间,时间长度是到下一个消息发送的时间
			//如果时间传入0不会休眠,传入-1会一直休眠,传入大于零的数据就是休眠该时间
			//比如是一个不延时消息,会进入到下面,直接返回Message
			//如果该消息是延时的,发现当前时间还不到消息处理的时间:now < msg.when
			//计算出该消息还有多久才处理msg.when - now,然后休眠这段时间,休眠完成后,往下走就会返回消息
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                //获取当前时间
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // 往后遍历找到第一个异步的消息,一般消息都是同步顺序执行的,所有这个循环一般只进一次
                    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;
						//下面这个是在链表中减去已经处理的msg消息
                        if (prevMsg != null) { //不太明白,msg.next !=null但是msg为空的情况。
                            prevMsg.next = msg.next; //这里其实是把msg.next往前推一个位置,可以慢慢体会
                        } else {
							//及时更新全局变量
                            mMessages = msg.next;
                        }
                        msg.next = null; //只返回单个Message,不返回整个链表数据
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse(); //标识该消息已经处理了
                        return msg;
                    }
                } else {
                    // No more messages.
					//消息为空,进入休眠状态
                    nextPollTimeoutMillis = -1;
                }

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

到这里Handler的主要源码和主要过程已经分析完成了,
当然还有一些相关变量没有进行详细分析,比如后面发送消息过来怎么再次唤醒的
但是经过上面代码的分析讲解,本文开始提出的几个问题都有思路了吧。

下面在总结一下:
代码图解:
Android消息传递机制Handler完全解析之源码解析

问题总结:

1、主线问题,Handler从发送sendMessage到接收回调handlerMessage的实现过程是怎么样的?

(1)Handler通过方法enqueueMessage发送消息到MessageQueue中
(2)MessageQueue在方法enqueueMessage对传进来的Message进行排序管理,整理成链表Message
(3)Looper从MessageQueu中通过方法next()获取可以处理完成的信息,并通过dispatchMessage方法发送給Handler
(4)Handler经过判断处理后回调到handlerMessage()

到这里整个回调过程就完成了。

2、Looper、MessageQueue是什么时候创建的?

Looper和MessageQueue创建是用Looper.prepra()创建的,
并且主线程默认已经创建了,如果是Looper在子线程工作,就要自己调用这个方法创建。

3、Looper是循环者,它是如何实现循环的?

Looper循环主要依靠looper()方法,里面有个循环,循环中不断调用MessageQueue.next()方法
如果有消息可以处理就会收到,如果没有消息或者有延时消息,会在next()方法进行休眠
直到有消息或者延时时间到了,就会停止休眠,自动进入循环。

4、MessageQueue是如何实现消息管理的?

MessageQueue消息管理的逻辑都在enqueueMessage方法里面,
当Message传入,会重新对对Message链表进行排列处理

5、sendMessageDelay是如何实现消息延迟的?

延迟消息是通过方法nativePollOnce,让线程固定时间的休眠,休眠后让出cpu,直到休眠时间过了自动进入消息处理过程。
具体的逻辑代码都在MessageQueue的next()方法里面

扩展:如果Handler有几个延时消息,这是马上发送一个立即发送的消息怎么处理?

MessageQueue 存入消息后,发现有数据,会判断是否需要wake,如果要唤醒就调用nativeWake方法,就会马上进入工作状态。

这里的Handler源码分析,只是对主要问题进行代码分析,有些具体细节需要在自己琢磨,比如有些变量的作用,比如Looper和MessageQueu使用for (;;)做循环等等。

这个文章也有很源码文字比较多的介绍
https://blog.csdn.net/u011240877/article/details/72892321

还有些自己比较初级的Handler总结:
消息传递机制之Handler使用总结:https://blog.csdn.net/wenzhi20102321/article/details/53098943

Handler消息传送机制总结:https://blog.csdn.net/wenzhi20102321/article/details/52837834

Android消息传递机制Handler完全解析之1基础介绍:https://blog.csdn.net/wenzhi20102321/article/details/107888152

Android 多线程之HandlerThread 详解:https://blog.csdn.net/wenzhi20102321/article/details/82596297

共勉:感谢现在努力的自己。