概要
Handler
封装了一套消息机制,用来发送和处理消息(Message
),而它常常被用来更新 UI。
创建
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(boolean async) {
this(null, async);
}
public Handler(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 that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler
构造函数最主要的目的就是获取 Looper
对象,要么在参数中提供 Looper
,要么就调用 Looper.myLooper()
获取
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
Looper.myLooper()
方法其实是从 ThreadLocal
对象中获取 Looper
对象,也就是说这个 Looper
是与当前线程有关的。 而且从 public Handler(Callback callback, boolean async) {}
构造函数中抛出的异常可以看出,获取到的 Looper
对象不能为空,否则报异常。
那么通常在 Activity
中创建的 Handler
方式如下
Handler handler = new Handler();
这个 handler
获取的就是 main thread
中的 Looper
对象,然而代码并没有报错,说明确实有这个 Looper
对象存在,而这个 Looper
对象是在应用创建的时候,在 ActivityThread
的 main()
方法中创建的。
public static void main(String[] args) {
Looper.prepareMainLooper();
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
其中 Looper.prepareMainLooper()
就是把 Looper
对象存储到与 main thread
相关的的 Looper.mThreadLocal
对象中的。
而有时候我们会在一个工作线程中创建 Handler
对象,例如
new Thread(){
@Override
public void run() {
Handler handler = new Handler();
}
}.start();
然而,这段代码就会报出刚刚说到的异常,Can't create handler inside thread that has not called Looper.prepare()
。 很明显是因为无法从 Looper.mThreadLocal
对象中获取与当前线程相关的 Looper
对象。而从异常信息中可以看出,需要调用 Looper.prepare()
来创建与线程相关的 Looper
对象,然而仅仅这样还不够,还需要调用 Looper.loop()
来轮询消息队列(MessageQueue
)来处理消息(Message
)。
那么正确的代码如下
new Thread(){
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
}
}.start();
而这段代码只是做到仅仅不报错而已,要在实际中使用还是有些小问题的,这个后面再说。
那么现在总结下,Handler
的构造函数在创建的时候会绑定一个 Looper
对象,如果没有提供 Looper
参数,就是从 Looper.mThreadLocal
中获取与当前线程相关的 Looper
对象。
发送消息
Handler
发送消息有两个方式,一种是 sendMessage()
之类的方法,一种是 post(Runnable)
之类的方法,其实这两种方法最终发送的都是 Message
对象。
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
从代码中可以看到 post(Runnable)
方法最终还是创建的 Message
对象,然后把参数 r
赋值给 message.callback
变量。
Message
Message
类实现了 Parcelable
接口,也就是说可以序列化,那么自然这个类的对象可以用来保存和传输数据。
public final class Message implements Parcelable {
public int what;
public int arg1;
public int arg2;
public Object obj;
Message next;
}
成员变量 what
用来辨认消息的类型。
成员变量 arg1
, arg2
, obj
是用来保存数据的,其实注意下 arg1
和 arg2
是 int
类型,而 obj
是 Object
,这样就可以保存任意的数据。
从成员变量 Message next
可以看出 Message
可以实现一个单链表,而实际也是如此,但是这个实现是由 MessageQueue
完成的。
处理消息
我们通常用 Handler
来更新UI,发送消息有两个方法,处理消息也有两种方式,首先看 post(Runnable)
之类的方法。
public class TestActivity extends AppCompatActivity {
private Handler mHandler = new Handler();
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_acrtivity);
mTextView = findViewById(R.id.id_tv);
new Thread(){
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
mTextView.setText("Update UI");
}
});
}
}.start();
}
}
代码中创建了一个线程,然后在线程中用更新 UI,使用了 Handler
的 post(Runnable)
方法,在 run()
方法中更新了 UI。
我们也可以用 Handler
的 sendMessage()
之类的方法
public class TestActivity extends AppCompatActivity {
private Handler mHandler;
private TextView mTextView;
private static final int MSG_UPDATE = 0x110;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_acrtivity);
mTextView = findViewById(R.id.id_tv);
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_UPDATE) {
mTextView.setText("Update OK");
}
}
};
new Thread() {
@Override
public void run() {
mHandler.sendEmptyMessage(MSG_UPDATE);
}
}.start();
}
}
用 mHandler.sendEmptyMessage(MSG_UPDATE)
发送了一个 Message
对象,而参数 MSG_UPDATE
就是定义 Message
对象的 what
的值。 而在 handleMessage()
方法中,我们识别了这个 what
,从而更新 UI。
处理消息原理
前面说过,发送消息有两个方式,而最终都是封装在 Message
对象中。 而这个 Message
对象,会发送给 Looper
对象的 MessageQueue
,而 MessageQueue
会让 Message
形成一个链表,也就是这里的队列,主要代码如下
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
msg.when = when;
Message p = mMessages;
boolean needWake;
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;
}
}
return true;
}
从实现可以看出,mMessages
是代表队列的头部,而插入的方式是从尾部插入的。
那么现在已经有了消息队列,如何取出消息呢? 这就是 Looper.loop()
方法做的事情,它会无限地循环从 MessageQueue
中取出消息,然后让 Handler
来处理
public static void loop() {
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;
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
}
Looper
的 Loop()
方法开启了一个无限循环,从 MessageQueue
中取出 Message
对象,然后交给了 Handler
的 dispatchMessage()
方法。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
内存泄露
与 Handler
相关有一个很著名的问题就是内存泄露问题,通常在 Android Studio
中会提示,其实在构造函数中也有踪迹可寻。
public Handler(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());
}
}
}
从这段代码可以看出,如果 Handler
对象的类不是静态,并且是匿名内部类或内部类或局部类的时候,就会发出警告可以造成内存泄露。
如下代码,Android Studio
就会提示这个警告信息
public class TestActivity extends AppCompatActivity {
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_acrtivity);
}
}
这个警告信息只是告诉你可能会发生内存泄露,但是并没有说一定会发生。那么什么时候会发生呢?
public class TestActivity extends AppCompatActivity {
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
}
};
private static final int MSG_UPDATE = 0x110;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_acrtivity);
mHandler.sendEmptyMessageDelayed(MSG_UPDATE, 1000 * 60);
finish();
}
}
当 Activity
被销毁的时候,例如屏幕旋转,这段代码就会造成内存泄露。原因如下
mHandler
是匿名内部类对象,而匿名内部类默认是会持有外部类的引用,也就是Activity
。Message
的target
变量指的就是与之相关的Handler
对象。- 当
finish()
被调用的时候,Activity
销毁,而Message
还没有被处理,message.target
持有了mHandler
对象,同时mHandler
持有了外部Activity
的引用,这样就导致了Activity
无法被回收,从而内存泄露。
通过分析原因,我们发现关键点在于,Handler
对象是持有了外部 Activity
引用造成的。那么内部类同样也会持有外部类 Activity
的引用,也可能会造成内存泄露,例如:
public class TestActivity extends AppCompatActivity {
private Handler mHandler = new LeakHandler();
private static final int MSG_UPDATE = 0x110;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_acrtivity);
mHandler.sendEmptyMessageDelayed(MSG_UPDATE, 1000 * 60);
finish();
}
private class LeakHandler extends Handler {
@Override
public void handleMessage(Message msg) {
}
}
}
解决内存泄露问题
既然知道了根本原因是因为 Handler
对象持有了外部类 Activity
的引用,那么就要断绝这层关系,有两个方式
- 使用静态的内部类继承
Handler
- 单独写一个类
以最常用的静态内部类举例
public class TestActivity extends AppCompatActivity {
private Handler mHandler = new LeakHandler();
private static final int MSG_UPDATE = 0x110;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_acrtivity);
mHandler.sendEmptyMessageDelayed(MSG_UPDATE, 1000 * 60);
finish();
}
private static class LeakHandler extends Handler {
@Override
public void handleMessage(Message msg) {
}
}
}
然而静态内部类并不能调用外部类的非静态方法,而如果我们想调用 Activity
的非静态方法咋办呢? 我们可以创建外部类 Activity
的弱引用
public class TestActivity extends AppCompatActivity {
private Handler mHandler ;
private static final int MSG_UPDATE = 0x110;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_acrtivity);
mHandler = new LeakHandler(this);
mHandler.sendEmptyMessageDelayed(MSG_UPDATE, 1000 * 60);
finish();
}
private void doSth() {
}
private static class LeakHandler extends Handler {
private WeakReference<AppCompatActivity> mActivity;
public LeakHandler(AppCompatActivity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
if (mActivity.get() != null) {
TestActivity activity = (TestActivity) mActivity.get();
activity.doSth();
}
}
}
}
LeakHandler
通过构造函数通过创建外部类 Activity
的 WeakReference
对象,从而在 Activity
销毁的时候,可以让 Activity
被回收,这样也就避免了内存泄露问题。
然而,这种方式看起来总感觉有那么点不顺畅,其实解决这个问题还可以从另外一个角度思考,既然是因为消息没有处理完导致的问题,那么我们可以在 Activity
销毁的时候移除这些消息即可。
public class TestActivity extends AppCompatActivity {
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
}
};
private static final int MSG_UPDATE = 0x110;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_acrtivity);
mHandler.sendEmptyMessageDelayed(MSG_UPDATE, 1000 * 60);
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
}
然而然而,这该死的 IDE 还是会发出警告,因为它并不知道我们正确的处理了呀,怎么办呢? 我们可以手动传入一个 Looper
对象。
private Handler mHandler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message msg) {
}
};
终于 IDE 没有提示这该死的警告了,但是这并不代表解决了内存泄露问题,还是需要调用 mHandler.removeCallbacksAndMessages(null);
。
优化
通常我们会发送一个消息,就会先创建一个消息,例如
Message msg = new Message();
msg.what = MSG_UPDATE;
mHandler.sendMessage(msg);
然而,这可能有点浪费资源,我们知道 MessageQueue
是维护了一个消息队列,那么复用 Message
对象就变得尤为重要,而 Message
类提供了一个方法可以用来获取复用的 Message
对象,这个方法是 obtain()
, 那么代码可以这样写
Message msg = Message.obtain();
msg.what = MSG_UPDATE;
mHandler.sendMessage(msg);
而 Handler
也提供了一种方法,来获取可复用的 Message
对象
public final Message obtainMessage()
{
return Message.obtain(this);
}
在 Message
中的实现如下
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
有点要注意的就是,它把从 Message.obtain()
中获取的 Message
对象的 target
指定了为此 Handler
对象。
那么我们代码也可以这样写
Message msg = mHandler.obtainMessage();
msg.what = MSG_UPDATE;
msg.sendToTarget();
由于 obtainMessage()
指定了 target
为 mHandler
,因此就可以调用 msg.sendToTarget()
方法来发送消息,其实也就是调用了 Handler
的 sendMessage()
方法。
HandlerThread
前面所说的都是在 main thread
中创建 Handler
,它会绑定 main thread
中的 Looper
对象,然后 Handler
会把消息发送给 Looper.messageQueue
,然后通过 Looper.loop()
来循环取出消息并处理。
有时候,我们想把消息在工作线程中处理,例如耗时的任务。
public class MyHandlerThread extends Thread {
private Handler mHandler;
@Override
public void run() {
Looper.prepare();
mHandler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message msg) {
}
};
Looper.loop();
}
public Handler getHandler() {
return mHandler;
}
}
在线程运行的时候,创建了 Looper
对象,并开启了轮循消息,并且创建了相应的 Handler
对象。 这样就可以通过 getHandler()
方法获取与线程相关的 Handler
,并发送消息,例如
public class TestActivity extends AppCompatActivity {
public static final int MSG_WORK = 0x110;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_acrtivity);
MyHandlerThread thread = new MyHandlerThread();
thread.start();
thread.getHandler().sendEmptyMessage(MSG_WORK);
}
}
然而运行的时候,十有八九会报空指针异常,why? 这是因为多线程并发问题,在 main thread
中要使用另外一个线程的东西,而这个东西此时还没有创建出来。
那么我们可以通过加锁解决这个问题,然而又有一个问题出现在面前,当创建这个线程的 Activity
销毁的时候,我们也需要手动停止这个线程,我们需要让 Looper
停止循环。然而这一切的一切,HandlerThread
类帮我们都解决了。
public class TestActivity extends AppCompatActivity {
public static final int MSG_WORK = 0x110;
public Handler mWorkerHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_acrtivity);
// 创建线程
HandlerThread handlerThread = new HandlerThread("worker");
// 启动线程
handlerThread.start();
// 创建与线程 Looper 相关的 Handler
mWorkerHandler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_WORK) {
Log.d("david", "worker thread: " + Thread.currentThread());
}
}
};
}
@Override
protected void onResume() {
super.onResume();
Log.d("david", "main thread: " + Thread.currentThread());
mWorkerHandler.sendEmptyMessage(MSG_WORK);
}
}
运行这段代码就可知道,任务是在工作线程中处理的,而非主线程。
利用 HandlerThread 实现线程交互
那么我们在工作线程中处理完了任务,想更新 UI 怎么办呢? 最简单的方式是直接调用 Activity
的 runOnUiThread(Runnable)
方法。
mWorkerHandler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_WORK) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.d("david", "update UI in: " + Thread.currentThread());
}
});
}
}
};
runOnUiThread()
也是利用的 Handler
原理。
其实我们还可以创建与主线程相关的 Handler
来更新 UI,例如
public class TestActivity extends AppCompatActivity {
public static final int MSG_WORK = 0x110;
private static final int MSG_UPDATE = 0x111;
private Handler mWorkerHandler;
private Handler mUIHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_acrtivity);
// 创建与主线程相关的 Handler
mUIHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
Log.d("david", "update UI in: " + Thread.currentThread());
}
};
// 创建工作线程
HandlerThread handlerThread = new HandlerThread("worker");
// 启动工作线程
handlerThread.start();
// 创建与工作线程相关的
mWorkerHandler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_WORK) {
Log.d("david", "do work in: " + Thread.currentThread());
// 向主线程相关的 Handler 发送消息更新 UI
mUIHandler.sendEmptyMessage(MSG_UPDATE);
}
}
};
}
@Override
protected void onResume() {
super.onResume();
// 向工作线程发送消息来处理耗时任务
mWorkerHandler.sendEmptyMessage(MSG_WORK);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 防止可能发生的内存泄露问题
mWorkerHandler.removeCallbacksAndMessages(null);
mUIHandler.removeCallbacksAndMessages(null);
}
}
思考
Google
为何要强制用 Handler
机制来更新 UI ? 如果没有 Handler
机制,多线程更新 UI 会导致并发问题,如果采用加锁,又会消耗性能。 Hanlder
机制能够对消息进行调度,在恰当的时候更新 UI,而又不会损耗性能,实则一举两得。
总结
通过学习本文,可以知道的事情如下:
- 粗略了解
Hanlder
的原理 - 如何使用
Handler
- 如何有效复用
Message
对象 - 如何实现线程交互。
- 如何解决
Handler
内存泄露问题