Handler的作用
Handler的核心作用用来解决多线程通信的问题
核心成员
成员 | 作用 |
---|---|
Handler | XX |
Message | XX |
MessageQueue | XX |
Looper | XX |
场景模拟
假设有2个线程thread1和thread2,我们需要实现线程通信,大致代码如下
threadl = new Thread("thread1") {
@Override
public void run() {
Log.d(TAG, "threadl run 创建handler1 ");
handler1 = new Thread1Handler(JustHandlerAct.this);
}
};
thread2 = new Thread("thread2") {
@Override
public void run() {
Log.d(TAG, "thread2 run 延时2秒发消息给thread1的handler");
try {
Thread.sleep(2000);
Message msg = Message.obtain();
msg.what = 111;
handler1.sendMessage(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
threadl.start();
thread2.start();
我们启动2个线程,thread1负责创建handler1,等待thread2发送消息过来
启动后报错:
This is not main thread, and the caller should invoke Looper.prepare() and Looper.loop()called byandroid.os.Handler.<init>:122
错误提示thread1不是主线程,使用Handler需要调用Looper.prepare() 和 Looper.loop() 方法
在thread1中修改代码如下:
threadl = new Thread("thread1") {
@Override
public void run() {
Log.d(TAG, "threadl run 创建handler1 ");
Looper.prepare();
handler1 = new Thread1Handler(JustHandlerAct.this);
Looper.loop();
}
};
通过日志可以看到,正常收到了message
2020-11-23 20:31:05.246 23347-23413/com.mj.just D/JustHandlerAct: threadl run 创建handler1
2020-11-23 20:31:05.246 23347-23414/com.mj.just D/JustHandlerAct: thread2 run 延时2秒发消息给thread1的handler
2020-11-23 20:31:05.246 23347-23347/com.mj.just D/ActivityThread: add activity client record, r= ActivityRecord{848d728 token=android.os.BinderProxy@293d50d {com.mj.just/com.mj.just.handler.JustHandlerAct}} token= android.os.BinderProxy@293d50d
2020-11-23 20:31:07.247 23347-23413/com.mj.just D/JustHandlerAct: mThread1Handler handleMessage what: 111, curThread: thread1
那么Looper.prepare()和Looper.loop()做了什么事呢?我们在主线程创建的handler为什么可以不用调用这两个方法?
主线程创建handler为什么可以不调用?
答案不是不用调用,而是Android系统帮我们做了这一步而已。
APP在启动的时候,会调用ActivityThread类的main方法,我们通过源码可以看到系统帮我们做了这个事
public final class ActivityThread extends ClientTransactionHandler {
public static void main(String[] args) {
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
}
}
接下来从创建和通信两个维度来说一下handler的跨线程通信机制
创建
1. handler的创建
首先看一下创建一个handler对象的时候,系统做了哪些事
public class Handler {
public Handler(@Nullable Callback callback, boolean async) {
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;
}
}
可以看到,handler在构造函数中从Looper类中获取了mLooper对象并持有,看一下Looper是如何提供的
public class Looper{
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
}
这里用了ThreadLocal来为提供每个线程的Looper获取方式,不同线程之间的Looper相互独立,那么Looper又是如何产生的呢?其实就是Looper.prepare()方法
public class Looper{
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));
}
}
我们看到,prepare方法其实就是给每个线程设置唯一的一个Looper对象
从生产过程来看,大致可以有这么几点总结:
- 一个线程可以有多个handler,这个没有限制
- 一个线程只有一个Looper,如果一个线程中创建了多个handler,那么这些handler其实持有的是同一个Looper,线程和Looper的应对关系是靠ThreadLocal来实现的
通信过程
接下来看一下通信过程,我们一般通过handler.sendMessage()方法将消息发送给另一个线程。
handler.sendMessage()方法最终会走到enqueueMessage方法:
public class Handler {
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);
}
}
这里核心做了2件事:
1. 将handler自身放进入message的target属性中
2. 将msg交给了MessageQueue处理
接下来看一下MessageQueue是如何处理的:
public final class MessageQueue {
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
...
msg.markInUse();
msg.when = when;
Message p = mMessages; //当前表头msg
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 如果当前表中没有积压msg 或者是 立即执行的msg 或者是 时间已过时的msg
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);
}
}
}
}
网友评论