android开发:android代码不规范引起的内存泄漏与及解决办法

一、什么是内存泄漏

内存泄漏是指当对象已经使用完毕,但是在内存中还有其他对象或者变量(静态变量)持有它的引用,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发生内存泄漏。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值