当前位置: 首页 > 科技观察

Android开发内存管理

时间:2023-03-13 07:41:35 科技观察

相信按部就班的Android从业者都会遇到OOM情况。如何避免和防止OOM的出现,确实是每个程序员必备的能力。今天我们就来说说Android平台下内存管理的方式。在开始今天的话题之前,我们再回顾一下两个概念。内存泄漏:对象在内存堆中分配的空间,当不再使用或没有引用点时,无法正常被GC回收。大多发生在编码不合理的情况下,比如在Activity中注册了一个广播接收器,但是在页面关闭的时候执行了unRegister,就会出现内存溢出。通常,大量内存泄漏会导致OOM。OOM:OutOfMemoery,顾名思义,就是内存溢出。内存溢出是指APP向系统申请超过最大阈值的内存请求,系统不会分配超出的空间,从而导致OOM错误。在我们的Android平台下,大部分情况都是图片加载不当导致的。内存管理的方式无非就是了解并找出内存泄漏的原因,然后根据这些转换形式进行合理的编码,以防止和避免过多的内存开销。要学习如何合理管理内存,首先要了解内存分配的机制和原理。只有深入理解其内部原理,才能真正避免OOM的发生。但是本文不会介绍Jvm/Davilk的内存分配机制。有兴趣的可以去查查历史新闻。我之前做过一个名为《JVM运行时数据区域分析》的分享。AndroidAPP最大可以申请多少内存?有人说是16MB,有人说是24MB。这种东西还是用自己的手机测试比较靠谱。测试方法也比较简单。Java中有一个Runtime类,主要用于APP与运行环境进行交互。APP不会为我们创建Runtime实例,但是Java为我们提供了单实例获取方法Runtime.getRuntime()。使用maxMemory()方法获取系统可以为APP分配的最大内存,使用totalMemory()方法获取APP当前分配的内存堆空间大小。我手上有两部手机,一部是Oppofind7,运行ColorOS,实测最大内存分配是192MB;一台天语v9,运行小米系统,实测最大内存分配100MB。现在你可以看出一些端倪了。由于Android是一个开源系统,不同的手机厂商其实是有能力修改这部分权限的,所以不同品牌不同系统的手机对APP的内存支持是不同的。,不同于IOS的永久100MB。一般来说,手机的内存配置越高,厂商也会提高手机支持的最大内存门槛,尤其是现在到处都在发布旗舰机。但是,为了考虑开发的APP的内存兼容性,开发者无法保证APP运行在什么样的手机上,只能从编码的角度对内存进行优化。下面一一分析Android内存优化的要点。1.邪恶的静电静电是好东西。声明赋值调用是如此简单方便,但随之而来的是性能问题。由于静态声明变量的生命周期其实和APP是一样的,有点类似于Application。如果大量使用,会占用内存空间得不到释放,堆积起来会造成内存不断的开销,直到挂掉。合理使用static一般用于修饰基本数据类型或轻量级对象,尽量避免修复集合或大对象,常用于修饰全局配置项、工具类方法、内部类。2、不相关的引用在很多情况下,我们需要用到传递引用,但是我们不能保证引用传出去后能及时回收。比如比较有代表性的Contextleaks。很多情况下,当Activity结束后,它还不能被回收,因为它还被其他对象指向,从而造成内存泄漏。此时可以考虑第三条建议。3.善用SoftReference/WeakReference/LruCache。Java和Android有没有这样的机制,当内存吃紧或者GC扫荡的时候,可以及时释放一些内存,从而分配到需要分配的地方。答案是肯定的,java为我们提供了两种解决方案。如果担心内存开销,可以考虑使用WeakReference,当GC扫过这块内存区域时会被回收;如果你不那么在意,可以使用SoftReference,它会在内存申请不足时自动释放,也可以解决OOM问题。同时Android从3.0开始也推出了LruCache类,使用LRU算法释放内存,同样可以解决OOM。如果兼容3.0以下版本,请导入v4包。关于第二篇中不相关引用的问题,我们可以考虑使用WeakReference来包裹参数。4.处理异步操作时,Handler+thread是一个不错的选择。但是相信大家在使用handlers的时候都会遇到warnings。这是lint对开发者的提醒。处理程序在UI线程上运行,并不断处理来自MessageQueue的消息。如果handler还有消息要处理,但是Activity页面已经结束,Activity引用不会被回收,从而造成内存泄漏。解决方法是在Activity的onDestroy方法中调用handler.removeCallbacksAndMessages(null);取消所有消息的处理,包括未决消息;第二种是将处理程序的内部类声明为静态的。5.Bitmap***killerBitmap处理不当很可能造成OOM,大部分情况都是由于这个原因造成的。Bitamp位图是Android中当之无愧的小胖子,操作起来当然要格外小心。由于Dalivk不主动回收,开发者需要在Bitmap不用的时候回收。在使用过程中,及时释放非常重要。同时,如果需求允许,也可以到BItmap上进行一定的缩放,通过BitmapFactory.Options的inSampleSize属性来控制。如果只想获取Bitmap的属性,则不需要根据BItmap的像素分配内存,只需要在解析读取Bmp时使用BitmapFactory.Options的inJustDecodeBounds属性即可。***建议大家在从网上加载图片时,使用软引用或弱引用,并在本地缓存。推荐使用android-universal-imageloader或者xUtils,大佬出品,一定是精品。前几天在说《自定义控件(三) 继承控件》的时候,也整理了一个。你可以去Github下载。6.及时关闭游标。查询SQLite数据库时,会返回一个Cursor。查询完成后,会及时关闭,使查询结果集及时回收。7、页面背景和图片加载在布局和代码中设置背景和图片时,如果是纯色,尽量使用彩色;如果是规则图形,尽量使用形图;如果稍微复杂一点,可以使用9patch;如果不能用9patch的情况下,给几款主流分辨率的机型切图。8、ListView和GridView的item缓存对于移动设备,尤其是硬件参差不齐的android生态,页面的绘制其实是非常耗时的,findViewById也相当慢。所以,如果不复用View,有列表的时候特别明显,滑动的现象往往很卡。具体可以参考历史文章《说说ViewHolder的另一种写法》9,BroadCastReceiver,Service绑定广播和服务,不用的时候记得解绑。10.I/O流I/O流操作完成,读写完成,记得关闭。11、线程当线程不再需要继续执行时,记得及时关闭。打开的线程数不容易太多。一般和自己机器的核心数一样。开启线程时建议使用线程池。12、String/StringBuffer当需要拼接的字符较多时,推荐使用StringBuffer。今天没有代码,纯文字,纯手打,很辛苦。整理了这么多优化策略,相信大家理解后就会用上,再也不会遇到OOM了。