Android中的内存泄漏

先说一下出现内存泄漏的原因:

       在Android应用程序中,如果一个对象已经不需要被使用了,那么它应当被回收,而这时另一个对象还在持有对该对象的引用,此时这样就会导致无法被GC回收,内存泄漏便出现了。内存泄漏是Android开发中的一个重要问题,我们在编写应用程序时一定要加以防范,防止内存泄漏发生。下面说一下Android开发中最常见的5个内存泄漏问题:

一:单例模式造成的内存泄漏:

       单例设计模式我就不多说了,这个是最基本的设计模式,相信大家都会使用,但是时候我们在使用单例设计模式时没有注意到其中的细节,就会造成内存泄漏。

       单例设计模式的静态特性会使他的生命周期和应用程序的生命周期一样长,这就说明了如果一个对象不在使用了,而这时单例对象还在持有该对象的引用,这时GC就会无法回收该对象,造成了内存泄露的情况。

       下面是错误的单例设计模式的代码:

import android.content.Context;

public class MessageManager{
    private static MessageManager instance;
    private Context context;
    private MessageManager(Context context) {
        this.context = context;
    }
    public static MessageManager getInstance(Context context) {
        if (instance != null) {
            instance = new MessageManager(context);
        }
        return instance;
    }
}

上面的代码是一个最普通的单例模式,但是需要注意两个问题:

1、如果我们传入的Context是Application的Context的话,就没有任何问题,因为Application的Context生命周期和应用程序生命周期一样长。

2、如果我们传入的Context是Activity的Context的话,这时如果我们因为需求销毁了该Activity的话,Context也会随着Activity被销毁,但是单例还在持有对该类对象的引用,这时就会造成内存泄漏。

所以,正确的单例模式写法应该是这样的:

 

import android.content.Context;

public class MessageManager{
    private static MessageManager instance;
    private Context context;
    private MessageManager(Context context) {
        this.context = context.getApplicationContext();
    }
    public static MessageManager getInstance(Context context) {
        if (instance != null) {
            instance = new MessageManager(context);
        }
        return instance;
    }
}

       这样的话不管我们传入什么样的Context,最终使用的都是Application的Context,单例的生命周期和应用一样长,这样就不会造成内存泄漏了。

二、非静态内部类创建的静态实例造成的内存泄漏

       有时候因为需求我们会去频繁的启动一个Activity,这时为了避免频繁的创建相同的数据源,我们通常会做如下处理:

import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;

import com.zhangjun.ckmessageforphoneofmultiple.R;

public class TestActivity extends AppCompatActivity {

    private static MyResourse mResource = null;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        if(mResource == null){

            mResource = new MyResourse();

        }
    }

    class MyResourse {


    }

}

       这样就在Activity中创建了非静态内部类,非静态内部类默认持有Activity类的引用,但是他的生命周期还是和应用程序一样长,所以当Activity销毁时,静态内部类的对象引用不会被GC回收,就会造成了内存溢出,解决办法:

1、将内部类改为静态内部类。

2、将这个内部类封装成一个单例,Context使用Application的Context

三、Handler造成的内存泄漏

先看一下不规范的Handler写法:

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import com.zhangjun.ckmessageforphoneofmultiple.R;

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(){
        Message message = Message.obtain();
        mHandler.sendMessage(message);
    }
}

这里的handler也是一个非静态匿名内部类,他跟上面的一样,也会持有Activity的引用,我们知道handler是运行在一个Looper线程中的,而Looper线程是轮询来处理消息队列中的消息的,假设我们处理的消息有十条,而当他执行到第6条的时候,用户点击了back返回键,销毁了当前Activity,这个时候消息还没有处理完,handler还在持有Activity的引用,这个时候就会导致无法被GC回收,造成了内存泄漏。正确的做法是:

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() {
        Message message = Message.obtain();
        mHandler.sendMessage(message);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
}

       创建一个静态内部类继承自handler,然后再在构造参数中对handler持有的对象做弱引用,这样在回收时就会回收了handler持有的对象,这里还做了一处修改,就是当我们的回收了handler持有的对向,即销毁了该Activity时,这时如果handler中的还有未处理的消息,我们就需要在OnDestry方法中移除消息队列中的消息。

四、线程造成的内存泄漏

       线程使用不恰当造成的内存泄漏也是很常见的,下面举两个例子:

   new AsyncTask<Void, Void, Void>() {
        @Override
        protected Void doInBackground(Void... params) {
            SystemClock.sleep(10000);
            return null;
        }
   }.execute();

   new Thread(new Runnable() {
       @Override
       public void run() {
           SystemClock.sleep(10000);
       }
   }).start();

      上面是两个内部类,当我们的Activity销毁时,这两个任务没有执行完毕,就会使Activity的内存资源无法被回收,造成了内存泄漏。

      正确的做法是使用静态内部类:如下

static class MyAsyncTask extends AsyncTask<Void, Void, Void> {
        private WeakReference<Context> weakReference;
  
        public MyAsyncTask(Context context) {
            weakReference = new WeakReference<>(context);
        }
  
        @Override
        protected Void doInBackground(Void... params) {
            SystemClock.sleep(10000);
            return null;
        }
  
        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            MainActivity activity = (MainActivity) weakReference.get();
            if (activity != null) {

            }
        }
    }
    static class MyRunnable implements Runnable{
        @Override
        public void run() {
            SystemClock.sleep(10000);
        }
    }

    new Thread(new MyRunnable()).start();
    new MyAsyncTask(this).execute();

  五、资源未关闭造成的内存泄漏

        在使用完BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源时,一定要在Activity中的OnDestry中及时的关闭、注销或者释放内存,否则这些资源不会被GC回收,就会造成内存泄漏。

这些是总结出来的比较常见的内存泄漏问题,之后还会有补充,如果大家有什么好的意见可在下方写评论!谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值