一、什么是内存泄漏
内存泄漏是指当对象已经使用完毕,但是在内存中还有其他对象或者变量(静态变量)持有它的引用,GC的时候系统无法回收该对象。即长生命周期的对象持有短生命周期的对象的引用。换成更通俗的话讲:一个对象已经使用完毕了,应该被回收,但是别人一直持有它的引用,导致它目前不能被回收。
二、android有哪些地方容易发生内存泄漏
1.单例对象持有activity引用
/**
* @Author: david.lvfujiang
* @Date: 2019/12/5
* @Describe:
*/
public class JavaBean {
private static JavaBean bean;
private Context context;
public JavaBean(Context context) {
this.context = context;
}
public static JavaBean createBean(Context context) {
if (bean == null) {
bean = new JavaBean(context);
return bean;
}
return bean;
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
JavaBean.createBean(this);
finish();
}
}
我们在activity中创建单例对象bean,并把this当做的属性赋值给bean,然后结束activity。因为bean对象是用static修饰的,它的生命周期是跟随整个进程。因此即使activity关闭了,bean对象依然持有activity的引用,activity对象将无法回收。
解决办法:
1.将activity的引用改成弱引用。
/**
* @Author: david.lvfujiang
* @Date: 2019/12/5
* @Describe:
*/
public class JavaBean {
private static JavaBean bean;
//强引用声明为弱引用
private WeakReference<Context> weakReference;
public JavaBean(Context context) {
//创建弱引用
this.weakReference = new WeakReference<Context>(context);
}
public static JavaBean createBean(Context context) {
if (bean == null) {
bean = new JavaBean(context);
return bean;
}
return bean;
}
}
2.将context改成application的context,因为application的生命周期也是跟随整个进程。
2.非静态内部类引起的内存泄漏
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//线程匿名内部类
Thread thread=new Thread(){
@Override
public void run() {
super.run();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread.start();
finish();
}
}
非静态内部类(成员内部类和匿名内部类)创建的时候会隐式的持有外部类的引用。例如我们创建一个线程的匿名内部类执行耗时操作,然后直接关闭activity,也会发生内存泄漏。
解决办法:
1.改成静态内部类,然后弱引用持有activity对象(因为静态内部类和外部类没有引用关系,不可以直接访问外部类的属性和方法,所以需要持有activity对象才能调用)
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
JavaBean.createBean(this);
MyThread myThread = new MyThread(this);
myThread.start();
finish();
}
static class MyThread extends Thread {
//弱引用声明
WeakReference<Context> weakReference;
public MyThread(Context context) {
//创建弱引用对象
weakReference = new WeakReference<Context>(context);
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2.日常我们使用handler的时候也是直接使用匿名内部类,并且还显示的持有activity对象在handler进行ui操作,但是当我们的消息队列还有消息时,若我们activity已经关闭就会发生内存泄漏。除了采用上面的静态内部类+弱引用外,我们可以在onDestroy()方法中清空消息队列。
public class MainActivity extends AppCompatActivity {
//创建handler
Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
Toast.makeText(MainActivity.this,"哈哈",Toast.LENGTH_SHORT).show();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
JavaBean.createBean(this);
//创建线程(采用静态内部类+弱引用方式)
MyThread myThread = new MyThread(this);
myThread.start();
finish();
}
static class MyThread extends Thread {
WeakReference<Context> weakReference;
public MyThread(Context context) {
weakReference = new WeakReference<Context>(context);
}
@Override
public void run() {
//获取外部类对象
MainActivity mainActivity = (MainActivity) weakReference.get();
//handler发现message
mainActivity.handler.sendMessage(new Message());
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//activity关闭时情况消息队列
handler.removeCallbacksAndMessages(null);
}
}
3.静态变量引用对象
public class Main2Activity extends AppCompatActivity {
static JavaBean bean;
static ArrayList<String> arrayList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
bean = new JavaBean();
arrayList.add("哈哈哈")
}
}
静态变量是跟随类进行加载,生命周期跟随进程,当我们的对象使用完毕后,进程结束之前该对象就被静态变量一直引用,系统也无法进行回收。使用静态集合也是,集合会一直引用内部的对象,导致内存泄漏。
4.注册与反注册
例如使用eventBus时我们都要在oncreate()中注册,在onDestroy() 反注册。若onDestroy()不进行反注册,eventBus将一直持有activity对象。当activity发生异常结束生命时不会执行onDestroy()方法,也就是说不会进行反注册,所以我们应当在oncreate()当中判断是否注册过再进行注册(避免activity发生异常结束后再打开时又创建新的activity对象注册)
5.使用AsyncTask引起的内存泄漏
AsyncTask可以执行异步操作,同时提供我们更新ui的方法,底层实现原理可以看这篇文章:Android开发:异步任务AsyncTask源码解析
但是AsyncTask很容易发生内存泄漏哦,因为AsyncTask可能持有activity的引用去更新ui,假如AsyncTask还在持续异步操作我们就把activity关闭了就会发生内存泄漏。
6.动态注册广播接收器
动态注册广播接收器时应该主动在activity生命周期结束时取消注册,注册广播时Activity与mBroadcastReceiver实际上是通过AMS相互持有强引用的。若不进行反注册则容易引起Activity发生内存泄漏。