为什么存在 Handler
Handler 是 Android 的一种消息处理机制,与 Looper,MessageQueue 绑定。可用于不同线程的通信,常见于在子线程发送消息让主线程(UI 线程)更新控件。
Hanlder 的基本原理
在 Android 的多线程中,每个线程都有一个自己的消息队列,线程可以开启一个死循环不断地从队列中读取消息。
当 B 线程要和 A 线程通信时,只需要往 A 的消息队列中发送消息,A 的事件循环就会读取这一消息从而实现线程间通信
Handler 源码分析
默认整个应用程序,都是通过ActivityThread类启动的,在ActivityThread类当中,负责创建我们所有的Activity,并回调每个Activity中的生命周期方法。在ActivityThread类中,默认会去创建一个线程,这个线程叫做main线程(主线程)。所有的应用程序,更新ui的操作,都是在这个main线程中进行的。
#ActivityThread 类
public static void main(String[] args) {
...
//省略了其他 主要看与 Hanlder 有关的方法
Looper.prepareMainLooper();
...
Looper.loop();
}
#Looper 类
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
跟进去prepare方法 ⬇️
#Looper 类
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<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));
}
目前线程:main 线程(即 Android 中绘制 UI 的线程)
在当前方法中,会new 一个 Looper 实例对象存在 ThreadLocal 中。
我们接着看看 new Looper 发生了什么:
#Looper 类
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
我们看到,在创建Looper轮训器的时候,同时创建了消息队列MessageQuene并存于 Looper 实例对象的变量中。也就是说默认的情况下,android为我们自动创建了主线程的Looper和MessageQuene。
这些都是系统自动为我们做的,而在开发中要做的,是为线程实例化一个 Handler 对象:
##MainActivity 类
private Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
//自己开发中用到的逻辑
case xx:
break;
default:
break;
}
}
};
那么问题来了,Handler怎么和我们的Looper 、MessageQuene消息队列联系在一起的?因为不是说handler发出的消息是发送到消息队列中了吗?
借着问题,我们来看看 new Handler 发生了什么?
#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;
mAsynchronous = async;
}
在执行mLooper = Looper.myLooper()时:由于目前 new Handler 是在 main 线程中,从 Looper 的 sThreadLocal 中获取到的 Looper 实例,正是系统为我们自动创建的那个。至此,在 Handler 中的成员变量 mLooper,指向的就是系统创建的 Looper 了,所以两者到此关联了起来。
#Looper 类
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
插一张关系图:
好,到此为止,Handler 的准备工作已经完成了,下面研究一下子线程发送消息到main 线程,消息是如何处理的。
#MainActivity类
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run(){
Message message = new Message();
message.what = UPDATE;
handler.sendMessage(message);
}
}
).start();
}
如上面模拟了在 activity 中,开启子线程使用 handler 发送消息。最终会发现,执行到了sendMessageAtTime方法。
#Handler 类
public boolean sendMessageAtTime(@NonNull 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);
}
继续跟进enqueueMessage方法:(顾名思义就是将消息放到 handler 实例中的 MessageQueue 中。
#Hanlder 类
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);
}
MessageQueue实际上是一条链表,queue.enqueueMessage将消息 add 进链表中,等待Looper轮询。
前面提到,在 ActivityThread 中,prepare 完了之后会执行Looper.loop();这个方法就是 Looper 开启死循环执行轮询的方法。我们跟进看看:
#Looper 类
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
...
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
for 循环就是死循环轮询的具体执行:
#Looper类,省略了很多源码
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // might block
...
try {
//关键在这里
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
}
msg.target.dispatchMessage(msg);关键在这句代码中。
msg.targer会得到 handler 实例对象,再调用其dispatchMessage方法。
#Handler 类
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
一般情况下我们发送消息的时候没有给Message的callback赋值,所以第一个if条件不满足。下面的mCallback是在我们初始化Handler的时候才被初始化,Handler初始化有一种方法Handler(Callback callback),此处的参数就是给mCallback赋值的。我们一般初始化Handler的时候使用的是空参数的构造器,所以导致mCallback未被初始化,所以会直接走handleMessage(msg)方法,也就是我们初始化Handler时重写的handleMessage方法。到此为止,已经分析完了!
相关问题
- 在死循环的过程中,不会卡死主线程吗?
可参考# 为什么Looper中的Loop()方法不能导致主线程卡死?
- Looper是线程唯一的吗?
参考:# 一切从Android的Handler讲起(三):Looper的唯一性——ThreadLocal
从 Looper 的源码出发,一个线程创建 Looper是源于 Looper.prepare()方法:
# Looper类
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<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));
}
其他的就是 ThreadLocal 的原理啦!
- 一个线程能有几个 Handler?
无数个。
理论上只要你内存足够,你可以一直 new 。
一个线程可以有多个Handler, 但是只有一个Looper对象,只有一个MessageQueue对象。
所以需要用到 ThreadLocal 来保证一个线程只有一个 Looper 。
(在学习的时候一直有一个问题困扰着,就是为什么要使用 ThreadLocal 呢, 如果要保证一个线程只有一个 Looper,那么直接在线程的实例对象中存一个 looper 的成员变量不就好了吗?想要用到该线程的 looper 时,直接去获取线程中的成员变量就好了。这样也可以保证一个线程只有一个 Looper?) 有没有大神解答一下。