Handler的写法

对Handler的写法进行的讨论。

作为内部类

第1种写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class TestView extends View {

private ViewHandler mViewHandler;

public TestView(Context context) {
super(context, null);
mViewHandler = new ViewHandler();
}

private class ViewHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
}

但是Framework的源码如com\android\internal\app\NetInitiatedActivity很多都这么写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case GPS_NO_RESPONSE_TIME_OUT: {
if (notificationId != -1) {
sendUserResponse(default_response);
}
finish();
}
break;
default:
}
}
};

第2种写法:

1
2
3
4
5
6
private Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
//TO DO
}
});

因为内部类会隐式的持有外部类的应用,第1种写法android studio的lint检查就会报有内存泄漏的风险。而第2种写法不会,感觉是android studio没有检查出来

对于内部类的写法,网上有很多文章都是建议采用静态类加弱引用来解决,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
private static class ViewHandler extends Handler {

WeakReference<TestView> testViewWeakReference;

ViewHandler(TestView testView) {
testViewWeakReference = new WeakReference<>(testView);
}

@Override
public void handleMessage(Message msg) {
// TO DO
}
}

作为一个变量

第3种写法:

1
Handler mHandler = new Handler();

然后去post一个Runnable。比如handler去post一个延时任务,如

1
handler.postDelay(mTask, 3000);

然后在Activity 的onDestroy的方法里调用

1
mHandler.removeCallbacksAndMessages(null);

现在的问题是

  1. 为什么android framework可以在这么写,不会造成内存泄漏吗?
  2. 对于new Handler(Looper.getMainLooper())的写法,这个只是声明了在主线程建立Handler,并不能避免内存泄漏的风险,但是Android Studio不会出现警告,因为android studio不是万能的吗?
  3. 对于第3种写法,有必要调用mHandler.removeCallbacksAndMessages(null);吗? 如果不调用,有没有可能出现延时任务还没执行,activity被销毁,而延时任务持有了activity的引用,从而造成内存泄漏
  4. 什么的写法比较好呢?

解答

1.我的理解是:内存泄漏不是必然的,只是会有这种风险

2.其实就是因为传了参数,而静态分析并没有判断这个参数是不是main thread的looper,而非主线程并没有这个问题的,所以简单化有参数就不报错了。

3.对于第3种写法,有必要调用mHandler.removeCallbacksAndMessages(null); 吗? 如果不调用,有没有可能出现延时任务还没执行,activity被销毁,而延时任务持有了activity的引用,从而造成内存泄漏

如果你需要继续运行handler内未完成的操作,那就不需要调用咯,这时候延时任务还没执行,activity被销毁,那引用activity的一些数据就可能空指针,但是不会内存泄漏,原因参照Java弱引用。当然更多情况下activity destory的情况下并不需要处理接下来的信息了,那就去remove相关的CallbacksAndMessgaes,null只是代表了所有。

4.哪种写法更好

参照编译器给你的信息就好:

1
If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.

相关链接

关于 Handler 的写法讨论