因为是由浅到深的讲解,我会在高级部分做专门的标注,基础部分是必须记住的部分,高级部分根据个人情况进行掌握,现在开始
Handler在新手时期是非常好用的,它是android.os包下的一个类,主要用处有以下几点
1、线程切换
2、延时操作
3、消息队列
4、界面间通信
这几个功能决定了基本任何中大型项目都会用到Handler,我们的讲解也会围绕这几个点展开
1、线程切换
最基本的用法,在主线程里可以这样调用
Handler().post {
// 执行UI线程操作
}
一句简单的代码就能让你轻松实现任何线程切换到主线程的操作,主要用于子线程耗时操作完成后的界面刷新,比如网络接口请求后,能力引擎调用后
当然,我们不要频繁的new Handler(),我们可以声明一个handler,在需要的时候使用
注意,我们说的是线程切换,不是切换到主线程,意思是,Handler也能切换到子线程
下面以一个工具类举例 首先看用法,简洁明了,获取后台线程的Handler和UI线程的Handler
工具类的实现如下
public class TaskRunner {
private static final String TAG = "TaskRunner";
private Handler mUIHandler;
private Handler mBackHandler;
private TaskRunner() {
mUIHandler = new Handler(Looper.getMainLooper());
}
private static class SingletonHolder {
static TaskRunner sRunner = new TaskRunner();
}
public static synchronized Handler getUIHandler() {
return SingletonHolder.sRunner.mUIHandler;
}
public static synchronized Handler getBackHandler() {
Handler backHandler = SingletonHolder.sRunner.mBackHandler;
if (null == backHandler) {
HandlerThread handlerThread = new HandlerThread(TAG);
handlerThread.start();
backHandler = new Handler(handlerThread.getLooper());
SingletonHolder.sRunner.mBackHandler = backHandler;
}
return backHandler;
}
}
可以看到,我们创建主线程的Handler,在有可能是子线程使用的情况下,不是单纯的new Handler(),而是使用new Handler(Looper.getMainLooper()),目前我们只需要记住这个写法,具体原因会在后续高级部分中讲解
同理,切换到子线程,使用new Handler(handlerThread.getLooper())进行初始化 这个HandlerThread也会在后续讲解
这个切换工具类十分便捷,作为开发的常用工具使用
2、延时操作
Handler().postDelayed({
// 延迟两秒做一些事情
}, 2 * 1000)
延时一定时间后执行,并且不阻塞主线程,这个方法很重要,主要用处如下
a、定位问题时,如果猜想是时序问题,可以使用延时执行某段代码来确认
b、解决频繁调用问题-这个涉及到消息队列,讲第三点的时候会解释
c、交互上要求需要延时一定时间,可以使用
但使用不熟练的话不建议在正式项目中使用,因为可能引起大问题
a、延迟操作可能会出现界面退出被回收时执行了刷新操作,导致空指针崩溃
解决方法:在界面Destroy时,调用handler的removeMessage(0)来移除所有延时操作
b、频繁调用会在消息队列中不停插入消息,导致界面更新缓慢或者卡顿
解决方法:不要频繁调用,或者remove上一个消息
3、消息队列
首先我们要知道,在我们持有一个handler的时候,我们的post,postDelay,发送的事件,Handler是不会立刻响应的,它们会在一个队列里,Handler会不断取出我们发送的事件,一个个处理,那么我们再来看,这个流程涉及到了哪些类
a、Handler的消息队列涉及哪些类,他们又是怎样工作的?
主要有三个类:
Looper 字面意思是循环,是我们上面说的,不断取出消息队列中事件的方式
MessageQueue 是我们上面说的消息队列,它只是一个队列,可以存储Message
Message 是我们上面说的事件,我们的post,postDelay方法,发送的就是Message
即:Handler从消息队列(MessageQueue )中不断循环(Looper )取出我们发送的事件(Message ),一个个处理
【高级部分】详解三个类的实现
a、Message 消息,它本质上是一个消息对象类,它主要有三个变量
target:就是负责发送且分发它的Handler;
callback:是一个Runnable回调;
next:下一个要执行的message
b、MessageQuenue 消息队列,它本质上是一个消息管理类
mMessage是一个Message对象,上面说过,Message的next标记了下一个要执行的Message,所以MessageQuenue持有了Message的单链表,并接收和处理消息
通过 boolean enqueueMessage(Message msg, long when) 函数接收消息
boolean enqueueMessage(Message msg, long when) {
// 校验Handler
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
// 简单的线程同步
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) {
// 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();
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;
}
我们来仔细分析下这段代码
可以看到,for循环里会比对消息发送的时间,并按时间先后顺序执行
注意,这里调用的native方法nativeWake()唤醒取出消息的next()
当消息入队时候,如果队列为空的或需要立即分发或当前消息分发时间-早于消息队列中最近一个需要分发的消息的时间的(而此时MessageQueue的next()方法的循环是处于阻塞状态的),就调用一个native方法nativeWake()唤醒取出消息的next()
通过 Message next() 函数处理消息
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//-1-直接休眠一定的时间;
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;
//-2-找出下一个需要分发的消息。
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());
}
if (msg != null) {
//-3- 如果时机未到,即当前时间还不到消息的处理时间,就计算休眠时间,
//反之,返回这个待处理的消息。并将休眠状态mBlocked置为false
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);
} 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;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
//-4-只有当消息队列是空的或者现在还不到待处理的消息的时间的时候,idleHanler(空闲handler)才会被执行
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
//-5-如果没有空闲Handler待处理(消息队列也没有需要处理的message),那么,标记一下循环被阻塞了。
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
//-5-将idleHandler列表转为数组保存
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
//-6-开始处理IdleHandler的事务。
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
//-7-如果IdleHandler的queueIdle返回false,就不需要保持,这执行完一次任务后直接移除,反之,
//后面每次空闲的时候都执行
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
这里调了native的休眠方法——nativePoolOnce()(正好对应了消息入队的nativeWake()),如果没有消息处理的时候,next()方法就会阻塞在这里,而阻塞却没有导致当前线程ANR的原因,是因为native的nativePoolOnce方法时候,调用了linux低层,使当前线程腾出CPU的使用权,处于休眠等待状态,直到休眠时间结束或消息入队被唤醒。