Android之Handler消息机制——深入理解 Looper、Handler、Message、MessageQueue
序言
handler是我们日常编码中经常使用的一个类,通常用来由子线程转到主线程,或子线程与子线程之前的通信(消息传递),那么什么是Looper呢?什么是MessageQueuene呢?不要着急,我们一步一步看!
1.示例
一般我们编码最基本的常识就是,不能在主线程执行耗时操作(如网络请求、读取数据、数据库读写、io流操作等等),必须创建一个子线程去执行,如以下示例:
//运行在子线程
Thread twoThread = new Thread(new Runnable() {
@Override
public void run() {
Message message = mHandler.obtainMessage();
message.obj = "handler";
mHandler.sendMessage(message);
Log.i("TAG" , "当前线程twoThread:" + Thread.currentThread().getName());
}
});
twoThread.setName("thread#2");
twoThread.start();
耗时操作可以放在run方法中去执行,并且可以设定指定的线程名,但是我们执行完耗时操作拿到数据之后,需要回到主线程去更新UI,但是子线程不能更新UI,所以我们需要转到主线程去进行UI更新操作,就需要用到handler,如以下示例:
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
我们在子线程中获取到数据之后,通过sendMessage来发送数据,然后在handler中接收数据进行处理,基本的使用就完成了,但是我们仅仅知道使用是不够的,我们要了解其中的原理,并且能够在未来的编码中使用这种模式。
到这里我们就有一个问题了,为什么handler能够接收子线程发送的Message?我们可以通过源码解析来进行分析!
2.源码解析
1.Looper
一般looper有两个常用的方法Looper.prepare()和Looper.loop()
Looper.prepare():用来创建一个Looper对象,并把Looper对象存储到sThreadLocal对象中
Looper.loop():用来轮询,假如有新的消息,就将新的消息添加到消息队列中;假如没有消息,就阻塞线程
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//prepare()方法用来创建一个Looper
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));
}
- 在prepare()方法中,先创建了一个ThreadLocal对象,相当于一个数组或者一个HashMap,用来存储当前线程的消息,使用ThreadLocal的好处是可以保证当前拿到的消息是这个线程的消息,可以避免消息错乱!
- 假如sThreadLocal已经有Looper对象了,将会报错!因为已经创建过的Looper对象不能重复添加,用来保证唯一性!这也说明了prepare()方法只能调用一次!
- 将创建的looper对象添加到sThreadLocal中
接下来我们看一下创建Looper的方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
- mQueue:消息队列,用来保存发送的消息
- mRun :是否运行
- mThread:当前所在的线程
创建完Looper之后,我们会调用Looper.loop()方法,loop()方法重点代码如下:
public static void loop() {
//获取Looper对象
final Looper me = myLooper();
//Looper对象不能为空,即必须先创建
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;
}
/*
* 如果有消息了,就通过dispatchMessage()方法进行发送消息,消息将在dispatchMessage()中
* 进行处理
*/
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//释放资源
msg.recycleUnchecked();
}
}
我们一步一步看,首先获取Looper对象,那么Looper对象从哪里来呢?我们还记得在调用Looper.prepare()方法时创建了一个Looper对象,并把它set到sThreadLocal中,那么我们的Looper对象也很有可能是在sThreadLocal中取的,我们看一下myLooper()方法;
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
果然如此!上面的注释已经写得很明白了,我们总结一下loop方法到底做了什么!
(1)获取Looper对象,并且从消息队列获取消息
(2)无限循环消息,如果没有新的消息,就阻塞线程;如果有新的消息,就通过dispatchMessage()方法处理
(3)释放资源
msg.target返回的是一个handler对象,这个时候,我们的handler和message已经开始产生关联了!接下来就到我们的主角Handler上场了!
2.Handler
回到最开始我们的示例代码
Message message = mHandler.obtainMessage();
message.obj = "handler";
mHandler.sendMessage(message);
我们从子线程转到主线程时,是通过handler来传输消息,首先获取了Message的单例对象,通过sendMessage方法来发送,那么最重要的肯定是sendMessage方法了
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);
}
sendMessage()方法最后调用enqueueMessage()方法来解析数据
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
我们为msg.target赋了值,在以上我们的Looper.loop()方法最后处理是调用msg.target.dispatchMessage(msg),在sendMessage时,msg.target被赋值!接下来我们看一下dispatchMessage()方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
我们可以看到很熟悉的一个方法handlerMessage()
public void handleMessage(Message msg) {
}
handlerMessage()竟然是一个空方法,仔细一想确实,我们看下面一串代码就知道了
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
handlerMessage()方法是用来给我们实现的,在handler中重写父类方法,拿到Message来进行接下来的操作!
我们再回头看一下handler的构造方法
public Handler() {
this(null, false);
}
public Handler(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 that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
在handler的构造方法中,通过获取对应且唯一的Looper来传递消息,当然,一定要调用Lopper.prepare()方法来创建Looper!完整的消息传递机制就显而易见了!
总结
(1)开启线程,调用Looper.prepare()方法创建一个Looper,Looper.prepare()只能调用一次,同时将创建一个MessageQueue(消息队列),用来存储消息
(2)调用Looper.loop()方法来获取Lopper对象和MessageQueue消息队列,并开启无限循环,读取消息队列的消息,如果消息队列里没有消息,则进行阻塞;如果有新的消息,则通过dispatchMessage传递给handler
(3)创建handler接收子线程发送过来的消息,通过dispatchMessage()方法拿到Message,通过重写父类方法handlerMessage()最终拿到数据
推荐阅读
-
Android Studio 之 Android消息机制 之简单Demo --- 使用Handler和Message类来完成消息传递
-
Android Handler 机制 - Looper,Message,MessageQueue
-
Android消息通信机制Handler详解,Handler,Looper,MessageQueue,源码解析,讲解这几个类怎么配合工作的
-
Android消息处理机制(Handler、Looper、MessageQueue与Message)
-
Android异步消息处理机制 深入理解Looper、Handler、Message的关系
-
简述android线程间消息处理机制(Looper、Handler和Message)
-
Android消息机制三剑客之Handler、Looper、Message源码分析(一)
-
Android消息机制原理,仿写Handler Looper源码解析跨线程通信原理--之仿写模拟Handler(四)
-
Android异步消息机制-深入理解Handler、Looper和MessageQueue之间的关系
-
Android异步消息机制-深入理解Handler、Looper和MessageQueue之间的关系