1. Handler简介
Handler机制,说的也是Android中的消息传递机制。也就是将工作线程(子线程)中创建的消息,传递到主线程,以进行UI更新操作的过程。简单的可理解为,子线程和主线程之间的通讯。采用这样的机制是为了防止并发更新UI所导致的线程安全问题,而不使用线程锁是为了提高系统的运行效率。
2.Handler简单使用
2.1 使用sendMessage()方法。
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = 0x11;
message.obj = "异步创建字符串";
handler.sendMessage(message);
}
});
thread.start();
2.2 使用post方法
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
handler.post(new Runnable() {
@Override
public void run() {
textView.setText("异步创建字符串");
}
});
}
});
thread.start();
2.3 接收、处理消息
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0x11:
String text = (String) msg.obj;
textView.setText(text);
break;
}
}
};
3.Handler相关方法
3.1 sendMessage系列
sendMessage()方法主要是用于发送一条消息。一般用法是通过重写handler的handler消息是通过Message进行封装。如:
Message message = new Message();
message.what = 0x11;
message.obj = "异步创建字符串";
message.arg1 = 1;
message.arg2 = 2;
handler.sendMessage(message);
- what字段一般用来表示事件类型,用int类型表示。
- arg1和arg2字段可以传递两个int类型的值。
- obj字段可以传递一个对象类型。
除了sendMessage方法可以调用外,还有如下方法:
image.png
sendEmptyMessage用户发送一条空消息,参数what用于表示一个消息类型。
- 后缀为Delayed的方法,表示延迟发送,delayMillis参数表示延迟的毫秒数。
- 后缀为AtTime的方法,表示指定时间发送,uptimeMillis参数指定发送的时间,也是毫秒级。
- 其实sendMessage()、sendMessageDelayed()方法,最终调用的还是sendMessageAtTime()方法,源码中的调用为:
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 final boolean sendEmptyMessage(int what) {
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
我们可以发现,sendEmptyMessage()方法,最终调用的是sendMessageDelayed()方法。是在sendEmptyMessageDelayed()方法中为我们创建了一个Message对象。
3.2 post系列
post方法主要是post一个Runnable对象,在Runnable的run方法中进行消息的处理,这种方法在使用方面不用自己去创建Message对象,但内部其实还是通过Message实现的。这种方法可以实现在子线程中方便的更新UI而不用去通过重写Handler的handleMessage()方法来更新UI。如:
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
handler.post(new Runnable() {
@Override
public void run() {
textView.setText("异步创建字符串");
}
});
}
});
thread.start();
同样,post也有延迟发送等方法:
image.png
4.消息机制
接下来通过源码来分析Handler的消息机制。
4.1 相关的类
- Handler 用于产生和处理消息
- Message 用于消息的封装
- MessageQueue 消息队列,用户存储消息。这个MessageQueue不是一个真正的队列,他并没有实现java的Queue接口,他实际上只是封装了一个单向链表,向外界提供了enqueueMessage()、next()等方法。
- Looper 轮询器,以轮询的方式从MessageQueue中查询消息,并将消息发送给Handler做处理。
- ThreadLocal 用于在线程中存储Looper,可以说实现了线程和Looper的绑定。
4.2 流程
下面一张图理解Handler、Message和MessageQueue之间的关系。
image.png
大概的流程为:handler发送message到MessageQueue中。Looper轮询MessageQueue,将消息发送给Handler进行处理。
4.3 Handler的创建过程
4.3.1 主线程中创建Handler
handle的创建只需要通过new Handler()就可以创建一个Handler。在创建handler的时候需要指定Looper,主线程的Looper是在ActivityThread的main方法中创建的,通过Looper.prepareMainLooper()方法进行创建。
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #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();
}
}
在prepareMainLooper方法中,sMainLooper通过myLooper()方法创建,而myLooper方法是通过调用ThreadLocal的get方法得到Looper对象。
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
这里,先不具体讲ThreadLocal,简单的可理解为讲数据和线程做一个绑定,在当前线程中使用ThreadLocal.get()方法获取对象,则获取到的对象是当前线程所独立拥有的对象,和其他线程中的对象互不干扰。ThreadLocal中有一个Map,用来保存线程和数据的映射关系。
通过ThreadLocal.get()方法获取数据,首先要调用ThreadLocal.set()方法设置数据。所以刚才在myLooper方法中使用ThreadLocal.get()方法获取Looper对象,那是在什么时候设置进去的呢?答案就是prepareMainLooper方法中调用的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));
}
我们再看prepare(boolean)方法。这里首先调用了ThreadLocal.get()方法检查当前线程是否已经创建了Looper对象,如果创建了,就抛出一个RuntimeException。然后调用ThreadLocal.set()方法设置了一个新的Looper,quitAllowed参数是表示是否可以退出消息队列。
由于Looper的构造方法是私有的,所以我们需要通过Looper.prepare()方法来创建Looper。在Looper的构造方法中,创建了一个MessageQueue,并且获取了当前线程信息。
此时主线程中的Looper已经创建完成。再看ActivityThread的main方法,在main方法的最后,调用了Looper.loop()方法,之后抛出一个RuntimeException。此时我们是不是有疑问,为啥要抛出一个RuntimeException?我们知道java程序的入口是main方法,当main方法执行结束,也表示应用程序已经执行结束。所以我们应该会想到,作为一个Android应用程序,怎么可以刚打开就结束了呢。所以说正常情况下只不会执行结束的。关键就在于Looper.loop()方法。我们查看Looper.loop()方法:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
//此处省略...........
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//此处省略..........
}
//此处省略..........
}
在loop方法中有一个死循环,这个死循环就防止了应用程序执行结束。我们看到通过queue.next()方法拿到一个Message,queue的定义在loop方法中,并且在for循环之前:
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;
通过myLooper方法拿到当前线程的Looper对象me,并通过me.mQueue方法拿到了当前Looper对象中的MessageQueue,这个MessageQueue的创建是在前面讲到的Looper的构造方法中。通过MessageQueue.next()方法从MessageQueue中取得一个Message。
此时Handler、Looper、MessageQueue已经创建完成,并且Looper循环已经开启。只需要handler开始发送消息,并处理消息。
4.3.2 子线程中创建Handler
上面是从主线程的角度开始讲解Handler的创建过程,那子线程呢?
在子线程中使用Handler,首先需要new一个handler,此时handler中是没有Looper对象的。上面我们分析源码得出。Looper的构造方法是私有的,只能通过Looper.prepare()方法来创建当前线程的Handler,并且每个线程最多只能存在一个Looper对象,如果多次创建,会抛出RuntimeException。
所以我们在子线程中要使用Handler的时候,必须指定Looper。为什么说是指定而不是创建呢,因为我们可以通过Looper.getMainLooper()方法拿到主线程的Handler。当Looper指定完成以后,就可以通过Looper.loop方法开启循环。所以说,消息处于哪个线程的Looper中,则消息回调处理就在哪个线程中。
4.4 消息发送
4.4.1 使用Send方法发送Message
上面说到了sendMessage()方法最终都是调用了sendMessageAtTime()方法。我们看看sendMessageAtTime()方法做了什么操作。
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);
}
在sendMessageAtTime()方法中,首先检查了当前Handler是否指定了MessageQueue。如果没有指定,则返回false,如果已经指定,则执行了enqueueMessage()方法,我们再看enqueueMessage()方法。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在这里我们就可以看明白,是在这个方法中将消息加入到MessageQueue中。并且在加入message之前,将msg的target字段赋值为this,这个this也就是当前的Handler对象了。mAsynchronous表示是否异步,这个参数是在handler创建的时候初始化的,默认为false。
4.4.2 使用Post方法发送Runnable
我们首先看Post方法中执行了什么操作。
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
我们发现post()方法直接调用的是sendMessageDelayed()方法。但是message的获取是通过getPostMessage()方法。我们看看getPostMessage()方法。
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
在getPostMessage()方法中通过Message.obtain()得到了一个Message对象,并且将runnable对象设置给message的callback,之后就返回了这个message对象。Message.obtain()方法简单理解是获取一个可复用的Message对象,降低了内存开销,提高效率。
所以说,我们使用post()方法最终还是通过Message,并且是将这个回调Runnable设置给Message对象中的callback属性。
4.5 消息的处理
我们回到Loop的loop()方法。
public static void loop() {
//... 省略
for (;;) {
//... 省略
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//... 省略
}
//... 省略
}
在loop()方法的for循环中,有一行代码。msg.target.dispatchMessage()。调用了message的target的dispatchMessage()方法。之前我们看过,message的target实际上是保存的当前Handler的实例,所以这里调用的是handler对象的dispatchMessage()方法进行消息的封发处理。我们查看dispatchMessage()方法。
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
这里我们发现,首先判断了msg的callback属性是否为null,这里的callback就是使用post方法传入的Runnable参数。
- 当callback不为null时,调用了handleCallback()方法
private static void handleCallback(Message message) {
message.callback.run();
}
handleCallback()方法直接调用了message.callback.run(),也就是调用了传入的Runnable的run方法。
- 当callback为null时,首先是判断了mCallback是否为null,不为null。则执行mCallback的handleMessage()方法,并且return。如果mCallback为null,则会调用handler的handleMessage方法。这个方法大家就很熟悉了,不就是我们重写的handler的handleMessage()用来处理消息的方法吗。
我们可以查看mCallback的初始化,其实是在Handler的构造方法中初始化的,可以传入一个Callback,实现Callback的handleMessage()方法。所以我们使用callback的handleMessage方法去处理消息,和重写handler的handleMessaeg()方法,都是可以实现消息回调处理的,但是callback的优先级在前。
从上面可以看出,我们可以重写dispatchMessage()方法进行消息的拦截处理。
写在最后
首先,谢谢大家的阅读。由于小编也是初次学习写技术文章,都是按照自己的理解来写的,可能会有错误的地方欢迎大家留言指出。文章中语言的组织也不是很好,希望大家理解。写这篇文章一是为了能让自己进一步理解Handler机制,并加强记忆。二是能和大家交流技术,共同进步。也希望能提起大家阅读源码的兴趣。最后,还是感谢大家阅读。
网友评论