Android 常见的内存泄漏以及解决方案(一)

来源:互联网 发布:自瞄程序源码 编辑:程序博客网 时间:2024/05/21 10:09

Android 常见的内存泄漏以及解决方案(一)

Android的内存泄漏是Android开发领域永恒的话题,那今天就总结一下常见的内存泄漏吧。
在Android Studio里可以通过一些分析工具比如MAT来找出潜在的内存泄漏,在Android Device Monitor中进行Dump HPROF File,并对这个文件在SDK的platform-tools目录下进行<hprof-conv infilePath outfileName>即可在platform-tools目录下生成使用MAT工具查看的hprof文件。
如果不想这么麻烦,可以使用LeakCanary进行自动化的OOM检测。使用起来非常简单,具体可以查看LeakCanary中文使用说明。

//LeakCanary原理总结  //1. 使用ActivityLifecycleCallbacks监控所有Activity的onDestory(),将该Activity添加到内存泄漏监控队列中  //2. 在后台线程检查引用是否被清除,如果没有,调用GC  //3. 如果引用还是未被清除,把heap内存dump到一个 .hprof 文件中  //4. 使用一个独立进程中的服务计算出到GC Roots的最短强引用路径来判断是否发生Leak  //5. 建立导致泄漏的引用链,结果在Log中打印出来  
此篇将从静态变量引用Activity、匿名类、内部类、Handler、以及监听器等方面的实例说明内存泄漏的发生原因以及解决方案。

1. 单例模式导致内存泄漏(实质是静态变量引用Activity)

