子线程完成耗时操作的过程中,通过 Handler
向主线程发送消息 Message
,用来更新 UI 界面。因为 Android 是在主线程中更新 UI 的,在主线程出现耗时操作时,就会导致用户界面卡顿,所以我们一般都把耗时的操作(网络请求、IO 等)放到子线程中,然后通过 Handler
的方式让主线程更新 UI。
如果是无参构造器,其中调用了重载的构造方法并分别传入 null
与 false
。并在构造方法中给两个全局变量赋值,两者都是通过 Looper
来获取。
@UnsupportedAppUsage
final Looper mLooper;
final MessageQueue mQueue;
@UnsupportedAppUsage
final Callback mCallback;
final boolean mAsynchronous;
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
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;
}
启动一个 Java 的入口函数是 main
方法,当 main
函数执行完毕后程序就会停止运行,但是我们打开一个 Activity
,只要不返回则会一直显示,即 Activity
所在进程会一直处于运行状态。
实际上 Looper
内部维护一个无限循环,保证 APP 的持续运行。
Activity
启动时,ActivityThread#main
方法是新 APP 进程的入口
// ActivityThread#main
public static void main(String[] args) {
//...
// 初始化当前进程的 Looper 对象
Looper.prepareMainLooper();
//...
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
//...
// 调用 Looper#loop 方法开启无限循环
Looper.loop();
//...
}
// Looper#prepareMainLooper
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
// 将从线程本地变量中取出的 Looper 对象赋值给 sMainLooper 对象
sMainLooper = myLooper();
}
}
// Looper#prepare
private static void prepare(boolean quitAllowed) {
// 一个线程中的 Looper#prepare 方法只能执行一次 否则会抛出异常
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 新建一个 Looper 对象 并设置到线程本地变量中
// 即创建的 Looper 与当前线程绑定
sThreadLocal.set(new Looper(quitAllowed));
}
// Looper#Looper 构造方法
private Looper(boolean quitAllowed) {
// 初始化了 MessageQueue 对象
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
// Looper#myLooper
public static @Nullable Looper myLooper() {
// 从线程本地变量中取出 Looper 对象
return sThreadLocal.get();
}
Looper#prepare
方法一个线程中只能被调用一次(会判定线程本地变量是否为空),即 Looper
的构造方法在一个线程中只能被调用一次,构造方法中 MessageQueue
在一个线程中只会被初始化一次,所以一个线程只会有一个 MessageQueue
对象。
Looper
的任务就是不断从 MessageQueue
中取出 Message
,然后处理 Message
中指定的任务。在 main
方法中调用的 Looper.loop
方法就是完成这件事的。Looper#loop
方法中有个死循环,这就是 Android App 进程能不断运行的原因。
如果从 MessageQueue
中取出的 message
不为空,则取出 message
的 target
对象并调用其 dispatchMessage
方法处理 Message
方法本身。这个 target
对象就是 Handler
。
// Looper#loop
public static void loop() {
final Looper me = myLooper();
//...
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
// Looper#loopOnce
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // might block
if (msg == null) {
return false;
}
//...
try {
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
//...
} finally {
//...
}
//...
return true;
}
// Message
public final class Message implements Parcelable {
//...
// target 即传递的 Handler 对象
@UnsupportedAppUsage
/*package*/ Handler target;
}
// Handler#dispatchMessage
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 调用 handlerMessage 方法
handleMessage(msg);
}
}
// Handler#handlerMessage
public void handleMessage(@NonNull Message msg) {
// 该方法为空
// 在创建 Handler 时需要继承并重写该方法
}
Handler
包括几个重载的 sendMessage
方法,该方法最后会通过 enqueueMessage
方法将 Message
插入到消息队列 MessageQueue
中。这个消息队列就是我们在 ActivityThread
的 main
方法中通过 Looper
创建的 MessageQueue
。
enqueueMessage
方法中,将 Handler
自身设置为 target
对象,后续 Message
会调用此 Handler
的 dispatchMessage
来处理。MessageQueue
是一个按照 Message
执行时间排序的有序队列,在使用 enqueueMessage
方法时会根据 Message
的时间 when
来有序插入 Message
到队列中。
在 Looper#loop
从 MessageQueue
中取出 Message
时,会调用 target
的 dispatchMessage
来处理消息。
如果 msg.callback
不为空,执行的是 handleCallback(msg)
去处理消息,如果 msg.callback
为空,会调用 handleMessage(msg)
来处理消息(即我们重写的方法)。
private static void handleCallback(Message message) {
message.callback.run();
}
handleCallback
会直接执行 Runnable
的 run
方法,Runnable
实际上是一个回调接口,与线程 Thread
无关。
Looper#loop
方法实际上是一个死循环,但是不会造成 UI 线程阻塞。因为在 MessageQueue
的 next
方法中调用了 native
方法 nativePollOnce
,当调用该方法时主线程会释放 CPU 资源进入休眠状态,直到下一条消息到达或者有事务发生,通过 pipe
管道写段写入数据唤醒主线程工作,采用 epoll
机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。
Handler
的 Message
被存储在 MessageQueue
中,有些 Message
不能马上被处理,在队列中存在的时间很长(排序靠后或者有使用了延时执行 sendMessageDelay
来发送 msg
,导致 Handler
无法被回收,如果 Handler
是非静态的,那么 Handler
也会导致引用它的 Activity
与 Service
不能被回收。
可能场景是:发送一条延时 msg
至 MessageQueue
中,未执行就 finish
关闭 Activity
。导致 handler
与 Activity
内存都不会被释放。
解决方式是使用一个静态的 Handler
内部类继承 handler
,且 Handler
持有的对象使用弱引用,并且在 Activity
的 onDestroy
方法中移除传入到 MessageQueue
中的消息。
Looper
,可以有多个 Handler
来处理消息,Looper
通过 Handler
对象的 handlerMessage
方法来处理消息。Handler
的话需要在处理逻辑的线程中创建 Looper
,有了 Looper
后才能创建 Handler
,之后再另外的线程中使用 Looper
线程的 handler
对象来发送 message
。Looper
方法是:Looper.getMainprepare()
,可以在子线程直接 new 一个 Handler
,需要在一个线程先 Looper.prepare()
和 Looper.loop()
Handler
在创建时会采用当前线程的 Looper
来构造消息循环系统, Looper
在哪个线程创建,就和哪个线程绑定,Handler
是在其关联的 Looper
对应的线程中处理消息的。通过 class
判断。
首先需要在主线程当中创建一个 Handler
对象,并重写 handleMessage()
方法。
然后当子线程中需要进行UI操作时,就创建一个 Message
对象,并通过 Handler
将这条消息发送出去。
之后这条消息会被添加到 MessageQueue
的队列中等待被处理,而 Looper
则会一直尝试从 MessageQueue
中取出待处理消息,最后分发回 Handler
的 handleMessage()
方法中。
由于 Handler
的构造函数中我们传入了 Looper.getMainLooper()
,所以此时 handleMessage()
方法中的代码也会在主线程中运行,然后便可以在此更新 UI
。
一条Message经过以上流程的辗转调用后,也就从子线程进入了主线程,从不能更新UI变成了可以更新UI。
应用启动是从 ActivityThread#main
方法开始的,先执行了 Looper#prepare
方法,创建 Looper
对象并绑定到当前线程 MainThread
中,而 Looper
对象创建时会初始化 MessageQueue
队列,因此我们会在主线程中获得一个 Looper
对象与 MessageQueue
队列。
当我们创建一个 Handler
子对象时,在构造方法中通过 ThreadLocal
方式获取绑定的 Looper
对象,并获取此 Looper
对象的成员变量 MessageQueue
作为该 Handler
对象的成员变量。
在子线程中调用创建好的 Handler
子类对象的 sendMessage
方法,将 Message
对象的 target
属性设置为 Handler
子对象自身,调用 MessageQueue
对象的 enqueueMessage
方法将 msg
插入 MessageQueue
中。
在主线程中的 Looper#loop
方法中会不断读取 MessageQueue
中的消息,如果消息不为空,就会执行 msg#target#dispatchMessage
方法,这个方法会调用我们在创建 Handler
对象时重写的 handlerMessage
方法。