Handler如何做到切换线程
1.Handler类里包含mLooper和mQueue;
2.Looper类包含mQueue;
3.目标线程通过调用Looper.prepare方法创建一个Looper对象,存在ThreadLocal里;
4.创建Looper对象的时候,会创建一个MessageQueue队列,就是mQueue;
5.调用Handler.post方法本质是往目标线程的mLooper的mQueue塞一条Message;
6.Looper通过loop方法不断轮询mQueue队列,处理Message,从而实现线程切换。
倒过来推演,如何切换线程:
1.想办法向目标线程的mQueue塞一个Message,这一步需要Handler来实现。
2.如何获取到目标线程的mQueue呢?mQueue在目标线程的mLooper对象里。
3.如何获取目标线程的mLooper对象呢?两种方法:传进去一个目标线程的mLooper;通过ThreadLocal,mLooper = Looper.myLooper()获取,这一步必须在目标线程执行;
5.所以构造Handler,要么传进去一个目标线程的mLooper;要么在目标线程执行没有Looper参数的构造方法。
关于同步屏障机制
1.同步屏障可以通过MessageQueue.postSyncBarrier函数来设置;
2.同步屏障就是一个Message,一个target字段为空的Message;
3.MessageQueue的next()方法,在遇到同步屏障Message时,会返回下一个异步消息(自动忽略同步消息),变相的提高的异步消息的处理优先级。
4.ViewRootImpl.scheduleTraversals应用到了同步屏障机制。
Message的tagert何时赋值
执行enqueueMessage的时候
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
底层wake机制
1.Android 6.0版本以前,采用pipe/epoll机制。Looper初始化时会创建一个pipe,生产两个fd(读端和写端)。
上层MessageQueue在读取消息时,走到nativePollOnce方法,线程可能会休眠:
Message next() {
...
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis); //这一步会休眠,但是有个超时时间nextPollTimeoutMillis,即下一个msg的需要被处理的时间
...
}
}
而在消息入队时,会去检测是否有必要唤醒休眠的线程:
boolean enqueueMessage(Message msg, long when) {
...
if (needWake) {
nativeWake(mPtr); //用往管道写值去唤醒休眠的线程
}
...
}
2.Android 6.0 及之后版本,采用eventfd机制。相比于管道,只需要产生一个fd。
Handler是跨线程通信,为什么用到管道这种跨进程通信机制?
以下是个人理解:管道主要工作是用于通知另一个线程的。Handler机制一个特点就是在消息队列没有消息时线程休眠,当消息队列不为空唤醒休眠线程,是典型的生产者/消费者问题。Linux中有很多方案可以处理这类问题,pipe机制相对而言比较轻量级,所以被采用。
网友评论