开发app中对内存的使用,一定是大家最关心的问题之一,因为内存的使用直接影响到app的性能。
内存使用中内存泄漏是开发Android应用中常见的,Android中可以使用LeakCanary来检测内存泄漏。
那么什么是内存泄漏?
内存泄漏简单解释就是:完全无用的对象,无法被被GC回收的现象就是内存泄漏。
内存泄漏会造成什么问题呢?
每个app可分配的内存是有限的,内存泄漏就会占有一块内存,过多的内存泄漏就会导致内存超出app可用的内存,导致内存溢出,app就奔溃了(引发Out Of Memory)。
接下来我们带着三个问题来分析下内存泄漏:
1.经常会出现内存泄漏的地方有哪些?
2.为什么会出现?
3.如何避免内存泄漏?
一.单例模式造成的内存泄漏:
public class Signleton {
private static Signleton mSignleton;
private Context mContext;
private Signleton(Context context){
this.mContext = context;
}
public static Signleton getInstance(Context context){
if (mSignleton == null){
mSignleton = new Signleton(context);
}
return mSignleton;
}
}
这里为什么会出现内存泄漏呢?
因为单例模式的生命周期和应用程序是一样长的,所以当我们在一个activity中调用这个单例,传入activity作为context,单例就持有了这个activity的引用,而当我们退出这个activity时,由于单例的生命周期是同应用程序一样长,所以这个单例还持有activity的引用,这个activity对象就不会被回收,这时就造成了内存泄漏。
如何解决:
private Signleton(Context context){
this.context = context.getApplicationContext();
}
这里我们改成不管传入的context是activity还是其他的都转换为整个应用程序的context,这样生命周期就和单例一样长,就避免了内存泄漏。
二.非静态内部类造成的内存泄漏:
private final String TAG = "DemoActivity";
private static Interior mInterior;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
if (mInterior == null){
mInterior = new Interior();
}
}
class Interior{}
这里为什么会出现内存泄漏呢?
这个activity中有一个非静态内部类,因为非静态内部类中隐式持有外部类的引用,所以内部类Interior中就持有activity的引用,
例子中静态变量mInterior的生命周期是和应用程序一样长的,而该静态变量中又持有activity的引用,所以到activity销毁时,回收activity的时候无法被回收,就出现了内存泄漏。
如何解决:
static class Interior{
}
把该非静态内部类改成静态内部类就可以了(因为静态内部类不会持有外部的引用)。
三.Handler造成的内存泄漏:
private void toHandler(){
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
tvTitle.setText("hello");
}
},1000);
}
这里为什么会出现内存泄漏呢?
这段代码中有两个方面会造成内存泄漏:
1.Runnable是匿名内部类,持有Activity的 TextView会造成内存泄漏。
2.TextView持有Activity 的强引用,这样也会造成内存泄漏。
因为handler的消息机制,当Activity销毁,handler中还有为处理的Message时就会持有activity的引用从而导致无法被回收,出现内存泄漏。
如何解决:
方法1:改成静态内部类+弱引用
private static class DemoRunnable implements Runnable{
private WeakReference<TextView> wTextView;
protected DemoRunnable(TextView textView){
wTextView = new WeakReference<TextView>(textView);
}
@Override
public void run() {
wTextView.get().setText("hello");
}
}
方法2:在Activity的onDestory中移除mHandler的所有Message
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
四.线程造成的内存泄漏:
线程的内存泄漏同Handler一样也是匿名内部类Runnable造成的,解决方式同handler方法1一样。
五.资源未关闭造成的内存泄漏:
主要使用的资源:
1.BraodcastReceiver
2.ContentObserver
3.File
4.Cursor
5.Stream
6.Bitmap
这些资源的使用都需要在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,从而造成内存泄漏。