简介
Handler是Android中的一种消息机制。handler的应用很广,平时我们自己的继承Thread、实现Runnable接口实现异步通信时都会使用到Handler,很多异步框架如AsyncTask,handlerThread等内部也都使用了Handler。
使用
因为Android不能在子线程中更新UI,当我们子线程执行异步任务时需要更新UI,这就可以使用在主线程中new Handler实例,在子线程中获取handler,实例使用sendMessage或者post方法通知主线程去更新UI。
简单demo展示
class HandlerTestActivity : AppCompatActivity() {
lateinit var handler: Handler
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_hander_test)
btnHandler.setOnClickListener {
var thread = Thread {
Thread.sleep(3000)
var msg = Message.obtain()
msg.what = 1
handler.sendMessage(msg)
}.start()
}
handler = Handler() {
if (it.what == 1) {
tvHandler.setText("已点击按钮")
}
flase
}
}
}
这里用的时handler的sendMessage方法,也可以使用post方法。使用post方法就不用单独在Handler后边写相应的UI操作了,直接写在Runable里面,还可以用postDelayed进行延迟效应的操作。
而Handler在实例化的时候需要实现Handler.callback接口里的handleMessage方法,这里写的是主线程接受到工作线程发过来消息的UI更新操作。
handler = Handler()
btnHandler.setOnClickListener {
Thread {
handler.postDelayed(Runnable {
tvHandler.setText("已点击按钮")
},1000)
}.start()
}
源码分析
Handler
handler的工作过程是与Looper和messageQueue禁密相连的。每个线程可以对应多个Handler,但是每个线程只有一个Looper和一个MessageQueue。 MessageQueue是线程消息的中转站,Looper是消息的管理者,当handler发送消息到消息队列后,消息队列中的消息会依次被取出,再分发到对应handler,让Handler再执行所在的线程的回调(handleMessage)。
- Handler共有7个构造函数,由 Looper/Callback/async三个参数的有无自我组合,其中有3个含async的方法通过@hide标签被隐藏为无法使用,初始化mLooper/mQueue/mCallback/mAsynchronous。
- 使用构造函数创建的Handler下处理程序是同步的,即mAsynchronous=false,而使用静态方法createAsync创建的handler会传入async = true。
- mLooper和mQueue只需创建一次,通过Looper的静态方法mylooper()直接获取,然后通过looper对象获取到对应的queue。
- Handler的Callback里面又一个handleMessage方法,在这里作为一个参数,我们要传入就必须实现回调里面的方法,这里写的也就是处理消息后的动作,如主线程更新UI。
- FIND_POTENTIAL_LEAKS是静态常量默认false,当要继承Handler写自己的Handler时,可以把它设为true,用于检测内存泄漏。
//同步方式
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 " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
//异步方式
@NonNull
public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) {
if (looper == null) throw new NullPointerException("looper must not be null");
if (callback == null) throw new NullPointerException("callback must not be null");
return new Handler(looper, callback, true);
}
- Message实例化obtainMessage有5个方法,最终指向Message的静态obtain方法,实例化一条message,但是这里自动传入handler将message.target = handler,我们直接使用message.obtain没有绑定Handler。但是这并不影响消息的传递。
- 创建的是异步的Handler。此时当传递消息到消息队列执行enqueueMessage时会进入msg.setAsynchronous(true)。异步消息消息结合同步屏障可以实现立即执行的效果(优先级最高),但是现在messageQueue的同步屏障方法在api28中被@hide,已经不推荐使用,并且在api28 之后无论是反射还是JNI方式也无法使用@hide标记的方法。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
- sendMessage——>sendMessageDelayed——>sendMessageAtTime——>enqueueMessagea。
这是sendMessage的调用顺序,我们可以通过前三种形式发送即时/延迟/定时消息,他们的实现过程也是向前递进的sendMessage延迟0秒,sendMessageDelayed在当前时刻➕延迟时间的时刻去执行。handler的enqueueMessage最后调用queue的enqueueMessage方法。发送消息完成。在Handler的enqueueMessage方法中指定message的target为当前Handler。
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;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
- handler除了直接sendMessage还可以使用post方式,post方式会传入Runnable参数,并赋值给message.callback,在把这个message通过对应的send方式发送出去,postDelay、postAtTime同上。
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;
}
- dispatchMessage就是最终处理消息,消息最终又回到了Handler,如果消息是通过post方式传来的,那么就会通过handleCallback方式去执行那个Runnable,会去mCallback.handleMessage(msg)执行初始化传过来的回调,返回的结果也是自己设定的,这个参数就是Handler初始化传进来的重写的handle方法的返回值,如果是false会接着执行handleMessage,需要重写。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
Looper
- looper初始化
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
- 在我们通常使用的looper的初始化方法子线程默认looper可以退出,prepareMainLooper是初始化主线程的looper不允许退出,这里传入了给quitAllow传入ture之后,那么使用Looper的quit就可以结束loop循环,再来消息就不会进行任何处理了。
- 因为looper的构造函数是私有的,实例化looper必须经过prepare方法,looper.prepare在实例化looper的时候使用了ThreadLocal的set方法,这样就保证了一个线程只有这一个looper对象的存在了。
- looper在实例化时先实例化了一个MessageQueue, 后绑定当前的thread,这就保证了这个线程里只有一个lopper和一个messageQueue。
- looper开启循环
public static void loop() {
public static void loop() {
...// 仅贴出关键代码
// 1. 获取当前Looper的消息队列
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;
// 获取Looper实例中的消息队列对象(MessageQueue)
// 2. 消息循环(通过for循环)
for (;;) {
// 2.1 从消息队列中取出消息
Message msg = queue.next();
if (msg == null) {
return;
}
// next():取出消息队列里的消息
// 若取出的消息为空,则线程阻塞
// 2.2 派发消息到对应的Handler
msg.target.dispatchMessage(msg);
// 把消息Message派发给消息对象msg的target属性
// target属性实际是1个handler对象
// 3. 释放消息占据的资源
msg.recycle();
}
}
- loop方法里面又一个for的死循环,每循环一次,就获取下一条消息,直接调用与消息绑定的Handler,用这个Handler去dispatchMessage(),到此就实现了一条消息的分发,接着会把这条已经分发的消息销毁释放资源。然后直至将消息队列的消息都分发完成。
- 到消息队列的消息为空是,线程阻塞,直到有下一条消息的到来。需要注意的是在主线程在执行looper.loop 之前用过thread.attach(false)建立Binder通道 (创建新线程),不会循环卡死。
链接
当消息队列为空时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。
所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。
Message
Message为了进行序列化实现了Parcelable接口,可以在不同进程间传递。
- Message的初始化方法如下,先是选择性初始化标识符what,两个int类型的参数和一个任意对象,再是单链表尾插。
public static Message obtain() {
//加锁保证线程安全
synchronized (sPoolSync) {
//sPool是上一条message,在初始化当前msg时,会把上一条msg的next指向当前msg。
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
//每增加一条消息,poolSize就-1
sPoolSize--;
return m;
}
}
return new Message();
}
- 消息回收,如果Handler初始化的是async=true,也就是是异步Handler,那么这里回收就会出抛出异常。否则使用recycleUnchecked进行回收msg。
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
3.其他参数说明
- target:这是msg锁绑定的Handler,可以在初始化或者send的时候进行绑定,每条消息都会被绑定,已便最终分发到对应的Handler。
- when:消息发送出去的时刻,是long类型的时间戳。
- data:Bundle类型的参数,用于存储一些额外数据。
- callback:存储一些异步动作,在事件处理是可直接执行callback里的操作。
MessageQueue
MessageQueue是一个用final修饰的类不可被扩展,它里面主要是对msg的管理。
- enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
//msg必须要target,否则无法传递分发,抛出异常
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//消息不可以重复使用
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
//当执行了looper.quit,looper结束循环,还没有发送出去的消息直接回收
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;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//消息队列的消息为空,直接插入
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//消息队列不为空,就通过msg发送时间排序,
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
//p为空说明到了队列尾部,或者找到了刚好比当前msg.when大的时间戳,进行插入
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;
}
- 需要注意的是messge在初始化的时候有一个初始化顺序先后的队列的,但是在消息被handler post后会执行enqueueMessage,他会重新对message按照时间先后顺序排列进行排列。
- 插入同步屏障postSyncBarrier方法是被hide了,他是在消息队列中插入一条没有target的消息,将后边同步消息都堵塞住,但是异步消息不受影响。当移除同步屏障后,才可以正常接受同步消息。
- message.next() // 该参数用于确定消息队列中是否还有消息, 从而决定消息队列应处于出队消息状态 或等待状态
Message next() {
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// nativePollOnce方法在native层,若是nextPollTimeoutMillis为-1,此时消息队列处于等待状态
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 出队消息,即 从消息队列中取出消息:按创建Message对象的时间顺序
if (msg != null) {
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 取出了消息
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 {
// 若 消息队列中已无消息,则将nextPollTimeoutMillis参数设为-1
// 下次循环时,消息队列则处于等待状态
nextPollTimeoutMillis = -1;
}
......
}
.....
}
}
4.quit,looper的quit方法最终调用的还是mesageQueue的quit方法,分为安全和非安全两种,安全的会清空消息队列在quit,非安全的就直接quit。