原文: Medium.com
Handler 是 Android 线程间传递消息的入口。consumer 和 producer 线程都通过与 Handler 的交互执行以下操作:
- 从 MessageQueue 中创建( creating ), 插入( inserting ), 移除( removing ) Message.
- 处理 consumer 线程上的 Message.
Handler
+ handleMessage
+ send*
+ post*
+ remove*
- mLooper
- mMessageQueue
每个 Handler 都与一个 Looper 和 MessageQueue 相关联,有两种方式创建 Handler:
- 通过默认的构造方法,使用与当前线程相关联的 Looper
- 通过明确指定使用哪个 Looper
没有 Looper 的话 Handler 无法工作,因为它不能将 message 放入到 MessageQueue 中,同样也无法接收处理 Message.
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
上面的代码简单地演示了创建一个新的 Handler 的逻辑。 Handler 检查当前线程是否有有效的 Looper,如果没有,抛出异常;如果有的话, Handler 接收 Looper 保存的 MessageQueue 的引用。
Note:与多个 Handler 相关联的同一 Thread 共用相同的 MessageQueue,因为它们共用相同的 Looper。
Callback 是可选参数。如果提供,它将会处理 Looper 调度的 messages.
Message
Message 可以作为任意数据的的容器。Handler 在 producer 线程发送 Message,然后被加入到 MessageQueue 队列。Message 提供额外的三个信息,可以被 Handler 和 MessageQueue 用来处理 message:
- what —— Handler 可以用这个标识符来区分不同的 Message.
- time —— 通知 MessageQueue 何时去处理 Message.
- target —— 表示使用哪个 Handler 去处理 Message.
Message
+ what
+ target handler [Handler]
+ callback [Runnable]
+ data [Bundle]
+ obj / arg1 / arg2
+ time (default, at_front, delay, uptime)
Message 的创建通常使用如下 Handler 的方法:
public final Message obtainMessage()
public final Message obtainMessage(int what)
public final Message obtainMessage(int what, Object obj)
public final Message obtainMessage(int what, int arg1, int arg2)
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
Message 是从 Message Pool 中获取,Handler 提供的 obtain 方法参数填充了 Message 的值域,同时将 Message 的 target 设置为自身。我们可以这样写方法链:
mHandler.obtainMessage(MSG_SHOW_IMAGE, mBitmap).sendToTarget();
Message pool 是一个 Message 对象的链表,最大数量为 50。当 Handler 处理完 Message 之后,MessageQueue 将 Message 对象返回到 pool 中并重置所有值域。
当通过 post(Runnable r)
方法向 Handler 传递一个 Runnable 对象之后,Handler 立即构建一个新的 Message 对象。同样设置 callback 值域来保存 Runnable。
Message m = Message.obtain();
m.callback = r;
Producer 线程创建一条 Message 并将它发送至 Handler. Handler 然后将 Message 加入到 MessageQueue. Handler 将在某个时间在 consumer 线程上处理 Message。
Message Queue
MessageQueue 是一个 Message 对象的链表。它按时间顺序插入 Message 对象,数值最低的时间戳将被最先调度( Lowest timestamp dispatch first )。
MessageQueue 还维护着一个与系统当前时间( SystemClock.uptimeMillis )相关的 dispatch barrier。当 Message 的时间戳低于这个数值,这个 Message 将被 Handler 调度和处理。
Handler 提供三种发送 Message 的方法:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
public final boolean sendMessageAtFrontOfQueue(Message msg)
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
发送有延迟时间( delayMillis )的 Message 表示该 Message 的 time 值域被设置为 SystemClock.uptimeMillis() + delayMillis。然而 MessageQueue 中位于链表前段的 Message time 值域会被设置为 0,并在下一个 Message 循环中处理。所以使用这种方法时需要注意,它可能会 starve(饿死) MessageQueue 或者有其它的副作用。
Handler 一般与 UI 组件相关联,通常是 Activity 组件。Handler 中的 Activity 引用可能会有潜在泄露。考虑下面的场景:
public class MyActivity extends AppCompatActivity {
private static final String IMAGE_URL = "https://www.android.com/static/img/android.png";
private static final int MSG_SHOW_PROGRESS = 1;
private static final int MSG_SHOW_IMAGE = 2;
private ProgressBar mProgressBar;
private ImageView mImageView;
private Handler mHandler;
class ImageFetcher implements Runnable {
final String imageUrl;
public ImageFetcher(String imageUrl) {
this.imageUrl = imageUrl;
}
@Override public void run() {
mHandler.obtainMessage(MSG_SHOW_PROGRESS).sendToTarget();
InputStream is = null;
try {
URL url = new URL(imageUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setDoInput(true);
connection.connect();
is = connection.getInputStream();
final Bitmap bitmap = BitmapFactory.decodeStream(is);
mHandler.obtainMessage(MSG_SHOW_IMAGE, bitmap).sendToTarget();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
assert is != null;
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class UIHandler extends Handler {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SHOW_PROGRESS:
mImageView.setVisibility(View.GONE);
mProgressBar.setVisibility(View.VISIBLE);
break;
case MSG_SHOW_IMAGE:
mProgressBar.setVisibility(View.GONE);
mImageView.setVisibility(View.VISIBLE);
mImageView.setImageBitmap((Bitmap) msg.obj);
break;
}
}
}
@Override protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
mImageView = (ImageView) findViewById(R.id.image);
mHandler = new UIHandler();
Thread thread = new Thread(new ImageFetcher(IMAGE_URL));
thread.start();
}
}
上例中 Activity 开启一个新的工作线程下载 Image 并展示在 ImageView 中。工作线程通过 UIHandler 和 UI 交互,保留对 Views 的引用并更新其状态。
假设在较慢的网络环境下,工作线程需要花费较长时间下载图片。在工作线程完成任务之前销毁 Activity 导致 Activity 泄露。在本例中有两个强引用,一是工作线程和 UIHandler 之间,另一个是 UIHandler 和 Views 之间。这些阻止 GC 收回 Activity 的引用。再来看另外一个例子:
public class MyActivity extends AppCompatActivity {
private static final String TAG = "Ping";
private Handler mHandler;
class PingHandler extends Handler {
@Override public void handleMessage(Message msg) {
Log.d(TAG, "Ping message received!");
}
}
@Override protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new PingHandler();
final Message msg = mHandler.obtainMessage();
mHandler.sendEmptyMessageDelayed(0, TimeUnit.MINUTES.toMillis(1));
}
}
这个例子中,依次发生了以下事件:
- 一个 PingHandler 被创建
- Activity 的 Handler 发送了一个延时的 Message,被加入 MessageQueue 中。
- Activity 在 Message 发生调度前销毁
- PingHandler 收到 Message 并打印出信息。
虽然没有下载网络图片的例子明显,但这里 Activity 依然发生了泄漏。
在 Activity 销毁之后,Handler 的引用需要被回收。然而,当创建一个 Message 对象之后,它持有一个 Handler 的引用:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
上面的源码片段表明,所有的 Message 传递到 Handler 后,最终会调用 enqueueMessage(...)
方法。注意 Handler 的引用显式地赋给了 msg.target
,这告知 Looper 从 MessageQueue 收回 Message 时,哪个 Handler 负责处理这个 Message。
这条 Message 被添加至 MessageQueue,它持有 Handler 的引用。MessageQueue 同样有一个与之相关联的 Looper。一个普通的 Looper 生命周期会持续到它的 terminated,而 Main Looper 的生命周期将会和 app 一样长。 Handler 的引用将会持续到 MessageQueue 回收这条 Message 之前。一旦被回收,它的值域,包括 target 引用,都被清除。
虽然 Handler 有一条 long living 引用,但并不是很清晰 Activity 是否发生泄漏。为了检查泄露问题,我们还需要确定 Handler 是否持有 Activity 的引用。在这个例子中,是有这种问题的。通常一个非静态内部类成员都会隐式的保留外部类的引用。这个例子中,内部类 PingHandler 是非静态类,因此它隐式地持有 Activity 的引用。
使用 WeakReference 和 static 类标识符相结合可以阻止 Handler 泄露 Activity。当 Activity 被销毁后,WeakReference 允许 GC 回收要保留的对象,static 类标识符可以防止内部类 Handler 隐式持有外部类 Activity 的引用。
修改后的 UIHandler 类:
static class UIHandler extends Handler {
private final WeakReference<Activity> mActivityRef;
public UIHandler(Activity activity) {
mActivityRef = new WeakReference<>(activity);
}
@Override public void handleMessage(Message msg) {
final Activity activity = mActivityRef.get();
if (activity == null) {
return;
}
switch (msg.what) {
case MSG_SHOW_LOADER:
activity.mProgressBar.setVisibility(View.VISIBLE);
break;
case MSG_HIDE_LOADER:
activity.mProgressBar.setVisibility(View.GONE);
break;
case MSG_SHOW_IMAGE:
activity.mProgressBar.setVisibility(View.GONE);
activity.mImageView.setImageBitmap((Bitmap) msg.obj);
break;
}
}
}
现在 UIHandler 构造方法接收 Activity,被包裹在 WeakReference 中。这允许垃圾回收器在 activity 被销毁时回收 activity 的引用,为了与 Activity 的 UI 组件进行交互,我们需要来自 mActivityRef.get()
的强引用 Activity。由于使用的是 WeakReference,在访问 Activity 时必须小心谨慎。如果获取
Activity 引用的唯一路径是通过 WeakReference,那么它可能已经被回收。我们需要多一些检查步骤。
static class UIHandler extends Handler {
private final WeakReference<Activity> mWeakRef;
public UIHandler(Activity activity) {
mWeakRef = new WeakReference<>(activity);
}
@Override public void handleMessage(Message msg) {
final Activity activity = mWeakRef.get();
if (activity == null || activity.isFinishing() || activity.isDestroyed()) {
removeCallbacksAndMessages(null);
return;
}
switch (msg.what) {
case MSG_SHOW_LOADER:
activity.mProgressBar.setVisibility(View.VISIBLE);
break;
case MSG_HIDE_LOADER:
activity.mProgressBar.setVisibility(View.GONE);
break;
case MSG_SHOW_IMAGE:
activity.mProgressBar.setVisibility(View.GONE);
activity.mImageView.setImageBitmap((Bitmap) msg.obj);
break;
}
}
}
我们可以概括 MessageQueue, Handler, Producer 线程之间的交互:
上图中多条 producer 线程发送 Messages 到不同的 Handler。然而每条 Handler 公用相同的 Looper,因此所有的 Message 被送往相同的 MessageQueue 中。这很重要,因为 Android 创建了多条与 Main Looper 相关的 Handler:
- Choreographer :处理 vsync 和 frame 的更新。
- ViewRoot:处理输入和窗口事件,系统配置更新等。
- InputMethodManager:处理软键盘触摸事件。
- 其他部分。
Tip:确保 producer 线程不会产生多条 Messages,否则它们会 starve 系统生产的 Messages.
Looper
Looper 从 MessageQueue 读取 Message 并执行调度到目标 Handler。一旦 Message 被传递到 dispatch barrier,Looper 可以在下一个消息循环中读取它。当没有 Message 被调度时,Looper 发生阻塞。在 Message 调度时恢复。
只有一个 Looper 可以与线程相关联,将另一个 Looper 附加到一个线程会导致 RuntimeException。Looper 类中的静态 ThreadLocal 对象确保只有一个 Looper 附加到线程上。
调用 Looper.quit
会立即终止 Looper,同时它会忽略 所有
MessageQueue 中传递到 dispatch barrier 的 Message。调用 Looper.quitSafely
会确保所有被调度( dispatch ) 的 Message 得到执行。
Looper 会在 Thread 的 run()
方法中被设置。调用静态方法 Looper.prapare()
检查 Thread 上是否有预先设定的 Looper,Looper 内部会通过静态的 ThreadLocal 对象检查是否 Looper 对象已存在。
注意:public 的 Looper.prepare
内部调用 prepare(true)
方法。
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));
}
此时 Handler 可以接收 Messages 并将它们添加至 MessageQueue。执行 Looper.loop()
将开始读取 Message 并出列。每次 loop 循环取出下一个 Message,调度至对应的 Handler,然后将其回收至 Message pool。 Looper.loop()
将会持续这种进程知道 Looper 被中止。源码片段如下:
public static void loop() {
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
for(;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting
return;
}
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}
}
没有必要创建一个有 Looper 依附的线程。Android 提供了一个便捷的类—— HandlerThread. 它继承自 Thread 类并管理创建 Looper。下面的 snippet 描述了一个典型的使用场景:
private final Handler mHandler;
private final HandlerThread mHandlerThread;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate();
mHandlerThread = new HandlerThread("HandlerThreadDemo");
mHandlerThread.start();
mHandler = new UIHandler(mHandlerThread.getLooper());
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandlerThread.quit();
}
onCreate(...)
方法中构造了新的 HandlerThread 对象。当 HandlerThread.start()
时,它的内部会创建一个有 Looper 依附的线,然后 Looper 开始在 HandlerThread 上处理 MessageQueue 的消息。
Note:在Activity 被销毁时,有必要终止 HandlerThread,从而终止 Looper.
总结
Android 的 Handler 在 App 的生命周期中起着不可或缺的作用,它为 Half-Sync/Half-Async 架构模型奠定了基础。各种内部和外部资源依赖 Handler 进行异步事件调度,因为它使开销最小化并维护线程的安全性。
更加深入的了解组件的工作原理能帮助我们解决一些难题,它还允许我们能最佳地使用组件 API。我们通常使用 Handler 作为工作线程与 UI 线程通信的机制,事实不仅于此。Handler 还出现在 IntentService 和 Camera2 API中。在这些 API 中,Handler 更广泛地被用于任意线程间的通信。
我们可以通过更深入地了解 Handler 来构建更高效,简单和强大的 App。