Handler 介绍
定义
一套 Android 消息传递机制 / 异步通信机制
背景
- 在 Android 开发中,UI 线程 (主线程) 负责处理用户界面的交互和更新。
- 如果 UI 线程被长时间阻塞或执行耗时操作,将会导致用户界面的响应性降低,用户体验很差。
- 如果在主线程进行大量耗时操作,没有响应用户输入事件 (如点击、滑动等) 时,Android 系统会认为应用程序无响应,并弹出 ANR (Application Not Responding) 对话框提醒用户。这个时间阈值通常为 5 秒。
问题
- UI 线程 (主线程) 不能进行大量耗时的工作,需要使用子线程 (工作线程) 处理。
- 如果工作线程直接尝试更新 UI 组件,就会违反 Android 的线程安全规定,可能会导致应用程序崩溃或界面异常。
解决方案
使用 Handler 机制进行线程间通信:
- 启动一个子线程 (工作线程) 进行耗时的任务处理,可以向 UI 线程的 Handler 发送消息。
- UI 线程的 Handler 收到消息后,在主线程中更新 UI 组件。
这样可以保证 UI 操作在主线程中执行,从而避免线程安全问题。
和真实世界类比
下图说明了子线程进行耗时操作,并且更新主线程的流程。
流程 | Handler 类流程 | 真实世界类比 |
---|---|---|
1 | 创建 Looper 对象并启动 | 创建一个工厂车间的传送带系统 |
2 | 创建 MessageQueue 对象 | 准备用于存放货物的货物槽链 |
3 | 创建 Handler 对象并和 Looper 对象关联 | 为传送带指派一名专门的处理人员 |
4 | 在工作线程中执行耗时操作 | 在另一个车间进行耗时的生产加工 |
5 | 将需要通知主线程的更新信息封装在 Message 对象中 | 将需要通知处理人员的货物信息打包到货物槽 |
6 | 将 Message 对象添加到 MessageQueue 中 | 将打包好的货物放入货物槽 |
7 | Looper 不断循环读取 MessageQueue 中的 Message | 传送带不断运转,读取其中的货物槽的信息 |
8 | Looper 取出 Message 对象,并分发给关联的 Handler 处理 | 处理人员取出货物槽,交给对应的处理人员进行处理 |
9 | Handler 的 handleMessage() 方法中更新 UI 组件 | 处理人员根据货物槽内的信息,执行相应的处理操作 |
Handler / Looper / Thread 的关系
- 一个线程最多只有一个 Looper 对象,一个 Looper 对象只和一个线程关联
- 主线程创建 Looper:Android 主线程都是 Activity,在 ActivityThread 内会自动创建
- 子线程创建 Looper:
- 继承于 Thread,在
run()
里面调用Looper.prepare() 和 Looper.loop()
- 继承于 HandlerThread,自动创建
- 继承于 Thread,在
- 一个 Looper 对象中必须有且仅有一个 MessageQueue 对象
- 一个 Looper 对象可以关联 0 到多个 Handler 对象
Handler 源码分析
Looper 对象创建 & MessageQueue 对象创建
创建 Looper 时会同步创建 MessageQueue 对象
主线程 Looper 的创建
-
ActivityThread.main() 调用 Looper.prepareMainLooper() 和 Looper.loop()
/**
* 源码分析:Looper.prepareMainLooper()
* 作用:为主线程(UI 线程)创建 1 个循环器对象(Looper),同时也生成了 1 个消息队列对象(MessageQueue)
* 注:该方法在主线程(UI 线程)创建时自动调用,即 主线程的 Looper 对象自动生成,不需手动生成
*/
// 在Android应用进程启动时,会默认创建1个主线程(ActivityThread,也叫 UI 线程)
// 创建时,会自动调用 ActivityThread 的1个静态的 main() 方法 = 应用程序的入口
// main() 内则会调用 Looper.prepareMainLooper() 为主线程生成 1 个 Looper 对象
public static void main(String[] args) {
...
// 1. 为主线程创建 1 个 Looper 对象,同时生成 1 个消息队列对象(MessageQueue)
// 方法逻辑类似 Looper.prepare()
// 注:prepare():为子线程中创建 1 个 Looper 对象
Looper.prepareMainLooper();
...
// 2. 创建主线程
ActivityThread thread = new ActivityThread();
...
// 3. 自动开启消息循环
Looper.loop();
} -
Looper.prepareMainLooper():在主线程中创建 1 个 Looper 对象 (自动生成)
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. See also: {@link #prepare()}
*
* @deprecated The main looper for your application is created by the Android environment,
* so you should never need to call this function yourself.
*/
@Deprecated
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}- Looper.myLooper()
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}- sThreadLocal
// sThreadLocal.get() will return null unless you've called prepare().
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal= new ThreadLocal<Looper>();
- sThreadLocal
- Looper.myLooper()
子线程 Looper 的创建
-
Looper.prepare():在子线程中创建 1 个 Looper 对象 (手动创建)
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
// 1. 判断 sThreadLocal 是否为 null,否则抛出异常
// 即 Looper.prepare() 方法不能被调用两次, 1 个线程中只能对应 1 个 Looper 实例
// 注:sThreadLocal 为 ThreadLocal 对象,用于存储线程的变量
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}- Looper.Looper()
private Looper(boolean quitAllowed) {
// 创建 1 个 Looper 实例时,会自动创建 1 个与之配对的 MessageQueue
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
- Looper.Looper()
Handler 对象创建
-
Handler 对象创建
private Handler mHandler = new Handler(Looper.myLooper()){
};-
Handler() 创建时,关联 Looper 对象和 Queue 对象。
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}-
Handler()
final Looper mLooper;
final MessageQueue mQueue;
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
-
-
Message 发送
-
Handler.sendMessageAtTime(Message msg,long uptimeMillis):调用 enqueueMessage()
/**
* 分析:sendMessageAtTime(Message msg, long uptimeMillis)
* 作用:在指定时间发出消息,消息会加入到消息队列 MessageQueue
*/
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* Time spent in deep sleep will add an additional delay to execution.
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
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);
}-
Handler.enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
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.enqueueMessage(Message msg,long when):将 Message 对象加入消息队列
/**
* 分析:MessageQueue.enqueueMessage()
* 作用:入队消息,即将新的消息加入消息队列
*/
// mMessages 为链表结构,头部为优先执行
// 即优 先执行 mMessages,执行完之后 mMessages = mMessages.next,直到 mMessages 为空为止
Message mMessages;
...
boolean enqueueMessage(Message msg, long when) {
// msg.target 即目标 Handler,如果为 null 则抛出异常
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
// 可能出现存在多个线程同时插入消息多情况,用 synchronized 同步
synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
...
// msg:当前(需要加入消息队列的)消息
msg.markInUse();
msg.when = when;
// p (mMessages):指针,指向当前队列的头部
Message p = mMessages;
boolean needWake;
// 如果以下出现情况之一:当前队列为空;当前 msg 需要马上执行;当前 msg 需要执行的时间 < 头部消息需要执行的时间,则将当前消息插入队列头部;反止插入队列头部之后(中间或尾部)。
if (p == null || when == 0 || when < p.when) {
// 将当前消息插入队列头部
// New head, wake up the event queue if blocked.
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);
}
}
return true;
}
Message 分发和 Handler 处理
-
Looper.loop():取出 Looper 中的消息队列,使用 MessageQueue.next() 无限循环取出 msg 并发送到对应的 handler
/**
* 作用:消息循环,即从消息队列中获取消息、 分发消息到 Handler
* 特别注意:
* a. 主线程的消息循环不允许退出,即无限循环
* b. 子线程的消息循环允许退出:调用消息队列 MessageQueue 的 quit()
*/
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
// 1. 获取当前Looper的消息队列
// myLooper()作用:返回 sThreadLocal 存储的 Looper 实例;若为 null 则抛出异常
final Looper me = myLooper();
// 若为 null 则抛出异常,即 loop()执行前必须执行 prepare() 创建 Looper 实例
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
...
me.mInLoop = true;
// 获取Looper实例中的消息队列对象 (MessageQueue)
final MessageQueue queue = me.mQueue;
// 2. 消息循环(for 无限循环)
for (;;) {
// 2.1 从消息队列取出消息 msg,若取出消息为空则线程阻塞
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
// 2.2 把消息 msg 派发给消息对象 msg.target(实际是 handler 对象)
msg.target.dispatchMessage(msg);
...
} ...
...
// 3. 释放资源
msg.recycleUnchecked();
}
} -
MessageQueue.next():从消息队列中取出下一个 Message 对象
/**
* 分析:MessageQueue.next()
* 作用:出队消息,即从消息队列中移出头部消息
*/
// mMessages 为链表结构,头部为优先执行
// 即优先执行 mMessages,执行完之后 mMessages = mMessages.next,直到 mMessages 为空为止
Message mMessages;
...
Message next() {
// 该参数用于确定消息队列中是否还有消息
// 从而决定消息队列应处于出队消息状态 or 等待状态
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// nativePollOnce 方法在 native 层,若是 nextPollTimeoutMillis 为 -1,此时消息队列处于等待状态
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
// 从消息队列中取出头部消息对象作为当前的 msg
Message msg = mMessages;
if (msg != null) {
// 如果当前 msg 时间大于当前时间,重新计算 nextPollTimeoutMillis 并设置超时唤醒
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 如果当前 msg 时间等于或者小于当前时间,取出当前 msg
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// 若消息队列中已无消息,则将 nextPollTimeoutMillis 参数设为 -1
// 下次循环时,消息队列则处于等待状态
nextPollTimeoutMillis = -1;
}
...
}
...
}
} -
Handler.dispatchMessage():将消息分发给接收者/回调方,由接收者/回调方进行相应处理
/**
* 分析:dispatchMessage(Message msg)
* 作用:派发消息到对应的 Handler 实例,根据传入的 msg 作出对应的操作
*/
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
// 若 msg.callback 属性不为空,则代表使用了 post(Runnable r) 发送消息
if (msg.callback != null) {
handleCallback(msg);
} else {
// 若 mCallback 不为空,则代表使用了 Handler(Looper looper, Callback callback) 传入了 callback 参数;之后调用 callback 自己实现的 handleMessage() 方法(必须进行 override 重写,返回 true 或 false)
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 若msg.callback属性为空,则代表使用了 sendMessage(Message msg)发送消息;之后调用 handleMessage(msg)(一般都会进行 override 重写)
handleMessage(msg);
}
}