Android内存泄漏

Android内存泄漏


直入正题,首先说说什么是内存泄漏

一、什么是内存泄漏

内存泄漏是当程序向系统申请内存空间后,在使用完毕后未释放内存或释放内存失败,从而产生了无用的内存消耗。各位看官也可以顺道去了解一下垃圾回收机制(gc)。这里还是简单讲一下,当一个不可到达的对象时,在执行gc操作时会被回收,而当一个我们不再使用的数据依然是可到达的状态的时候,这就是发生了内存泄漏。

二、内存泄漏的影响

在说内存泄漏的影响之前大家需要先了解一下内存溢出,简单来说,内存溢出就是所需要的内存超出了系统为之分配的内存。
而过多的内存泄漏将会导致内存溢出,这将会导致应用出现Crash,这对用户来说将会是很不好的体验,所以在开发过程中我们应该尽量避免内存泄漏的发生。

三、常见的内存泄漏情形

在开发过程中发生内存泄漏的情况很多,下面我将对比较常见的内存泄漏情况进行简单说明

1.单例模式引发的内存泄漏

Android的单例模式非常受开发者的喜爱,不过使用的不恰当的话也会造成内存泄漏。因为单例的静态特性使得单例的生命周期和应用的生命周期一样长,这就说明了如果一个对象已经不需要使用了,而单例对象还持有该对象的引用,那么这个对象将不能被正常回收,这就导致了内存泄漏。
下面给个单例发生内存泄漏的例子:

public class AppManager {

private static AppManager instance;

private Context context;

private AppManager(Context context) {

    this.context = context;

}

public static AppManager getInstance(Context context) {

    if (instance != null) {

        instance = new AppManager(context);

    }

    return instance;

}
}

当我们在初始化getInstance的时候传入的context为Activity的context的时候将会发生内存泄漏,因为在Activity退出时,activity本该被回收,但是由于单例模式持有了Activity的引用,而单例模式的生命周期和应用一样长,所以会导致Activity不能被回收,这也就导致了内存泄漏的发生。
避免方法:用Application的Context代替Activity的Context,因为Application的生命周期和单例是相同的,所以将不会有内存泄漏问题。

2.非静态内部类引发的内存泄漏

非静态内部内部类会默认持有外部类的引用,这在内部类与外部类的生命周期不相同的时候将会发生内存泄漏。
例:

public class MainActivity extends AppCompatActivity {

private static DemoResource mResource = null;

@Override

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    if(mManager == null){

        mManager = new DemoResource();

    }

    //...

}

class DemoResource {

    //...

}

}

由于DemoResource是非静态内部类,持有外部类的引用,而mResource 的生命周期和应用的相同,所以当activity结束时会发生内存泄漏。
避免方法:避免使用非静态内部类,尽量使用静态内部类。

3.Handler造成的内存泄漏

在开发过程中需要访问网络或一些耗时的操作时经常会使用Handler进行处理,这时候不加留意便会发生内存泄漏。(我们知道消息队列是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用)
下面举一个Handler内存泄漏的例子:

public class MainActivity extends AppCompatActivity {

private Handler mHandler = new Handler() {

    @Override

    public void handleMessage(Message msg) {

        //...

    }

};

@Override

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    loadData();

}

private void loadData(){

    //...request

//发送消息
    Message message = Message.obtain();

    mHandler.sendMessage(message);

}
}

这种写法相信很多同学都有过,然而这样将会造成内存泄漏,因为mHandler持有Activity的引用。在上面括号中的情况发生时会发生内存泄漏。
解决办法:
继承Handler写一个静态内部类,然后对Handler的持有对象使用弱引用,这样再回收是也可以回收handler持有的对象。并在Activity的Destroy时或者Stop时应该移除消息队列中的消息。
具体的示例如下:

public class MainActivity extends AppCompatActivity {

private MyHandler mHandler = new MyHandler(this);

private TextView mTextView ;

private static class MyHandler extends Handler {

    private WeakReference<Context> reference;

    public MyHandler(Context context) {

        reference = new WeakReference<>(context);

    }

    @Override

    public void handleMessage(Message msg) {

        MainActivity activity = (MainActivity) reference.get();

        if(activity != null){

            activity.mTextView.setText("");

        }

    }

}



@Override

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    mTextView = (TextView)findViewById(R.id.textview);

    loadData();

}



private void loadData() {

    //...request

    Message message = Message.obtain();

    mHandler.sendMessage(message);

}



@Override

protected void onDestroy() {

    super.onDestroy();

    mHandler.removeCallbacksAndMessages(null);

}

}

注意,因为使用的是弱引用持有数据,所以在使用时一定要先进行判Null,否则可能会造成空指针异常

4.线程造成的内存泄漏

对于线程造成的内存泄漏,也是平时比较常见的,废话不说,直接上示例:

new Thread(new Runnable() {

        @Override

        public void run() {

            SystemClock.sleep(10000);
            //延时操作

        }

    }).start();

上面的Runnable是一个匿名内部类,所以ta对activity有一个隐式的引用, 很明显ta进行了一个延时操作,当我们在现在还没有执行完之前关闭了这个activity,也就发生了内存泄漏。
解决办法:你都能猜到的方法,依然还是使用静态内部类。示例代码就不上了

5.资源未关闭造成的内存泄漏

对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。

总结

以上是在android开发中常见的内存泄漏情形,对于生命周期不一致的问题(其实前四种都可以看作这种情况),主要是采用静态内部类,还有就是合理使用弱引用(ps:大家也可以去看看弱引用、软引用、强引用等的区别),其他具体开发中的问题还需具体分析。对于资源,一定要及时关闭

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值