用一个最常见的例子来总结handler的机制:子线程处理耗时操作,然后通知主线程更新UI。
1. 在主线程创建handler对象
1 | public Handler() { |
创建handler对象的过程会绑定当前线程的looper和该looper的消息队列,主线程的looper在主线程创建时就通过Looper.prepareMainLooper()
创建,而子线程要创建looper应该使用Looper.prepare()
,因此在子线程创建handler时应先调用Looper.prepare()
创建looper。looper在创建的时候就会创建其消息队列。
2. 在子线程处理完耗时操作后通过主线程的handler发送消息
发送消息有两种方式:
sendMessage(Message msg)
post(Runnable r)
两者最后均为调用sendMessageAtTime(Message msg, long uptimeMillis)
,区别在于后者的msg.callback = r
。消息队列会根据消息处理的时间将消息放在合适的位置,而不是直接放在队尾。
3. 主线程的looper会不断从消息队列中取出消息进行分发
1 | public static void loop() { |
loop()
首先获取当前线程的looper,因此调用该函数之前必须确定当前线程已经创建了looper。通过一个死循环,从消息队列里取出消息进行分发。
注意: 当消息队列没有消息取出来时,主线程会阻塞在loop的queue.next()
中的nativePollOnce()
方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,重新唤醒主线程工作,所以大多数时候主线程处于休眠状态,不会消耗大量CPU资源。
4. 消息会被分发到指定的handler进行处理,更新UI
1 | public void dispatchMessage(Message msg) { |
关于内存泄漏
由于Java的非静态内部类和匿名内部类会隐式地持有外部类的引用,所以下面的代码会存在内存泄漏的问题。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public class SampleActivity extends Activity {
private final Handler mLeakyHandler = new Handler() {
public void handleMessage(Message msg) {
// ...
}
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Post a message and delay its execution for 10 minutes.
mLeakyHandler.postDelayed(new Runnable() {
public void run() { /* ... */ }
}, 1000 * 60 * 10);
// Go back to the previous Activity.
finish();
}
}
当activity被finish的时候,延迟发送的消息仍然会存活在UI线程的消息队列中,直到10分钟后它被处理掉。这个消息持有activity的Handler的引用,Handler又隐式的持有它的外部类(这里就是SampleActivity)的引用。这个引用会一直存在直到这个消息被处理,所以垃圾回收机制就没法回收这个activity,内存泄露就发生了。需要注意的是:匿名Runnable子类也会导致内存泄露。非静态的匿名类会隐式的持有外部类的引用,所以context会被泄露掉。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44public class SampleActivity extends Activity {
/**
* Instances of static inner classes do not hold an implicit
* reference to their outer class.
*/
private static class MyHandler extends Handler {
private final WeakReference<SampleActivity> mActivity;
public MyHandler(SampleActivity activity) {
mActivity = new WeakReference<SampleActivity>(activity);
}
public void handleMessage(Message msg) {
SampleActivity activity = mActivity.get();
if (activity != null) {
// ...
}
}
}
private final MyHandler mHandler = new MyHandler(this);
/**
* Instances of anonymous classes do not hold an implicit
* reference to their outer class when they are "static".
*/
private static final Runnable sRunnable = new Runnable() {
public void run() { /* ... */ }
};
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Post a message and delay its execution for 10 minutes.
mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
// Go back to the previous Activity.
finish();
}
}
实现Handler的子类或者使用static修饰内部类。静态的内部类不会持有外部类的引用,所以activity不会被泄露。如果要在Handler内调用外部activity类的方法的话,可以让Handler持有外部activity类的弱引用,这样也不会有泄露activity的风险。关于匿名类造成的泄露问题,我们可以用static修饰这个匿名类对象解决这个问题,因为静态的匿名类也不会持有它外部类的引用。