一、Handler总览
Handler是Android消息处理机制的上层接口。
Android消息处理机制本质:一个线程不断地从消息队列中获取消息,并进行处理的机制
Handler主要应用场景:在子线程中进行一些耗时的I/O操作,操作完毕后需要在主线程更新UI,使用Handler将UI更新操作切换到主线程执行。
看一下Handler内的几个核心角色:
-
Message:消息的载体。包含消息ID,消息处理对象以及处理的数据等,由
MessageQueue统一列队,终由Handler处理 -
Handler:发送消息的工具类,用于发送和处理消息
-
MessageQueue:存储
Handler发送过来的Message,内部维护了Message链表,同时也是与native通信的中枢。每次拿取
Message时,若该Message离真正执行还需要一段时间,会通过nativePollOnce进入阻塞状态,避免资源的浪费。若存在消息屏障,则会忽略同步消息优先拿取异步消息,从而实现异步消息的优先消费。 -
Looper:一个用于遍历
MessageQueue的类。每个线程有一个独有的Looper,在所处线程开启一个死循环,不断从MessageQueue中获取Message,然后交给Handler处理 -
Messenger:可以跨进程传输的消息载体 -
......
总览一下Handler的整体架构设计图
APP内可运行多个线程,不同线程间可以互相拿到对方的Handler对象MessageQueue与native通信,native与kernel通信,此调用链赋予了App使用系统内核资源的能力- 一个线程有多个
Handler,只能有一个Looper,一个Looper对象拥有一个MessageQueue
看一下Handler的消息处理机制流程
Handler的消息处理机制流程基本是:
- 在
handler所创建的线程需要维护一个唯一的Looper对象, 一个线程只能有一个Looper,每个线程的Looper通过ThreadLocal来保证。 Looper对象的内部又维护有唯一的一个MessageQueue。所以一个线程可以有多个handler,但是只能有一个Looper和一个MessageQueueMessage在MessageQueue不是通过一个列表来存储的,而是将传入的Message存入到了上一个Message的next中,在取出的时候通过顶部的Message就能按放入的顺序依次取出MessageLooper对象通过loop()方法开启了一个死循环,不断地从looper内的MessageQueue中取出Message,然后通过handler将消息分发传回handler所在的线程
二、基本使用
看一下Handler的消息处理的分发函数dispatchMessage
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
从dispatchMessage可知,Handler的使用分为handleCallback()、mCallback.handleMessage()和handleMessage()
2.1 handleCallback
handleCallback()可以理解为收发一体,消息的发送和接收都在post()中完成,无需创建Message实例
class MainActivity : AppCompatActivity(){
private lateinit var mMsgTv:TextView
private val mHandler = Handler(Looper.getMainLooper())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mMsgTv = findViewById(R.id.handler_demo)
thread {
val info = "子线程处理结果"
mHandler.post {
mMsgTv.text = info //UI更新
}
}
}
}
2.2 handleMessage
handleMessage又分为mCallback.handleMessage()与handleMessage()
mCallback.handleMessage()使用
实现callback接口即可
class MainActivity : AppCompatActivity(){
private lateinit var mMsgTv:TextView
//接收消息,刷新UI
private val mHandler = Handler(Looper.getMainLooper()) { msg ->
if (msg.what == 1) {
mMsgTv.text = msg.obj.toString()
}
//返回false 重写Handler类的handleMessage会被调用;返回true 则不会被调用
return@Handler false
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mMsgTv = findViewById(R.id.handler_demo)
//子线程中发送消息
thread {
val message = Message.obtain()
message.what = 1
message.obj = "者文个人博客:https://zhewendev.github.io/"
mHandler.sendMessage(message)
}
}
}
handleMessage()使用
重写Handler类的handlerMessage()方法
class MainActivity : AppCompatActivity(){
private lateinit var mMsgTv:TextView
//接收消息,刷新UI
private val mHandler = object :Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
if (msg.what == 1) {
mMsgTv.text = msg.obj.toString()
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mMsgTv = findViewById(R.id.handler_demo)
//子线程中发送消息
thread {
val message = Message.obtain()
message.what = 1
message.obj = "者文微信公众号:者文静斋"
mHandler.sendMessage(message)
}
}
}
下面看一下Handler的内部实现
三、Looper
实际开发中,若遇到子线程与子线程通信,在子线程初始化Handler时,需
thread {
Looper.prepare()
val handler = Looper.myLooper()?.let { Handler(it) }
Looper.loop()
}
主线程初始化Handler时怎么不需要做Looper.prepare()等处理呢,看一下主线程入口
ActivityThread.java
...
public static void main(String[] args) {
...
//主线程Looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
//主线程的loop开始循环
Looper.loop();
...
}
//Looper.java
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
...
主线程在Looper.prepareMainLooper() 中创建了主线程的Looper,并调用了loop方法,所以可以直接使用Handler
prepare和loop作用
为什么要使用prepare和loop呢
Looper.prepare():生成Looper对象,set在ThreadLocal里handler构造函数:通过Looper.myLooper()获取到ThreadLocal的Looper对象Looper.loop():内部有个死循环,开始事件分发
prepare()实现
看一下prepare()
//Looper.java
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");
}
//将new出来的Looper实例设置给sThreadLocal
sThreadLocal.set(new Looper(quitAllowed));
}
Looper.prepare()会生成Looper对象,set在ThreadLocal里。一个线程内只能使用一次prepare(),否则会抛异常
loop()实现
Looper创建好后,不会自己启动,需要手动启动,调用loop()函数即可。创建Looper并开启消息循环,Looper的prepare和loop方法是配套使用的,两者必须成对存在。
public static void loop() {
//获取当前线程的Looper
final Looper me = myLooper();
......
//开启死循环读取消息
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // might block
.....
try {
//调用Message对应的handler处理消息
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
}
......
//回收Message
msg.recycleUnchecked();
return true;
}
Looper.loop()主要实现内容:
- 获取当前线程的
Looper对象和MessageQueue,开启死循环读取消息 - 通过
dispatchMessage方法把消息分发下去 - 消息回收,放回到消息缓存池。
这里需要注意的是 Message 对象并没有释放,会缓存起来
Looper小结:
Looper会不断从MessageQueue中获取消息,然后交给Handler去处理。Looper使用前需要先初始化当前线程的Looper对象,再调用loop()方法来启动它
Handler是线程切换的核心,因为不同的Looper运行在不同的线程,其调用的dispatchMessage方法则运行在不同的线程,因此Message的处理就被切换到Looper所在的线程了。
当looper不再使用时,可调用不同的退出方法来退出他。Looper一旦退出,线程则会直接结束。
四、消息收发
看一下Message的数据结构
public final class Message implements Parcelable {
public int what;
public int arg1;
public int arg2;
public Object obj;
public long when;
Message next;
Handler target;
......
}
其中target是Handler类型,表示该Message归属的Handler,会在Handler发送消息时赋值。
先看一下Message中when这个属性,这是内部包才能访问的写操作,将消息加入到消息队列的时候会给发送的消息设置该属性。
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
再继续追溯下去,发现when = SystemClock.uptimeMillis() + delayMillis
使用sendMessage发送消息的时候,实际上也会调用sendMessageDelayed延时发送消息发放,不过此时传入的延时时间会默认为0
4.1 发送消息
消息的发送形式有两种:sendMessage()和post(),对于post(),其Runnable会被封装进一个Message,所以它本质上还是一个Message
Handler发送消息,不管使用哪种形式,都会调用到Handler的enqueueMessage()方法中,最终调用到MessageQueue的enqueueMessage()中
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
post()和sendMessage()都通过sendMessageDelayed()进行消息发送,再继续跟踪
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
......
return enqueueMessage(queue, msg, uptimeMillis);
}
//Handler.java
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
//将当前的Handler赋值给Message的target属性
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
//设置为异步方法
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
Handler发送消息,调用到Handler的enqueueMessage()方法中,将当前Handler赋值给Message的when,最后调用到MessageQueue的enqueueMessage方法
MessageQueue/enqueueMessage()方法
boolean enqueueMessage(Message msg, long when) {
//Handler为空,抛出异常
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//对MessageQueue进行加锁
synchronized (this) {
//当前消息如果已经被执行,抛出异常
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
//判断目标Thread是否已死亡
if (mQuitting) {
......
msg.recycle();
return false;
}
//标记Message正在被执行,以及需要被执行的时间。
msg.markInUse();
msg.when = when;
Message p = mMessages; //P是messageQueue的链表头
boolean needWake; // 判断是否需要唤醒MessageQueue
// 如果有新的队头,同时MessageQueue处于阻塞状态则需要唤醒队列
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
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;
}
//如果需要则唤醒队列
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
MessageQueue的enqueueMessage()主要内容是:
- 首先判断
Message中的Handler不能为空,且Message不能为在使用中,否则抛异常 - 对
MessageQueue进行加锁,判断当前线程是否dead,dead就抛异常 - 标记
Message的执行时间等 - 新插入的
Message在链表头时,如果messageQueue是空的或者正在等待下个延迟消息,则需要唤醒MessageQueue - 根据
Message的执行时间,找到在链表中的插入位置进行插入,这里我们可以理解MessageQueue中维护了一个优先级队列
4.2 分发消息
Handler发送的消息放入了MessageQueue中,接着进入消息的读取与分发了。
Looper的Loop方法会从MessageQueue中循环读取消息,调用queue.next()
MessageQueue/next()实现
Message next() {
// Return here if the message loop has already quit and been disposed.
// 源码中的注释表示:如果looper已经退出了,这里就返回null
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0; // 定义阻塞时间赋值为0
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 阻塞对应时间 这个方法最终会调用到linux的epoll机制
nativePollOnce(ptr, nextPollTimeoutMillis);
// 对MessageQueue进行加锁,保证线程安全
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//...
if (msg != null) {
if (now < msg.when) {
// 下一个消息还没开始,等待两者的时间差
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 获得消息且现在要执行,标记MessageQueue为非阻塞
mBlocked = false;
// 链表操作
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
// 没有消息,进入阻塞状态
nextPollTimeoutMillis = -1;
}
//退出
if (mQuitting) {
dispose();
return null;
}
//...
}
}
MessageQueue/next()目的:
- 获取
MessageQueue中的一个Message,进行死循环读取。 - 如果消息队列中没有消息,那么
next方法会一直阻塞在这里。 - 当有新消息到来时,就会将它唤醒,
next方法会返回这条消息并将其从优先级队列中给移除。
获取到消息后,进行消息的分发,即msg.target.dispatchMessage(msg)
消息池
loopOnce代码尾部执行了msg.recycleUnchecked(),这是将完成分发的消息放入消息池,等待被复用。有存入消息池操作,肯定有取出操作。
当我们获取Message的时候,官方建议是通过Message.obtain()方法来获取,看一下该方法
public static Message obtain() {
synchronized (sPoolSync) {
//消息池不为空,取消息池头节点
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
Message维护了一个静态链表,链表头是sPool,Message有一个next属性,Message本身就是链表结构。
sPoolSync是一个object对象,仅作为解决并发访问安全设计。当我们调用obtain来获取一个新的Message的时候,首先会检查链表中是否有空闲的Message,如果没有则新建一个返回。
看一下msg.recycleUnchecked()
void recycleUnchecked() {
// Clear out all other details.
//相关属性全部重置
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
//存入消息池
synchronized (sPoolSync) {
//判断是否超出消息池的最大容量
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
Message使用完成后,可以调用Message的recycle方法进行回收。把Message中的内容清空,然后判断链表是否达到最大值(50),然后插入消息池的链表中。
4.3 接收消息
直接看一下dispatchMessage()实现
Handler.java
...
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
...
五、同步屏障
Handler发送的Message会放入到MessageQueue中,MessageQueue中维护了一个优先级队列,将存储数据的单链表按照时间升序进行排序,Looper则按照顺序,每次从这个优先级队列中取出一个Message进行分发,一个处理完就处理下一个。
是否可以让指定的Message优先被处理?当然可以,即通过同步屏障实现。
看一下MessageQueue.next()中部分关键代码
Message next() {
//...
for (;;) {
synchronized (this) {
if (msg != null && msg.target == null) {
// 存在同步屏障,寻找队列中下一个同步屏障消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
}
}
msg.target什么时候会为空?怎么判断是异步消息?
看一下Handler的enqueueMessage()方法
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
//将当前的Handler赋值给target
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
通过Handler的post和send系列方法都会走到这个方法内,msg.target会被赋值,不可能为空。说明是其他消息发送的方法导致的。
5.1 同步屏障消息生成
看一下MessageQueue的postSyncBarrier
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
//Message target属性为null的消息插入
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
Message的target属性为null就是同步屏障。这是一种特殊消息,不会被消费,仅作为一个标识处于MessageQueue中。当MessageQueue的next方法遇到同步屏障的时,会循环遍历整个链表找到标记为异步消息的Message,忽略其他消息,则异步消息就被提前执行了。
异步消息处理完,同步屏障不会被移除,需要手动移除。若不移除,同步消息永远无法执行。
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
5.2 异步消息生成
如何生成一个异步消息?看一下Handler的enqueueMessage()方法
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
//如果mAsynchronous,将消息设置为异步消息
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
通过msg.setAsynchronous方法设置为true,可以把一个消息变成异步消息,但是前提得满足mAsynchronous属性为true。
mAsynchronous是Handler中的一个属性,会在这两个构造方法中被赋值
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
//...
mAsynchronous = async;
}
public Handler(@Nullable Callback callback, boolean async) {
//...
mAsynchronous = async;
}
前述的两个构造方法都是对外不可见的,无法调用,且设置同步屏障的方法对外也是不可见,只能系统会去使用它。
例如在ViewRootImpl中执行UI绘制的方法会使用到同步屏障:
ViewRootImpl.java
...
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
在把绘制消息放入队列之前,先放入了一个同步屏障,然后再发送异步绘制消息,从而使得界面绘制的消息会比其他消息优先执行,避免了因为 MessageQueue 中消息太多导致绘制消息被阻塞导致画面卡顿。当绘制完成后,就会将同步屏障移除。
六、IdleHandler
IdleHandle是MessageQueue中的一个静态内部接口,当消息队列没有消息时或者是队列中的消息还没有到执行时间时才会执行的 IdleHandler,它存放在 mPendingIdleHandlers 队列中。
public static interface IdleHandler {
boolean queueIdle();
}
IdleHandle常见应用场景:在UI线程处理完所有View事务后,处于空闲状态时会回调该接口进行一些额外操作,且不会阻塞主线程。
6.1 基本使用
class MainActivity : AppCompatActivity(){
companion object {
const val TAG = "MainActivity"
}
private val mHandler = object :Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
if (msg.what == 2 || msg.what == 3) {
Log.d(TAG, msg.obj.toString())
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
testIdleHandler()
}
fun testIdleHandler() {
Looper.myQueue().addIdleHandler {
Toast.makeText(this@MainActivity,"消息空闲时发个Toast",Toast.LENGTH_SHORT).show()
Log.d(TAG,"来自IdleHandler 的空闲消息")
return@addIdleHandler false
}
val message = Message.obtain()
message.what = 2
message.obj = "来自Handler 的消息"
mHandler.sendMessage(message)
val message1 = Message.obtain()
message1.what = 3
message1.obj = "来自Handler 的第二条消息"
mHandler.sendMessage(message1)
}
}
/******打印结果******/
来自Handler 的消息
来自Handler 的第二条消息
来自IdleHandler 的空闲消息
IdleHandler在消息队列其它消息执行完后才执行,且只执行一次。如果前述示例addIdleHandle操作时返回true,则会多次执行,只要空闲就会一直执行,不会从 IdleHandler 的队列中删除。
6.2 原理解析
看一下addIdleHandler()的内部实现
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
//添加进mIdleHandlers队列中
mIdleHandlers.add(handler);
}
}
看一下MessageQueue的next关于IdleHandler的部分内容
Message next() {
int pendingIdleHandlerCount = -1;
for(;;) {
synchronized (this) {
//当前无消息,或还需要等待一段时间消息才能分发,获得IdleHandler的数量
if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
//如果没有idle handler需要执行,阻塞线程进入下次循环
mBlocked = true;
continue;
}
//初始化mPendingIdleHandlers
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
//把List转化成数组类型
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
//循环遍历所有的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 {
//获得idler.queueIdle的返回值
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
//keep即idler.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避免下次再次执行
pendingIdleHandlerCount = 0;
// 当在执行IdleHandler的时候,可能有新的消息已经进来了
// 所以这个时候不能阻塞,要回去循环一次看一下
nextPollTimeoutMillis = 0;
}
}
MessageQueue.next()中关于IdleHandler的主要内容:
- 初始时,将
pendingIdleHandlerCount赋值为-1 - 判断
pendingIdleHandlerCount是否小于0并且MessageQueue是否为空或者有延迟消息需要执行。如果是则把存储IdleHandler的list的长度赋值给pendingIdleHandlerCount - 判断如果没有
IdleHandler需要执行,阻塞线程进入下次循环。如果有,则初始化mPendingIdleHandlers,把list中的所有IdleHandler放到数组中。这一步是为了不让在执行IdleHandler的时候List被插入新的IdleHandler,造成逻辑混乱 - 循环遍历所有的
IdleHandler并执行,查看idler.queueIdle方法的返回值。为false表明这个IdleHandler只需要执行一次,并移除;为true,则不移除 - 将
pendingIdleHandlerCount置为0避免下次再次执行。当在执行IdleHandler的时候,可能有新的消息已经进来了,所以这个时候不能阻塞,要回去循环一次看一下
七、Handler典型应用
7.1 Looper妙用
可以通过Looper.getMainLooper方法获取主线程Looper,从而可以判断当前线程是否是主线程
可以通过Handler将Runnable post到主线程执行
object MainThread {
private val HANDLER = Handler(Looper.getMainLooper())
fun run(runnable: Runnable) {
if (isMainThread) {
runnable.run()
} else {
HANDLER.post(runnable)
}
}
val isMainThread: Boolean
get() = Looper.myLooper() == Looper.getMainLooper()
}
7.2 HandlerThread
主线程发送消息到子线程处理,可以用HandlerThread,其继承了Thread并对Looper进行了封装。
class MainActivity : AppCompatActivity(){
companion object {
const val TAG = "MainActivity"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
testHandlerThread()
}
fun testHandlerThread() {
//1,创建HandlerThread实例
val mHandlerThread = HandlerThread("HandlerThread")
//2,启动线程
mHandlerThread.start()
//3,使用传入Looper为参数的构造方法创建Handler实例
val handler: Handler = object : Handler(mHandlerThread.looper) {
override fun handleMessage(msg: Message) {
Log.d(TAG, "当前线程: " + Thread.currentThread().name + " handleMessage")
}
}
//4,使用Handler发送消息
handler.sendEmptyMessage(0x001)
//5 在合适的时机调用HandlerThread的quit方法,退出消息循环
}
}
Handler、HandlerThread、Thread三者区别:
Handler:在Android中负责发送和处理消息HandlerThread:继承自Thread,对Looper进行了封装,也就是说它在子线程维护了一个Looper,方便我们在子线程中去处理消息Thread:cpu执行的最小单位,即线程,它在执行完后就立马结束了,并不能去处理消息。如果要处理,需要配合Looper,Handler一起使用
7.3 子线程处理Toast、Dialog
fun testToast() {
thread {
Toast.makeText(this@MainActivity,"子线程弹Toast",Toast.LENGTH_SHORT).show()
}
}
上述代码运行会崩溃
java.lang.NullPointerException: Can't toast on a thread that has not called Looper.prepare()
Toast的实现依赖Handler,在子线程中创建Handler,需先创建Looper并开启消息循环
private fun testToast() {
thread {
Looper.prepare()
Toast.makeText(this@MainActivity,"子线程弹Toast",Toast.LENGTH_SHORT).show()
Looper.loop()
}
}
Dialog与Toast类似,其实现也依赖Handler。