优化你的APP--Android内存优化(一)

Android 内存优化(一)


内存使用三大问题

Android中内存使用和优化问题一直是我们开发者关注的重点,随着系统版本和虚拟机的升级内存的限制和使用都有了变化,那么今天我就讲讲如何优化我们App的内存使用。
针对内存方面我们主要解决3大问题:内存溢出、内存泄漏和内存抖动,同时保持一个良好的内存使用习惯。接下来我们逐一讲解三个问题的出现原因和解决办法。

内存溢出:OutOfMemory

OOM的发生是开发者最痛苦的事情之一,其提供的crash log有时候让我们无从入手。为什么会出现OOM?这跟android系统判断规则有关系,在2.X和4.X系统上对于OOM的条件有所改变,但关键都是getMemoryClass()

发生OOM的条件
  • Android 2.x系统 GC LOG中的dalvik allocated + external allocated + 新分配的大小>= getMemoryClass()值的时候就会发生OOM。 例如,假设有这么一段Dalvik输出的GC LOG:GC_FOR_MALLOC free 2K, 13% free 32586K/37455K, external 8989K/10356K, paused 20ms,那么32586+8989+(新分配23975)=65550>64M时,就会发生OOM。
  • Android 4.x系统 Android 4.x的系统废除了external的计数器,类似bitmap的分配改到dalvik的java heap中申请,只要allocated + 新分配的内存 >= getMemoryClass()的时候就会发生OOM。
如何避免OOM
  1. Bitmap压缩、缓存与复用
    Bitmap是app最需要注意的内存使用者,Bitmap在2.x系统上是被分配在native层heap中,而在3.0以后Bitmap被分配到了app的Dalvik heap中,减小其大小并重复利用它们是我们的最优选择。

    • 使用inSampleSize在把图片载入内存之前,我们需要先计算出一个合适的缩放比例,避免不必要的大图载入
    • 使用decode format设定不一样的编码格式会有很大的区别
    • 使用LRUcache来缓存bitmap对象(尤其是在List & Grid)
    • 充分使用BitmapFactory.Option inBitmap属性,利用这种特性即使是上千张的图片,也只会仅仅只需要占用屏幕所能够显示的图片数量的内存大 小。在3.0-4.4系统中inBitmap要求新申请的Bitmap必须和可复用的Bitmap对象的大小完全相同,而在4.4系统以后要求有所放 松,只要新申请Bitmap对象小于或等于可复用的Bitmap对象即可复用
    • 对Bitmap对象使用weakReference进行包装,这点在使用的时候需要分析应用场景是否适合

    现在app中使用的大多数图片框架都实现上述这些功能,大家有兴趣的话可以去github上看看这些图片库的源码,学习一下他们的设计理念和实现方式。推荐glide和fresco~

  2. 使用轻量级的数据结构
    我们最常用的K-V形式的数据结构是HashMap,Android为我们提供了更轻量级数据结构ArrayMap/SparseArray,在效率和内存都比HashMap有明显的优势。因为HashMap需要一个额外的实例对象来记录Mapping操作,同时有大量的”自动装箱“操作。
    我们在满足下述两个条件时就应该选择ArrayMap/SparseArray:对象个数的数量级最好是千以内数据组织形式包含Map结构

    • ArrayMap1:内存效率更高,它内部使用两个数组进行工作,其中一个数组记录key hash过后的顺序列表,另外一个数组按key的顺序记录Key-Value值。但是当数据过多时ArrayMap的工作效率也会下降,这是在内存占用与访问时间之间做权衡交换。

    • SparseArray2:使用场景和条件与ArrayMap基本一致,同时还解决HashMap的autoboxing行为,提高了运算效率还减少了内存占用。没有提供String类型的封装容器,这个可能在大多数使用场景中不太方便(主要为了解决自动装箱的问题~)。

  3. 避免在Android里面使用Enum
    Android官方培训课程提到过“Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.
    枚举通常情况下所占用的内存是静态常量的两倍甚至更多,有兴趣的同学可以建一个工程先打一个包看看dex有多大,然后分别加上这两段代码,再对比一下dex的大小。

    public static final int VALUE1 = 1;
    public static final int VALUE2 = 2;
    public static final int VALUE3 = 3;
    
    int func(int value) {
        switch(value) {
            case VALUE1:
                return -1;
            case VALUE2:
                return -2;
            case VALUE3:
                return -3;
        }
        return 0;
    }
    public static enum Value{
        VALUE1,
        VALUE2,
        VALUE3
    }
    
    int func(Valuevalue) {
        switch(value) {
            case VALUE1:
                return -1;
            case VALUE2:
                return -2;
            case VALUE3:
                return -3;
        }
        return 0;
    }
  4. 内存监控 onLowMemory()与onTrimMemory()

    • onLowMemory():Android系统提供了一些回调来通知当前应用的内存使用情况,通常来说,当所有的background应用都被kill掉的时候,forground应用会收到onLowMemory()的回调。在这种情况下,需要尽快释放当前应用的非必须的内存资源,从而确保系统能够继续稳定运行。
    • onTrimMemory(int):Android系统从4.0开始还提供了onTrimMemory()的回调,当系统内存达到某些条件的时候,所有正在运行的应用都会收到这个回调,同时在这个回调里面会传递以下的参数,代表不同的内存使用情况,收到onTrimMemory()回调的时候,需要根据传递的参数类型进行判断,合理的选择释放自身的一些内存占用,一方面可以提高系统的整体运行流畅度,另外也可以避免自己被系统判断为优先需要杀掉的应用。下图介绍了各种不同的回调参数:
      这里写图片描述
  5. 其他优化点
    • StringBuilder:需要使用到大量的字符串拼接的操作,这种时候有必要考虑使用StringBuilder来替代频繁的“+”
    • 对资源文件进行压缩:很有可能在初始化视图的时候就会因为内存不足而发生InflationException,这个问题的根本原因其实是发生了OOM。我们可以对我们使用的资源文件进行压缩,例如使用TinyPng。
    • Try catch某些大内存分配的操作
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值