项目是去年七月份写的,功能都完成了。因为时间比较紧,就没怎么测试。后来想把这个项目用来做毕业设计,在测试的时候出现了问题。点击注销登录,跳转到登录页面,重新登录,跳转到主页,然后出现OOM,怀疑出现了内存泄漏。
主要原因是,注销登录后(从主界面跳转到登录页面),某个类持有MainActivity的实例,导致MainActivity 无法得到回收,导致内存泄漏。
于是集成了 Leakcanary进行检测,刚开始Leakcanary显示是这样的
Leakcanary 显示 EaseConversationList 持有 MainActivity 的实例,导致无法进行回收。
简单,将EaseConversationList 类里面的所有 context,换成context.getApplicationContext()
Leakcanary 显示 EaseConversationList 的父容器 LinearLayout持有MainActivity的引用,确实是这样的
看一下源码:
试着解决一下 ,重写布局,不能让布局持有 MainActivity的实例
public class MyLayout extends LinearLayout {
public MyLayout(Context context) {
this(context.getApplicationContext(),null);
}
public MyLayout(Context context, @Nullable AttributeSet attrs) {
this(context.getApplicationContext(), attrs,0);
}
public MyLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context.getApplicationContext(), attrs, defStyleAttr);
}
}
修改布局:
本以为解决了,但是又出现下一个问题
显示MyMessageFragment 存在匿名类 实现runable方法
看一下代码
private void initData() {
// run in a second
final long timeInterval = 10000;
Runnable runnable = new Runnable() {
public void run() {
while (true) {
// ------- code for task to run
conversationListView.refresh();
// ------- ends here
try {
Thread.sleep(timeInterval);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
new Thread(runnable).start();
}
这下子终于找到了真正的原因,这个线程如果没有关闭的话,会一直运行下去。
当注销登录时,调用了MainActivity的finish()方法,但是这个线程没有被关闭, conversationListView.refresh();一直都在被调用。
conversationListView 一直持有MainActivity的引用,导致MainActivity 无法被回收,引起内存泄漏,最终出现OOM。
好了,问题发现了,接下来 就是 在注销登录的时候关闭这个线程就好啦。
1、线程直接关闭 使用共享变量和 interrupt();。 不知为何无法进行关闭线程
2、使用线程池,关闭线程池,进而关闭线程。 不知为何也无法进行关闭线程。shutdown()、shutdownNow() 都不行
3、既然前两种方式不行,那就使用系统回调进行关闭,借助 AsyncTask 解决成功。Leakcanary 再也没有显示内存泄漏
private MyTask myTask;
private void initData() {
myTask=new MyTask();
myTask.execute();
}
class MyTask extends AsyncTask {
@Override
protected Void doInBackground(Void... voids) {
// run in a second
final long timeInterval = 10000;
while (true) {
// ------- code for task to run
conversationListView.refresh();
// ------- ends here
try {
Thread.sleep(timeInterval);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}
}
@Override
public void onDestroy() {
//如果异步任务不为空 并且状态是 运行时 ,就把他取消这个加载任务
if(myTask !=null && myTask.getStatus() != AsyncTask.Status.FINISHED){
myTask.cancel(true);
}
super.onDestroy();
}
然后就再也没有出现内存泄漏问题
在多线程开发中,如果一个线程可能会一直运行下去,必须进行显试的关闭。以后需要多注意。
不过最好的话 还是在应该 在应用 全局 封装一个线程池吧