Android开发之内存优化有效防止内存溢出OOM与内存泄漏
来源:互联网 发布:安装apache 编辑:程序博客网 时间:2024/05/20 18:41
在进行Android开发的过程中,很多开发人员都出现过OOM的情况。
什么叫做OOM其实就是OutOfMemoryError。相信Android开发对这个问题引起的程序异常,再熟悉不过了。一般会引起这些情况的原因包含:Bitmap的处理;未关闭Cursor;长期周对象拥有短周期对象,导致短周期对象不能及时释放(如单例对象长期持有Context);
现在就简单的例举一些出来,下面来进行一步步的处理:
Bitmap的处理
比较常见,所以简单说一下处理思路即可,首先获取图片先关尺寸信息,然后对比需要显示的控件尺寸,根据BitmapFactory来对图片的尺寸进行裁剪,减少图片对内存的使用,这样避免发生OOM内存溢出,代码不再列出,其他博文有很多类似代码。
Cursor的操作
示例错误代码
public void inidata() {Cursor cur = sqlliste.query(querysql, new String[] { userid, SystemCache.getCurrentUser().getAccDB() });if (cur == null)return;while (cur.moveToNext()) {userconfigcache.put(cur.getString(cur.getColumnIndex("config")), cur.getString(cur.getColumnIndex("value")));break;}}
上述代码首先对于游标的使用并未关闭,这是错误的写法,正确的应该如下
public void inidata() {Cursor cur = null;try {cur = sqlliste.query(querysql, new String[] { userid, SystemCache.getCurrentUser().getAccDB() });if (cur == null)return;while (cur.moveToNext()) {userconfigcache.put(cur.getString(cur.getColumnIndex("config")), cur.getString(cur.getColumnIndex("value")));break;}} catch (Exception e) {} finally {if (cur != null)cur.close();}}
这样即使sqllite发生了任何错误,也是保证Cursor是关闭的。
可以想像一下:在一个Activity中调用此方法,然后关闭,打开,关闭这个Activity,游标一直没有关闭,这样的结果肯定会导致内存泄漏。所以需要注意编码习惯的养成。
相似的还有BraodcastReceiver,ContentObserver,File,Stream,使用完一定要注意关闭。
长期周对象拥有短周期对象
例如把Activity作为一个参数传递给一个方法函数体A,并且这个A方法的初始化如下;
A a=new A(Activity.this);
A函数方法体
Public class A{ Private Static Context context;Public A(Context context){This.context=context;}}
这样即使这个Activity关闭了,但是这个Activity作为一个变量被A方法体内部的静态变量所持有,导致Activity的资源不能释放。重复打开、关闭操作,会发现内存会持续升高,因为Android对于一个app的内存分配是有限的,这样持续升高的内存最后的结果肯定是内存泄漏,导致App的崩溃。所以要避免这种写法。
修改代码如下:
//方法1:Public class A{ Private Static Context context;Public A(Context context){This.context=context;}Public void release(){Context=null;//去掉对Android的关联引用}}//在Activity的Ondestory方法中调用a.release();进行资源释放就好了。//方法2:这种方法简单,推荐使用Public class A{ Private Static Context context;Public A(Context context){This.context=context.getApplicationContext();//关联到全局的环境变量,这样不会影响对于Activity的资源释放}}
单例情况1:
与上面类似:这样写代码
public class SystemCache {private static SystemCache localcache;private Context context;public static SystemCache getInstance(Context context) {if (localcache == null)localcache = new SystemCache(context);return localcache;}public SystemCache(Context context) {this.context = context.getApplicationContext();}}
单例情况2:
public class TestManager { public static final TestManager INSTANCE = new TestManager(); private List<TESTListener> listenerList; public static TestManager getInstance() { return INSTANCE; } public void registerListener(TESTListener listener) { if (!mListenerList.contains(listener)) { mListenerList.add(listener); } } public void unregisterListener(MyListener listener) { mListenerList.remove(listener); }}interface TESTListener { public void doSomeThing();}
在Activity中
public class TestActivity extends AppCompatActivity { private TESTListener mTESTListener =new TESTListener() { @Override public void doSomeThing() { } }; private TestManager testManager=TestManager.getInstance(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); testManager.registerListener(mTESTListener ); }}
上面的代码看似很正常的一段代码,但是却很容易引起内存溢出,为什么呢?
非静态的内部类都会持有指向外部类对象的引用。因此我们创建的mTESTListener让单例所持有的时候,mTESTListener
是引用了TestActivity对象的(只是这种实例化监听没有写出)。
从而导致TestActivity在关闭的时候,TestActivity对象是被单例长期持有,所以不能释放,从而出现了内存泄漏的现象。
修改代码:
在TestActivity中重载onDestroy方法,在页面销毁的时候调用unregisterListener对mTESTListener进行移除。
protected void onDestroy() { testManager.unregisterListener(mMyListener); super.onDestroy();}
AsyncTask的使用不当
示例代码
mAsyncTask=new AsyncTask<String,Void,Void>() { @Override protected Void doInBackground(String... params) { //doSamething return null; } }.execute("mytask");
在主线程中用AsyncTask创建一个异步任务,那么AsyncTask就持有了对当前activity的引用了,如果这个activity关闭,
然后这个异步现成还在一直的进行长时间的后台循环运算,这样连续的对activity进行打开关闭操作,这样就会存在多个
activity的后台长时间循环运算,并且多个activity并没有被释放。这样的结果也是可想而知。
修改代码如下:
mAsyncTask=new AsyncTask<String,Void,Void>() { @Override protected Void doInBackground(String... params) { Boolean loop=true; while (loop) {//此处为模拟长时间运算 if(isCancelled()) return null; //doSomething.. Log.d("test", "task is running"); } return null; } }.execute("mytask");
然后在
protected void onDestroy() { mAsyncTask.cancel(true); super.onDestroy();}
修改的策略是在执行长时间或者循环时,进行查看当前线程是否有进行取消操作
adapter的使用
一定要注意定义一个Holder类来进行控件缓存优化,不过现在大家应该都在使用recycleview了吧Handler的使用
内部可以创建WeakReference<Activity>来对资源进行弱引用,这样如果在回收资源的时候,不会影响Activity的资源回收操作。
有时候也可以进行如下代码:去掉引用关系
public void onDestroy() { mHandler.removeMessages(message); mHandler.removeCallbacks(Runnable); mHandler.removeCallbacksAndMessages(null);}
Static的使用这个就不再类述
注意:千万不要使用Static来定义一个共享的全局变量信息(例如登录信息之类),即使是保存在ApplicationContext
中的静态变量同样会在资源紧缺的时候被回收,在手机资源不足的时候,系统会回收Static的变量值的,从而置为null;
这种全局的变量信息建议使用单例保存,并且集合相关的一些本地保存措施。
我现在使用的是内存缓存,然后是本地二进制文件,内存为空取本地二进制文件进行对象还原
对于Context的引用,尽量应用到全局的ApplicationContext(与APK同生命周期),这样不会影响资源的释放
在开发中经常遇到的也就大体这么多注意点,当然还有其他的,也应该用MAT工具来具体的分析了
- Android开发之内存优化有效防止内存溢出OOM与内存泄漏
- android 内存优化篇之内存泄漏原因与内存泄漏优化
- 内存优化之内存泄漏问题
- 30、内存优化之内存泄漏记录
- Android----内存溢出、内存优化、内存泄漏
- Java虚拟机剖析之内存区域,内存的溢出,泄漏
- Android面试题之内存溢出和内存泄漏的问题
- Android面试题之内存溢出和内存泄漏的问题
- android开发内存优化之如何有效避免oom
- android图片加载内存优化方法,有效解决大图片内存溢出(oom)
- Android 性能优化之内存泄漏检测以及内存优化(上)
- Android 性能优化之内存泄漏检测以及内存优化(下)
- Android之内存泄露、内存溢出、内存抖动分析
- ImageLoader防止内存溢出(OOM)
- Android内存优化之内存缓存
- Android内存优化之内存缓存
- Android内存优化之内存泄露
- Android OOM之内存泄漏详解
- ESB企业服务总线 --- ESB概述
- Java LinkedHashMap 逆序遍历
- 数据结构
- 内存溢出和内存泄露的区别
- Android开发之Jsoup解析webView加载数据
- Android开发之内存优化有效防止内存溢出OOM与内存泄漏
- Android事件分发理解
- C# 控件命名规范
- 【使用教程】如何把数据导出到 WPF 环境的 Excel 中
- Ogre画中画摄像机靠近模型,模型会被遮挡一部分
- Project configuration is not up-to-date with pom.xml
- core_java泛型篇小总结(1)
- 77_游戏项目_加载窗口_画图形_加载图片_编程中坐标基本知识
- AngularJS停止定时器$interval