如果不了解单例模式的小伙伴可以查看我之前写过的设计模式-单例模式解析。已经对单例模式分析的很清楚了。这里就不多赘述了。
public class SingleUtils {      private static SingleUtils mInstance = null;      private Context context;      private SingleUtils (Context context){          this.context = context;      }        public static SingleUtils getInstance(Context context){          if(mInstance == null){              mInstance = new SingleUtils (context);          }          return mInstance;      }        public Object getObject(){//根据业务逻辑传入参数          //返回业务逻辑结果,这里需要用到context      }  }
单例由于它的静态特性使得其生命周期跟应用一样长,如果我们把上下文context(比如说一个Activity)传入到了单例类中的执行业务逻辑,这时候静态变量就引用了我们的Activity,如果没有及时置空,就会在这个Activity finish的时候,导致该Activty一直驻留在内存中,并发生内存泄漏。

解决方案:

在单例中我们尽可能的引用生命周期较长的对象,将第10行代码修改如下即可。
mInstance = new SingleUtils (context.getApplicationContext());

2. 内部类导致内存泄漏

如果我们在一个外部类中定义一个静态变量,这个静态变量是非静态内部类对象,这就会导致内存泄漏,因为非静态内部类会持有外部类的引用,从而间接导致静态地引用了外部类。
public class MyActivity extends Activity {      //非静态内部类InnerClass创建的静态实例mInnerClass    private static InnerClass mInnerClass = null;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        mInnerClass = new InnerClass();    }    class InnerClass{    }}

解决方案:

(1)在onDestroy方法中手动将mInnerClass置为null。
(2)将内部类定义为静态内部类,使其不能与外部类建立关系。

3.匿名内部类导致内存泄漏

匿名内部类同样会持有一个外部类的引用,比如说在Activity的onCreate()方法中定义了一个匿名的AsyncTask对象。如果Activity被销毁之后AsyncTask仍然在执行,那就会阻止垃圾回收器回收Activity对象,进而导致内存泄漏,直到执行结束才能回收Activity。
@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);      new AsyncTask<Void, Void, Void>() {        @Override        protected Void doInBackground(Void... params) {            //子线程中持有Activity的引用            //子线程在Activity销毁后依然会继续执行,导致该Activity内存泄漏            while (true) ;        }    }.execute();}

解决方案:

(1)在onDestroy中中断子线程的运行。
(2)使用全局的线程池代替在类中创建子线程。

4.Handler导致内存泄漏

如下所示我们在Activity中定义了一个handler,然后在Activity的onCreate()方法中,发送了一条延迟消息。
当Activity finish的时候,延时消息还保存在主线程的消息队列里。而且,这条消息持有对handler的引用,而handler又持有对Activity引用。这条引用关系会保持到消息被处理,从而,这就阻止了Activity被垃圾回收器回收,造成内存泄漏。

private final Handler handler = new Handler() {    @Override    public void handleMessage(Message msg) {    }};@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    handler.postDelayed(new Runnable() {        @Override        public void run() { /* ... */ }    }, Integer.MAX_VALUE);}

解决方案:

如果一个内部类实例的生命周期比Activity更长,那么我们就不要使用非静态的内部类。最好的做法是使用静态内部类,然后在该类里使用弱引用来指向所在的Activity。
public class SampleActivity extends Activity {    private static class MyHandler extends Handler {        private final WeakReference<SampleActivity> mActivity;        public MyHandler(SampleActivity activity) {        mActivity = new WeakReference<SampleActivity>(activity);        }        @Override        public void handleMessage(Message msg) {            SampleActivity activity = mActivity.get();            if (activity != null) {                // ...            }        }    }    private final MyHandler mHandler = new MyHandler(this);    private static final Runnable sRunnable = new Runnable() {        @Override        public void run() { /* ... */ }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        mHandler.postDelayed(sRunnable, 1000 * 60 * 10);        finish();    }}

5.监听器导致内存泄漏

很多系统服务负责执行某些后台任务。如果context对象想要在服务事件发生时被通知,就需要把自己注册到服务的监听器中。这就会让服务持有Activity的引用,如果忘记在Activity销毁时取消注册,那就会导致Activity泄漏了。
void registerListener() {    SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);    Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL);    sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);}  View mButton = findViewById(R.id.button);  mButton.setOnClickListener(new View.OnClickListener() {      @Override public void onClick(View v) {          registerListener();          nextActivity();      }  });

6. 使用IntentService 

如果Service停止失败也会导致内存泄漏。
因为系统会倾向于把这个Service所依赖的进程进行保留,如果这个进程很耗内存,就会造成内存泄漏。

解决方案:

所以推荐使用IntentService,它会在后台任务执行结束后自动停止,从而避免了Service停止失败导致发生内存泄漏的可能性。

7. Adapter中引用了Activity如何避免内存泄漏

有时需要点击ListView条目里的某个按钮实现界面跳转,getView()方法inflate布局的时候需要上下文,而且点击按钮后的跳转逻辑也需要上下文。所以我们经常会把Activity传入到Adapter中,如果Adapter中有很多耗时操作,可能就会防止Activity finish的时候被回收。

解决方案:

inflate布局是可以使用getView()方法里的parent参数的,通过parent.getActivity()获得上下文。
跳转逻辑的话,可以使用接口回调的方式,在Activity中实现这个接口并且覆写里面的方法,跳转逻辑就在这个回调方法里进行。
具体可以查看Android开发——告诉你Adapter应该写在Activity里面还是外面

转载来源:http://blog.csdn.net/seu_calvin/article/details/52333954

阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 华清池图片 华清池长恨歌门票 华清池门票多少钱 华清池温泉 华清池兵马俑一日游攻略 华清池儿童门票 华清池景区简介 华清池 兵马俑 攻略 华清池游记 华清池长恨歌2019表演时间 华清池离西安多远 兵马俑华清池一日游 兵马俑华清池路线 华清池长恨歌演出时间2018 华清池值得去吗 西安临潼华清池门票 西安华清池怎么走 华清池门票价格2018 华清池门票多钱 西安华清池兵马俑门票 华清池票价 团购华清池门票 华清池要不要门票 华清池门票预订 华清池照片 西安旅游华清池 华清池面积 西安华清池好玩吗 华清池攻略 华清池景点 西安 华清池 华清池景区 华清池旅游路线 西安华清池旅游攻略 西安华清池攻略 西安华清池介绍 西安到华清池 华清池在西安哪里 华清池好玩吗 西安华清池简介 华清池怎么玩