Android开发——常见的内存泄漏以及解决方案(一)
来源:互联网 发布:java redis2.9 教程 编辑:程序博客网 时间:2024/05/02 05:05
0. 前言
转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52333954
Android的内存泄漏是Android开发领域永恒的话题,那今天就总结一下常见的内存泄漏吧。
在Android Studio里可以通过一些分析工具比如MAT来找出潜在的内存泄漏,在Android Device Monitor中进行Dump HPROF File,并对这个文件在SDK的platform-tools目录下进行<hprof-conv infilePath outfileName>即可在platform-tools目录下生成使用MAT工具查看的hprof文件。
还有如果不清楚Java里的OOM、以及内存泄漏和内存溢出区别的小伙伴,可以参考我之前写过的Java技术——Java中的内存泄漏。
此篇将从静态变量引用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里面还是外面。
下一篇将会从资源角度来说明内存泄漏的问题。
- Android开发——常见的内存泄漏以及解决方案(一)
- Android 常见的内存泄漏以及解决方案(一)
- Android开发——常见的内存泄漏以及解决方案(二)
- android开发中比较常见的内存泄漏以及改正
- Android 常见内存泄漏的解决方案
- Android内存泄漏产生的原因以及解决方案(OOM)
- Android内存泄漏产生的原因以及解决方案(OOM)
- 内存——Android中常见的内存泄漏
- Android内存泄漏——常见的内存泄露
- Android中常见的内存泄漏及解决方案
- Android——内存篇:Android中5种最常见的内存泄漏问题以及解决办法
- 常见的Android内存泄漏问题以及解决办法
- 再谈android内存泄漏—常见的八种导致 APP 内存泄漏的问题
- Android常见内存泄漏以及解决办法
- Android 常见的内存泄漏
- android常见的内存泄漏
- [Android]常见的内存泄漏
- android 常见的内存泄漏
- 动画插值器
- 关于DataGridView的使用
- new/delete 与 malloc/free的区别是什么?
- Leetcode 357. Count Numbers with Unique Digits (Medium) (cpp)
- Lua 自定义打印table函数
- Android开发——常见的内存泄漏以及解决方案(一)
- oc和h5交互
- Ubuntu下几种常用的文本编辑器
- C++ 变量和基本类型(二) const 限定符
- Android源码分析-全面理解Context
- iOS开发 UIToolBar的使用
- python中reduce()、map()、filter()的学习
- 关于使用ActionMode做长按事件报空问题
- "飞机大战"开发笔记