大家应该很喜欢吧 Handler
写成一个内部类譬如:
private Handler mMainActivityHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
其实包括我也很喜欢,而且一个
Activity
对应一个Handler
,每一个Handler
负责更新本Activity
的 UI,一对一关系,分工明确。好用到爆炸。然而 java 内部类是默认持有一个外部类的引用,因为 jvm 在把.java 源文件编译成 .class 字节码的时候,会在默认的构造函数加入外部类的引用。所以我们在内部类中也能访问外部类的引用。
然后问题就发生了,当前
Handler
持有当前Activity
的引用,Handler
不释放,Activity
也别想释放了。
当Android应用首次启动时,framework会在应用的UI线程创建一个Looper对象。Looper实现了一个简单的消息队列并且一个接一个的处理队列中的消息。应用的所有事件(比如Activity生命周期回调方法,按钮点击等等)都会被当做一个消息对象放入到Looper的消息队列中,然后再被逐一执行。UI线程的Looper存在于整个应用的生命周期内。
当在UI线程中创建Handler对象时,它就会和UI线程中Looper的消息队列进行关联。发送到这个消息队列中的消息会持有这个Handler的引用,这样当Looper最终处理这个消息的时候framework就会调用Handler#handleMessage(Message)方法来处理具体的逻辑。
在Java中,非静态的内部类或者匿名类会隐式的持有其外部类的引用,而静态的内部类则不会。
1public class SampleActivity extends Activity {
2
3 private final Handler mLeakyHandler = new Handler() {
4 @Override
5 public void handleMessage(Message msg) {
6 // ...
7 }
8 }
9
10 @Override
11 protected void onCreate(Bundle savedInstanceState) {
12 super.onCreate(savedInstanceState);
13
14 // Post a message and delay its execution for 10 minutes.
15 mLeakyHandler.postDelayed(new Runnable() {
16 @Override
17 public void run() { /* ... */ }
18 }, 1000 * 60 * 10);
19
20 // Go back to the previous Activity.
21 finish();
22 }
23}
当activity被finish的时候,延迟发送的消息仍然会存活在UI线程的消息队列中,直到10分钟后它被处理掉。这个消息持有activity的Handler的引用,Handler又隐式的持有它的外部类(这里就是SampleActivity)的引用。这个引用会一直存在直到这个消息被处理,所以垃圾回收机制就没法回收这个activity,内存泄露就发生了。需要注意的是:15行的匿名Runnable子类也会导致内存泄露。非静态的匿名类会隐式的持有外部类的引用,所以context会被泄露掉
解决办法:
声明为静态类以后,Handler就没有了Activity的引用,无法直接引用其变量或方法了,因此我们在这里使用弱引用WeakReference来解决这个问题:
static class MyHandler extends Handler {
WeakReference<MainActivity> activity; public DownloadHandler(MainActivity mainActivity) {
activity = new WeakReference<MainActivity>(mainActivity);
} @Override
public void handleMessage(Message msg) { }
}
WeakReference可以尽早地被GC回收掉,因此可以防止内存泄漏。由于这里的activity变量实际不是MainActivity类型的,因此在handleMessage中还要多一步获取与判断:
@Override
public void handleMessage(Message msg) { if (activity.get() != null) {
activity.get().someMethod();
}
}
当然每次这样调用比较麻烦,可以将WeakReference声明初始化在MainActivity中,在Handler初始化时直接传入:
public class MainActivity extends Activity {
WeakReference<MainActivity> activity;
MyHandler mHandler; @Override
protected void onCreate(Bundle savedInstanceState) {
activity = new WeakReference<MainActivity>(mainActivity);
mHandler = new MyHandler(activity);
}
class MyHandler extends Handler { private MainActivity activity; public MyHandler(WeakReference<MainActivity> ref) {
activity = ref.get();
} @Override
public void handleMessage(Message msg) { if(activity != null) {
activity.someMethod();
}
}
}
}
喜欢 就关注吧,欢迎投稿!
如有任何疑问可在文章底部留言。为了防止恶意评论,本博客现已开启留言审核功能。但是博主会在后台第一时间看到您的留言,并会在第一时间对您的留言进行回复!欢迎交流!
本文链接: https://leetcode.jp/你真的会用户handler吗?/