您的位置:首页 > 移动开发 > Android开发

自定义无内存泄漏的Handler内部类

2016-07-13 00:08 609 查看
最近做项目有很多需要在子线程中进行耗时操作,因为操作也比较简单,也就是单纯的发送网络请求然后处理回调更新UI,所以选择了Thread+Runnable+Handler的组合。

然后有了最初的代码:

代码片段1:SGAddressFragment.java

Handler handler;
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getView() != null) {
...
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
updateView();
super.handleMessage(msg);
}
};
}
}

private void saveNewAddress() {
saveNewThread = new Thread(new Runnable() {
@Override
public void run() {
Message msg = new Message();
Bundle bundle = new Bundle();
try {
boolean isSaved = doSaveNewAddress();
bundle.putBoolean("boolean_save", isSaved);
} catch (Exception e) {
e.printStackTrace();
}
msg.setData(bundle);
handler.sendMessage(msg);
}
});
saveNewThread.start();
threads.add(saveNewThread);
}


其实很多人可能一不小心就会写出这样的代码,好在Android Studio检测出来了这样的情况,并且给出了警告:



Android Studio自己检查出来可能出现内存泄漏,然后他推荐我们使用静态类来避免。为什么一定要是静态内部类?这里应该很多人都知道,非静态内部类会持有外部类的引用,而静态内部类则不会有这样的情况。具体原因可以参考这篇文章。那么我们就照它所推荐的方法,将Handler改造成静态内部类:

代码片段2:

static class AddressHandler extends Handler {
WeakReference<SGAddressFragment> sgAddressFragmentRef;

AddressHandler(SGAddressFragment sgAddressFragment) {
sgAddressFragmentRef = new WeakReference<>(sgAddressFragment);
}

@Override
public void handleMessage(Message msg) {
boolean b;
if (msg.getData() != null) {
sgAddressFragmentRef.get().update();
}
}
super.handleMessage(msg);
}
}


以上就将我们的Handler改造成了静态的内部类。这里由于更新要用到外部类Fragment里的非静态方法,所以将一个Fragment对象传进来,而大家可以看到,我使用了WeakReference对传入的对象进行弱引用处理,我们可以借助弱引用类型对外部非静态变量进行操作,而Handler仅有一条弱引用指向了Fragment对象,所以不会影响Fragment的回收,使用WeakReference也是避免内存泄漏很重要的一点。

还有一点不得不说,眼尖的朋友可以看到,在代码片段1里,当我start了线程之后,有这样一段代码:
threads.add(saveNewThread);
而这段代码在内存泄漏处理中也起到了非常大的作用。当我们新开一个线程,如果这个线程的工作一直到Activity被finish掉也没有处理完,那它将一直持有这个Activity导致Activity不能被回收。所以我在代码中创建了一个ArrayList对象用来存入新开的线程:
ArrayList<Thread> threads = new ArrayList<>();
每当我们新开了线程之后,就将thread对象添加进list里:
threads.add(saveNewThread);
然后在onDestroy()方法里将所有添加进去的线程中断:

for (Thread thread : threads) {
if (thread != null && thread.isAlive()) {
thread.interrupt();
}
}
threads.clear();


这样我们就能彻底避免内存泄露啦!

所以综上所述,只有做到这“三重”防护,我们才算是真正做到避免了内存泄漏☺。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息