Handler是我们常用的一种通信方式,可用于子线程更新UI
对于Handler我们需要知道的有以下四种对象
- Handler:用于分发消息
- MessageQueue:用于存储Message
- Message:通信的消息
- Looper:一个消息循环机制
一:Handler的基本使用
我们使用Handler的时候,一般是这么使用的(有多种使用方式,这里简单的举例)
// MainActivity.kt
val handler: Handler = Handler(
Handler.Callback {
when (it.what) {
1 -> {
// doSomething
}
else -> {}
}
true
}
)
// 在其他地方调用
val obtain = Message.obtain()
obtain.what = 1
handler.sendMessage(obtain)
先新建一个Handler,并声明回调,在回调中根据传递的数据做不同的动作,之后在任何一个地方都可以使用handler对象的sendMessage()方法,发送数据,数据会在之前声明的回调中接收到
二:源码分析
为什么我们的Handler可以这么使用呢?
对于这个问题,我觉得我们学习Android的有必要知道一下,起码以后面试可能也有用处。
我们可以一步一步来分析
首先我们使用Handler
就必须创建一个Handler
,我们查看Handler
的构造函数
// Handler.java
// 本例用的即此构造函数
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper(); // 1
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
此时我们可以看到1
位置上Looper.myLooper()
这是去获取Looper
对象,Looper
是一个循环机制,可以不断的从队列中拿到消息进行发送,我们来看下这个对象是怎么获取到的
// Looper.java
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
看到这里,可能大家会疑惑这sThreadLocal
是什么鬼,其实这是一个ThreadLocal
对象,我们可以简单理解下,就是它可以让我们每个线程都有一个属于自己的局部变量,隶属于线程,在当前线程中使用sThreadLocal.get()
都可以拿到线程中唯一的局部变量,可能介绍的不是那么容易懂,其实它的源码也很简单,有兴趣的大家可以去看看,绝对让你一下子就能理解的。
注意:使用
Looper
也是有规定的,需要先调用Looper.prepare()
,然后才能调用Looper.loop()
,为什么?请看下面的解释
一般我们使用Looper
,我们如果想要让子线程一直运行,并随时在子线程中处理数据
则可以这么做
Thread {
Looper.prepare()
mThreadHandler = Handler(Looper.myLooper()) {
// 处理数据
true
}
Looper.loop()
}.start()
如果我们不使用Looper.prepare()
,继而使用Looper.loop()
则程序会报错
// Looper.java
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
...
}
那么问题来了,我们的Handler是在主线程实现的,既然能一直接收消息,证明主线程是有Looper
存在,并且执行了Looper.prepare()
与Looper.loop()
,不然程序就会奔溃,那么这玩意到底是怎么在哪里创建出来的呢?
答案是在ActivityThread类的main函数创建出来的
// ActivityThread.java
public static void main(String[] args) {
...
Looper.prepareMainLooper(); // 1
...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop(); // 2
throw new RuntimeException("Main thread loop unexpectedly exited");
}
可以看到这里的1
和2
分别调用了我们想要看到的Looper.prepare()
与Looper.loop()
,至于这个ActivityThread
是什么,我们现在只需要知道它就是一个手机中每个应用的入口,正如我们运行Java程序时需要调用main()
入口函数一样,这个类中也提供了一个main()
函数,就是应用的入口函数,在这个入口函数中,就创建了我们需要的主线程Looper
对象,所以我们直接在主线程实例化Handler对象就是使用的这个Looper
对象,并可以一直接收消息
既然Looper
循环有了,Handler
就能一直接收消息了。
接下来我们从发送数据开始看起
val obtain = Message.obtain()
obtain.what = 1
handler.sendMessage(obtain)
以上就是创建一个Message
然后发送出去,我们可能也常用handler.post(Runnable r)
,其实查看源码可以得知不管是用哪一种方式去发送数据,最后调用到Handler
对象的一个方法
// Handler.java
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) {
msg.target = this; // 1
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到1
这里需要发送的msg的target
指定了这个Handler
(这里要记住),然后再用这个queue
发送一个数据出去
那么这里又疑惑了,这个queue
又是哪里来的,也就是Handler
的mQueue
对象从哪里来,其实上面的代码就已经提示了,在Handler
的初始化构造函数里面就有
// Handler.java
mQueue = mLooper.mQueue;
这时候我们可以理一下就是,Looper一直循环,Looper里面有一个消息队列MessageQueue
,发送数据都是通过这个Looper
里面的一个对象mQueue
发送的
接下来我们看这个消息队列是怎么发数据的,通过queue.enqueueMessage(msg, uptimeMillis);
我们去看下MessageQueue
类里面的enqueueMessage
方法
// MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
// 1,先获取当前准备要发送的消息
Message p = mMessages;
boolean needWake;
// 2,如果当前要准备的消息是空的,或者我们设置的时间是0
// 意思就是此时消息队列里面是空的,或者我们设置的时间小于这个当前准备发送的消息
// 则交换下顺序,把发送的消息和当前准备发送的消息调换一下顺序,即先发我们要发送的消息
if (p == null || when == 0 || when < p.when) { // 2
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
// 3,如果不能插入到当前准备发送的消息前面,则往后面依次进行排序,根据这个when时间
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
代码中标明了注释,仔细品品就知道这个queue.enqueueMessage(msg, uptimeMillis);
,与其说是发送数据,更确切的应该说是对消息根据时间进行排序
那么我们真正的发送消息是在哪里呢?
我们知道Looper
是一个可以带有循环机制的东西,Looper.loop
即开始进行循环,我们可以看下这里面的代码,这个方法里面有很长代码,大家要挑重点看
// Looper.java
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;
...
// 1,此时一直循环从队列中获取消息
for (;;) {
// 2,获取消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
try {
// 3,开始分发消息,此时的target即Handler
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
}
}
我们看代码注释2
这个地方,调用queue.next
,取出一个消息,然后注释3
这块就是使用调用消息里面的target
对象,由前面的代码分析可以知道这个target
就是发送消息的handler
,此时就把消息分发出去了,接下来我们可以查看下这个MessageQueue
的next
方法,和Handler
的dispatchMessage
方法
MessageQueue
的next
方法
// MessageQueue.java
Message next() {
...
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 1,nativePollOnce 方法用于“等待”, 直到下一条消息可用为止,当没有消息的时候会阻塞住
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 一般的,我们的msg.target是不会为空的,所以可以略过这条判断
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
// 2,开始取消息
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);
}
// 情况二:到了发送的时间了,则把下一个需要发送的消息赋值给mMessage
// 并返回这个时候需要发送的消息
else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
// 返回需要发送的消息
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
...
}
}
}
这个方法注释写了很清楚,总的来说就是没有消息则阻塞等待,有消息的就取出消息
Handler
的dispatchMessage
方法
// Handler.java
public void dispatchMessage(Message msg) {
// 一般我们还可以使用Handler.post(Runnable r)方法发送消息
// 这个时候的msg.callback 就是这个 r
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
这个方法就简单了,如果我们设置了回调,则会回调出去,具体执行什么方法要看你怎么发送这个消息。
上面我们举的例子是这样发的
val obtain = Message.obtain()
obtain.what = 1
handler.sendMessage(obtain)
如果使用上面的这个handler.sendMessage(Message msg)
则会传消息给我们初始化的时候的回调函数
val handler: Handler = Handler(
Handler.Callback {
when (it.what) {
1 -> {
// doSomething
}
else -> {}
}
true
}
)
还有另外一种发送消息的方法,就是使用post(Runnable r)
发送
handler?.post {
object : Runnable {
override fun run() {
}
}
}
我们可以看下这个post方法
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
可以看到这里有个赋值,m.callback = r
,所以我们post发送数据后对应的就是运行这个r
public void dispatchMessage(Message msg) {
// 一般我们还可以使用Handler.post(Runnable r)方法发送消息
// 这个时候的msg.callback 就是这个 r
if (msg.callback != null) {
handleCallback(msg);
} else {
...
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
直接运行r
的run
方法
解决了上面两种数据接收方式,还剩下一个handleMessage(msg)
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
...
handleMessage(msg);
}
}
这个用于我们创建一个类,继承自Handler
class Myhandler : Handler() {
override fun handleMessage(msg: Message?) {
}
}
这个时候使用如下方式发送消息,则会在上面重写的方法handleMessage
中接收到消息
val obtain = Message.obtain()
obtain.what = 1
myHandler.sendMessage(obtain)
分析到这,基本算是把Handler机制给理清了,看下来可能有点吃力,不过如果大家自己跟着源码一步一步走下去,自己也是可以理解清楚的
总结
Handler机制:
1,Looper消息循环机制 创建
使用Looper.prepare()
,创建一个Looper
实例,并保存在Looper
的一个静态变量sThreadLocal
中,然后用Looper.loop()
开启消息循环,此时创建的Handler可以传指定的Looper实例进去,如果是主线程的创建的Handler,则已经有Looper实例了,此时就不需要创建Looper实例
2,Handler发送消息
使用Handler.sendMessage(Message msg)
,或者Handler.post(Runnable r)
发送消息,此时会调用Handler
的enqueueMessage()
,里面调用了MessageQueue
的enqueueMessage
,此时就把消息根据发送时间进行了排序
3,Looper.loop() 循环,检测消息并发送
真实的发送放在了Looper.loop()
方法里面,此时会调用Message.next()
方法,获取一个消息,如果没有消息,next()
方法则会阻塞等待,释放CPU,一旦有消息,则会唤醒,并在Looper.loop()
方法里面进行消息的发送,然后消息发送又调用了Handler.dispatchMessage()
方法,此时我们就可以接收到消息了