handler机制总结

用一个最常见的例子来总结handler的机制:子线程处理耗时操作,然后通知主线程更新UI。

1. 在主线程创建handler对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public Handler() {
this(null, false);
}
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;
}

创建handler对象的过程会绑定当前线程的looper和该looper的消息队列,主线程的looper在主线程创建时就通过Looper.prepareMainLooper()创建,而子线程要创建looper应该使用Looper.prepare(),因此在子线程创建handler时应先调用Looper.prepare()创建looper。looper在创建的时候就会创建其消息队列。

2. 在子线程处理完耗时操作后通过主线程的handler发送消息

发送消息有两种方式:

  1. sendMessage(Message msg)
  2. post(Runnable r)

两者最后均为调用sendMessageAtTime(Message msg, long uptimeMillis),区别在于后者的msg.callback = r。消息队列会根据消息处理的时间将消息放在合适的位置,而不是直接放在队尾。

3. 主线程的looper会不断从消息队列中取出消息进行分发

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
public static void loop() {
...// 仅贴出关键代码
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// myLooper()作用:返回sThreadLocal存储的Looper实例;若me为null 则抛出异常
// 即loop()执行前必须执行prepare(),从而创建1个Looper实例

final MessageQueue queue = me.mQueue;
// 获取Looper实例中的消息队列对象(MessageQueue)

for (;;) {

Message msg = queue.next();
if (msg == null) {
return;
}
// next():取出消息队列里的消息

msg.target.dispatchMessage(msg);
// 把消息Message派发给消息对象msg的target属性
// target属性实际是1个handler对象

msg.recycle();
}
}

loop()首先获取当前线程的looper,因此调用该函数之前必须确定当前线程已经创建了looper。通过一个死循环,从消息队列里取出消息进行分发。

注意: 当消息队列没有消息取出来时,主线程会阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,重新唤醒主线程工作,所以大多数时候主线程处于休眠状态,不会消耗大量CPU资源。

4. 消息会被分发到指定的handler进行处理,更新UI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void dispatchMessage(Message msg) {

// 1. 若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息
// 则执行handleCallback(msg),即回调Runnable对象里复写的run()
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}

// 2. 若msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息
// 则执行handleMessage(msg),即回调重写的handleMessage(msg)
handleMessage(msg);

}
}
private static void handleCallback(Message message) {
message.callback.run();
// Message对象的callback属性 = 传入的Runnable对象
// 即回调Runnable对象里复写的run()
}

关于内存泄漏

由于Java的非静态内部类和匿名内部类会隐式地持有外部类的引用,所以下面的代码会存在内存泄漏的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class SampleActivity extends Activity {

private final Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Post a message and delay its execution for 10 minutes.
mLeakyHandler.postDelayed(new Runnable() {
@Override
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
44
public 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);
}

@Override
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() {
@Override
public void run() { /* ... */ }
};

@Override
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修饰这个匿名类对象解决这个问题,因为静态的匿名类也不会持有它外部类的引用。

0%