Android常见内存泄漏及优化的学习笔记
- 单例导致的内存泄漏
public class AppSettings {
private static AppSettings sInstance; private Context mContext;
private AppSettings(Context context) {
// 错误代码
this.mContext = context;
// 正确代码
this.mContext = context.getApplicationContext();
}
public static AppSettings getInstance(Context context) {
if (sInstance == null) {
sInstance = new AppSettings(context);
}
return sInstance;
}
}
说明:如果AppSettings的getInstance传入的context是Activity、Service的上下文就会导致内存泄漏(当我们退出 Activity 时,该 Activity 就没有用了,但是因为 sIntance 作为静态单例(在应用程序的整个生命周期中存在)会继续持有这个 Activity 的引用,导致这个 Activity 对象无法被回收释放,这就造成了内存泄露。)。
- 静态变量导致的内存泄漏
public class MainActivity extends Activity {
private static Info info;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (info != null) {
info = new Info(this);
}
}
}
class Info {
public Info(Activity activity) { }
}
说明:Info作为Activity的静态成员,并且持有Activity的引用,所以当Activity退出后,info仍然引用了Activity,Activity不能被回收,这就导致了内存泄露.
解决办法:在不使用或者适当的时候把info重置为null.
- 非静态内部类导致内存泄漏
非静态内部类(包括匿名内部类)默认就会持有外部类的引用(静态内部类不会),当非静态内部类对象的生命周期 比外部类对象的生命周期长时,就会导致内存泄露。
类似场景:
1、Handler使用
2、在Activity中直接使用Thread或者AsyncTask内部类
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start();
}
private void start() {
Message msg = Message.obtain();
msg.what = 1;
mHandler.sendMessage(msg);
}
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) { }
};
}
// 造成内存泄漏的原因:message->handler->activity
// 解决办法:采用静态内部类+弱引用的方式
public class MainActivity extends AppCompatActivity {
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new MyHandler(this);
start();
}
private void start() {
Message msg = Message.obtain();
msg.what = 1;
mHandler.sendMessage(msg);
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
private static class MyHandler extends Handler {
private WeakReference<MainActivity> activityWeakReference;
public MyHandler(MainActivity activity) {
activityWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = activityWeakReference.get();
if (activity != null) {}
}
}
}
- 其他问题导致的内存泄漏
1、未取消注册或者回调导致内存泄漏(广播)
2、Timer和TimerTask导致内存泄漏
3、集合中的对象未清理造成内存泄漏
如果一个对象放入到 ArrayList、HashMap 等集合中,这个集合就会持有该对象 的引用。当我们不再需要这个对象时,也并没有将它从集合中移除,这样只要集合还在使用(而 此对象已经无用了),这个对象就造成了内存泄露。并且如果集合被静态引用的话,集合里面那 些没有用的对象更会造成内存泄露了。所以在使用集合时要及时将不用的对象从集合 remove,或 者 clear 集合,以避免内存泄漏。
4、资源未关闭或释放导致内存泄漏
在使用 IO、File 流或者 Sqlite、Cursor 等资源时要及时关闭。这些资源在进行读写操作时通常都 使用了缓冲,如果及时不关闭,这些缓冲对象就会一直被占用而得不到释放,以致发生内存泄露。 因此我们在不需要使用它们的时候就及时关闭,以便缓冲能及时得到释放,从而避免内存泄露
5、属性动画造成内存泄漏
动画同样是一个耗时任务,比如在 Activity 中启动了属性动画(ObjectAnimator),但是在销毁 的时候,没有调用 cancle 方法,虽然我们看不到动画了,但是这个动画依然会不断地播放下去, 动画引用所在的控件,所在的控件引用 Activity,这就造成 Activity 无法正常释放。因此同样要 在 Activity 销毁的时候 cancel 掉属性动画,避免发生内存泄漏。
6、WebView造成内存泄漏
关于 WebView 的内存泄露,因为 WebView 在加载网页后会长期占用内存而不能被释放,因此我 们在 Activity 销毁后要调用它的 destory()方法来销毁它以释放内存。