通过源码分析Anroid消息处理机制

前言

最近通过微信公众号推文以及博客文章学习了Android的消息机制的原理,然后抽空写下了这篇文章,对自己学到知识进行梳理,也方便以后自己查阅。

主线程不处理耗时操作,子线程不更新UI

Handler消息处理机制在Android中占有非常重要的地位

消息机制的大概流程:

系统启动后,系统主线程会创建一个Looper对象,然后调用Looper.loop()开启一个死循环,循环会不断地从消息队列中(MessageQueue)中取出待处理的消息(Message),Looper取到消息后就会回调Handler中的方法处理消息。消息队列中的消息是从哪里来的呢?没错,就是我们通过Handler发送过去的。大概流程如下图:

在这里插入图片描述

消息机制源码分析:

如果我们想使用消息机制进行更新UI的话有两种方法:

  1. 创建Handler时传入一个Handler.Callback对象,然后重写handleMessage()
private Handler mHandler = new Handler(new Handler.Callback() {
	@Override
	public boolean handleMessage(Message msg) {
		return false;
	}
});
  1. 调用Handler的post方法传入一个Runnable对象
Handler handler = new Handler();
handler.post(new Runnable() {
	@Override
	public void run() {
		try {
			Thread.sleep(2000);
			setText();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
});

子线程不是不能更新UI吗?怎么第二种方法更新UI的时候好像是在子线程。接下来看看源码吧,这个问题看完源码后自然会有答案。
首先看看上面使用到的Handler的两个构造函数

public Handler() {
	this(null, false);
}
public Handler(Callback callback) {
	this(callback, false);
}

上面两个构造函数最终都是调用Handler(Callback callback, boolean async),接着往下看

public Handler(Callback callback, boolean async) {
	//省略代码
	//这里获取了一个Looper对象
	mLooper = Looper.myLooper();
	//Looper对象很重要,如果当前线程中没有Looper对象的话,会抛出异常,提示用户应该用Looper.prepare()创建一个
	if (mLooper == null) {
		throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
	}
	//从Looper中获取消息队列对象
	mQueue = mLooper.mQueue;
	mCallback = callback;
	mAsynchronous = async;
}

到此为止,一个Handler就创建好了。然后呢,如果是使用第一种方式更新UI,就需要调用Handler的sendMessage()发送消息了,看看这一步是怎么处理的吧

//直接把延迟时间设为0
public final boolean sendMessage(Message msg) {
	return sendMessageDelayed(msg, 0);
}

//对延迟时间进行校验
public final boolean sendMessageDelayed(Message msg, long delayMillis)	{
	if (delayMillis < 0) {
		delayMillis = 0;
	}
	return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

//对消息队列对象进行判空
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) {
	//在这里,把this,也就是当前Handler对象封装进消息对象中
	msg.target = this;
	if (mAsynchronous) {
		msg.setAsynchronous(true);
	}
	//最终调用消息队列中的enqueueMessage()方法
	return queue.enqueueMessage(msg, uptimeMillis);
}

最终是调用MessageQueue中的enqueueMessage方法

boolean enqueueMessage(Message msg, long when) {

       //省略代码...
       
	synchronized (this) {
	
            //...
            
		msg.when = when;
		Message p = mMessages;
		boolean needWake;
		//注意看when<p.when,可以看出消息队列中的消息是按延迟时间从小到大排序的
		if (p == null || when == 0 || when < p.when) {	//把消息插入到表头
			msg.next = p;
			mMessages = msg;
			needWake = mBlocked;
		} else { 
			Message prev;
			for (;;) {
				prev = p;
				p = p.next;
				if (p == null || when < p.when) {
					break;
				}
				
                    //...
                    
			}
			msg.next = p; // invariant: p == prev.next
			prev.next = msg;
		}
		
		//...
	}
        return true;
}

从上面的源码中可以看出来,MessageQueue中的数据结构是单链表。到这里,就已经把消息成功插入到消息队列当中去了。好了,第一种方法已经成功插入消息了,接下来看看第二种方法了,从哪里开始看起呢?没错,就是从post()开始看:

public final boolean post(Runnable r) {
	return  sendMessageDelayed(getPostMessage(r), 0);
}

post()方法里面调用了sendMessageDelayed()方法,那最终还是调用enqueueMessage(),这里面的流程再上一步已经看过了,唯一的不同就是这个getPostMessage()方法:

private static Message getPostMessage(Runnable r) {
	Message m = Message.obtain();
	m.callback = r;
	return m;
}

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

在getPostMessage中把Runnable封装进了消息当中,最终把这个消息插入到消息队列里。

这时候,就已经成功把消息(Message)插入到MessageQueue中了,并且知道了Handler把自己封装进了Message当中。走到这里,我们好像走进了一个死胡同,走不下去了?先来看看下面几个问题:

  1. Looper对象是在哪里被创建的(好像我们都没有创建过Looper对象)?
  2. Handler中的handlerMessage()方法是怎么被回调的?

我们在上面的步骤中,没有创建过Looper对象,而Looper对象却已经存在了,那唯一的一种可能就是,当我们启动系统的时候,系统已经帮我们创建好Looper对象了。我们在ActivityThread类中找到main()方法,ActivityThread类是一个隐藏类,我们可以在SDK文件夹中查找出来:

public static void main(String[] args) {

	//...
       
	Looper.prepareMainLooper(); //创建一个Looper对象

	//...

	Looper.loop(); //开启循环

	//...

    }

果然,在main()中发现了Looper。main()方法中通过Looper.prepareMainLooper()创建一个Looper对象,prepareMainLooper()方法实际上就是调用Looper.prepare(boolean quitAllowed),这个方法是不是很熟悉,没有记错,它就是在Hanlder构造方法里面的异常提示中出现的方法,不过main()中调用的是带参的prepare()方法,而我们之前看过的是Looper.prepare(),先看看源码:

public static void prepareMainLooper() {
	prepare(false);
	synchronized (Looper.class) {
	if (sMainLooper != null) {
		throw new IllegalStateException("The main Looper has already been prepared.");
	}
	sMainLooper = myLooper();
	}
}

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

public static void prepare() {
	prepare(true);
}

可以看到Looper.prepare()最终也是调用Looper.prepare(boolean quitAllowed)。

很简单,prepare()中就是new了一个Looper,并把Looper set进ThreadLocal中(ThreadLocal是什么来的?我暂时还没有去了解,有时间会补充),所以Looper在系统启动时就创建好了,创建Handler时才不会报错

Looper创建完成之后,然后Looper.loop()就会开启一个循环,开启循环做什么呢?就是之前说过的,从MessageQueue中不断的取出消息来

//下面省略了部分代码
 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;
	for (;;) {
		Message msg = queue.next(); // might block
		if (msg == null) {
			 // No message indicates that the message queue is quitting.
			return;
		}
		try {
			msg.target.dispatchMessage(msg);
		} finally {
			//...
		}
	}
}

很明显可以看出loop方法一直遍历MessageQueue,阻塞线程,直到获取到一个Message,然后调用Message的一个成员变量target的dispatchMessage方法。之前说过了,target其实就是Handler,dispatchMessage方法最终就调用我们重写的Handler的handlerMessage方法

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

到这里好像已经都结束了,对了,好像还有之前使用post()方法时的一个问题,看看上面的源码,msg.callback不正是当初使用post传进来的Runnable,它不为空的话就调用handleCallback():

private static void handleCallback(Message message) {
	//直接调用子线程的run方法
	message.callback.run();
}

由Java多线程的知识,主线程直接调用子线程的run方法,那相当于还是在主线程中,和普通的调用方法没有区别,所以我们用post传入Runnable更新UI的时候不会报错。

以上就是消息机制的原理了,让我们整理一下上面的知识:

  • Handler:在消息机制中,是作为消息的发送方和处理方。消息在一个线程中通过Handler发送到MessageQueue中。
  • Looper:在消息机制中,是作为消息队列的管家。不停的从消息队列中获取消息,获取到消息后,根据Message中绑定的Handler对象调用Handler中的dispatchMessage方法,进而调用到开发者重写的handlerMessage方法进行消息的处理
  • MessageQueue和Message:MessageQueue就是存放Message的地方,实质上是一个单链表的结构,有新的Message来的时候,就把插入到消息队列,当Looper来取消息时就把表头的Message给它。Looper拿到消息后,可以从Message中拿到消息的发送方Handler。

知道消息机制的原理后,我们就可以轻松的在任何线程下使用handler了:

new Thread() {
	@Override
	public void run() {
		Looper.prepare();
		Looper.loop();
		Handler handler = new Handler(new Handler.Callback() {
			@Override
			public boolean handleMessage(Message msg) {
				return false;
			}
		});
	}
}.start();
  1. 首先用Looper.prepare()创建一个Looper并初始化Looper持有的MessageQueue
  2. 然后用Looper.loop()方法开启循环,从MessageQueue中取消息,并调用handler的dispatchMessage方法处理消息,如果消息队列里面没有消息,循环就会阻塞进入休眠状态,等有消息时就会被唤醒
  3. 最后再new一个Handler,Handler构造方法会获取到Looper和Looper的MessageQueue对象,然后通过sendMessage方法往消息队列中插入消息。

最后,我们对消息机制有一些了解了,先写这么多吧,如有错误欢迎指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值