详解Android内存优化策略

目录
  • 前言
  • 一、内存优化策略
  • 二、具体优化的点
    • 1.避免内存泄漏
    • 2.Bitmap等大对象的优化策略
      • (1) 优化Bitmap分辨率
      • (2) 优化单个像素点内存
      • (3) Bitmap的缓存策略
      • (4) drawable资源选择合适的drawable文件夹存放
      • (5) 其他大对象的优化
      • (6) 避免内存抖动
    • 3.原生API回调释放内存
    • 4.内存排查工具
      • (1)LeakCanary监测内存泄漏
      • (2)通过Proflier监控内存
      • (3)通过MAT工具排查内存泄漏
  • 总结

前言

在开始之前需要先搞明白一个问题,为什么要做内存优化?或者说做内存优化的目的是什么?

一、内存优化策略

内存优化一般从两个方向着手优化,一方面就是上篇博客写的防止内存泄漏,避免不必要的内存资源浪费;另一方面就是APP中大对象的优化,减小大对象占用的内存。

二、具体优化的点

1.避免内存泄漏

这里直接看上篇博客就行:
详解Android内存泄露及优化方案

2.Bitmap等大对象的优化策略

图片加载算是内存占用的罪魁祸首,而且也是最常见的,所以优化bitmap的占用内存是很关键的。
Bitmap的内存计算公式如下:

Bitmap占用内存 = 分辨率 * 单个像素点的内存

比如说一个 1920 * 1080 的图片,它所占用的内存就是1920 * 1080 * 单个像素点内存。因此,对于Bitmap的优化就可以从分辨率和单个像素点两个方面来进行优化。

(1) 优化Bitmap分辨率

通常APP加载一张图片时候,ImageView的大小是确定的,比如一个ImageView的大小设置为 100 * 100 ,但是被加载的Bitmap的分辨率是 200 * 200,那么就可以通过采样压缩将该 ‘Bitmap' 的分辨率压缩到 ‘100 * 100'。通过这一压缩操作可以直接减少4倍的内存大小。代码如下:

val options = BitmapFactory.Options()
options.inSampleSize = 2 // 设置采样率为2,则会每两个像素点采一个像素,最终分辨率宽高变为原来的 1/2
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.image, options)

(2) 优化单个像素点内存

计算机中的图像一般都是由 红、绿、蓝 三个通道加上一个透明通道组成的,因此一个像素点也是由红、绿、蓝,以及一个透明通道组成,对应到内存就是通过byte来表示,比如用2个 byte 来存储一个像素点,那么每个通道就占用 4 bit 的内存,而如果用 4 个 byte 来存储一个像素点,那么每个通道就占用 1 个byte。4 字节的像素点,相比2字节的像素点可以表示的色彩会更加丰富,因此四字节的像素点组成的图像质量也更加清晰。(一个Byte由8 bits组成,是数据存储的基础单位,1Byte又称为一个字节)

在 Android 的 Bitmap 中单个像素点占用的内存与 Bitmap 的 inPreferredConfig 参数配置有关系,代码设置如下:

     final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;//只解析图片边沿,获取宽高
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        BitmapFactory.decodeFile(filePath, options);
        // 计算缩放比
        options.inSampleSize = calculateInSampleSize(options, desWidth, desHeight);
        // 完整解析图片返回bitmap
        options.inJustDecodeBounds = false;
        Bitmap bm = BitmapFactory.decodeFile(filePath, options);

options.inPreferredConfig = Bitmap.Config.RGB_565;设置的参数如下表:

Config设置 占用内存(byte) 备注
ALPH_8 1 只包含一个透明通道,透明通道占用 8bit,即 1byte
RGB_565 2 包含R/G/B三个颜色通道,不包含透明通道,三个通道占用的内存分别为5bit/6bit/5bit
ARGB_4444 2 已废弃,包含A/R/G/B四个颜色通道,每个通道占用4bit
ARGB_8888 4 24位真彩色,Android默认配置,每个通道占用 8bit
RGBA_F16 8 Android 8.0 新增,每个通道占用16bit,即两个字节

在Android系统中 Bitmap 的默认色彩模式为 ARGB_8888, 即每个像素占用了4byte,那么在默认情况下,一张分辨率为1920 * 1080 的图片,加载到内存后占用的内存大小为1920 * 1080 * 4 = 7.91M

可以通过设置 inPreferredConfig 参数来设置对应的色彩模式,例如,一个不包含透明通道的图片,我们可以将其设置为RGB_565,即保证了图片的质量,又减少了内存的占用。
此时,一张 1920 * 1080 的图片加载到内存后的内存大小为 1920 * 1080 * 2 = 3.955M,比默认情况下的内存占用减小了一半。

(3) Bitmap的缓存策略

通过缓存策略也可以一定程度上的优化内存占用问题,比如 Glide 框架中采用了三级本地缓存策略来实现Bitmap的优化,通过设置活动缓存、LRU内存缓存和本地缓存。对于相同参数的ImageView,在内存中只保存一份,以此来减少内存大小。

(4) drawable资源选择合适的drawable文件夹存放

例如我们只在 hdpi 的目录下放置了一张 100 * 100 的图片,那么根据换算关系,分辨率匹配到 xxhdpi 的手机去引用这张图片时就会被拉伸到 200*200。需要注意到在这种情况下,内存占用是会显著提高的。对于不希望被拉伸的图片,需要放到 assets 或者 nodpi 的目录下。

(5) 其他大对象的优化

可以使用更加轻量级的数据结构。例如,我们可以考虑使用 ArrayMap/SparseArray 而不是 HashMap 等传统数据结构,相比起 Android 系统专门为移动操作系统编写的 ArrayMap 容器,在大多数情况下,HashMap 都显示效率低下,更占内存。另外,SparseArray更加高效在于,避免了对key与value的自动装箱,并且避免了装箱后的解箱。

(6) 避免内存抖动

内存抖动是指在短时间内突然创建大量的对象,频繁的引发GC回收,造成内存波动的情况。在开发中应该避免频繁的创建对象,来避免内存抖动。因为内存抖动会频繁触发 GC,而GC又会引起 STW 问题,直接影响程序的性能。

比如在绘制自定义View的时候一定要避免在onDraw或者onMeasure中创建对象。

3.原生API回调释放内存

Android系统提供了一些回调来通知当前应用的内存使用情况,比如下边的两个方法:

onLowMemory() 通常来说,当所有的Background应用都被kill掉的时候,forground应用会收到onLowMemory()的回调。在这种情况下,需要尽快释放当前应用的非必须的内存资源,从而确保系统能够继续稳定运行。尤其是要释放Glide中缓存的Bitmap资源,通过调用Glide.onLowMemory方法进行资源回收。

onTrimMemory() Android系统从4.0开始还提供了onTrimMemory()的回调,当系统内存达到某些条件的时候,所有正在运行的应用都会收到这个回调,同时在这个回调里面会传递以下的参数,代表不同的内存使用情况,收到onTrimMemory()回调的时候,需要根据传递的参数类型进行判断,合理的选择释放自身的一些内存占用,一方面可以提高系统的整体运行流畅度,另外也可以避免自己被系统判断为优先需要杀掉的应用。例如调用Glide.onTrimMemory()来进行bitmap的回收。

4.内存排查工具

(1)LeakCanary监测内存泄漏

在debug模式下会一直开着LeakCanary来检测内存泄漏问题,根据LeanCannary提供的引用连可以快速定位到内存泄漏的位置。

(2)通过Proflier监控内存

在一个功能开发完成后可以通过Profiler来检测APP的内存使用情况。反复的打开关闭页面,然后触发GC,内存是否能够减少。

(3)通过MAT工具排查内存泄漏

MAT提供了很强大的功能,可以查看对象的深堆、浅堆的内存大小等。

总结

平时开发对于这块的关注不是很多,可能在没有出现内存不足的问题前不会考虑这些,项目的要求没有那么高,学习过这些点以后需要在开发中慢慢关注这些问题。

到此这篇关于详解Android内存优化策略的文章就介绍到这了,更多相关Android内存优化内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • jsp Ewebeditor使用说明

    <display-name>defaultroot</display-name> <servlet> <servlet-name>debugjsp</servlet-name> <description>Added to compile JSPs with debug info</description> <servlet-class>org.apache.jasper.servlet.JspServlet&l

  • 详解Android使用Handler造成内存泄露的分析及解决方法

    一.什么是内存泄露? Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收.也就是说,一个对象不被任何引用所指向,则该对象会在被GC发现的时候被回收:另外,如果一组对象中只包含互相的引用,而没有来自它们外部的引用(例如有两个对象A和B互相持有引用,但没有任何外部对象持有指向A或B的引用),这仍然属于不可到达,同样会被GC回收. Android中使用Handler造成内存泄露的原因 private Han

  • Android 优化Handler防止内存泄露

    Android 优化Handler防止内存泄露 Demo描述: Handler可能导致的内存泄露及其优化 1 关于常见的Handler的用法但是可能导致内存泄露 2 优化方式请参考BetterHandler和BetterRunnable的实现 package cc.cc; import java.lang.ref.WeakReference; import android.os.Bundle; import android.os.Handler; import android.os.Messag

  • 详解Android的内存优化--LruCache

    概念: LruCache 什么是LruCache? LruCache实现原理是什么? 这两个问题其实可以作为一个问题来回答,知道了什么是 LruCache,就只然而然的知道 LruCache 的实现原理:Lru的全称是Least Recently Used ,近期最少使用的!所以我们可以推断出 LruCache 的实现原理:把近期最少使用的数据从缓存中移除,保留使用最频繁的数据,那具体代码要怎么实现呢,我们进入到源码中看看. LruCache源码分析 public class LruCache<

  • 浅谈Android性能优化之内存优化

    1.Android内存管理机制 1.1 Java内存分配模型 先上一张JVM将内存划分区域的图 程序计数器:存储当前线程执行目标方法执行到第几行. 栈内存:Java栈中存放的是一个个栈帧,每个栈帧对应一个被调用的方法.栈帧包括局部标量表, 操作数栈. 本地方法栈:本地方法栈主要是为执行本地方法服务的.而Java栈是为执行Java方法服务的. 方法区:该区域被线程共享.主要存储每个类的信息(类名,方法信息,字段信息等).静态变量,常量,以及编译器编译后的代码等. 堆:Java中的堆是被线程共享的,

  • 详解Android内存优化策略

    目录 前言 一.内存优化策略 二.具体优化的点 1.避免内存泄漏 2.Bitmap等大对象的优化策略 (1) 优化Bitmap分辨率 (2) 优化单个像素点内存 (3) Bitmap的缓存策略 (4) drawable资源选择合适的drawable文件夹存放 (5) 其他大对象的优化 (6) 避免内存抖动 3.原生API回调释放内存 4.内存排查工具 (1)LeakCanary监测内存泄漏 (2)通过Proflier监控内存 (3)通过MAT工具排查内存泄漏 总结 前言 在开始之前需要先搞明白一

  • 详解Android内存泄露及优化方案

    目录 一.常见的内存泄露应用场景? 1.单例的不恰当使用 2.静态变量导致内存泄露 3.非静态内部类导致内存泄露 4.未取消注册或回调导致内存泄露 5.定时器Timer 和 TimerTask 导致内存泄露 6.集合中的对象未清理造成内存泄露 7.资源未关闭或释放导致内存泄露 8.动画造成内存泄露 9.WebView 造成内存泄露 总结 一.常见的内存泄露应用场景? 1.单例的不恰当使用 单例是我们开发中最常见和使用最频繁的设计模式之一,所以如果使用不当就会导致内存泄露.因为单例的静态特性使得它

  • 详解Android内存泄露及优化方案一

    目录 一.常见的内存泄露应用场景? 1.单例的不恰当使用 2.静态变量导致内存泄露 3.非静态内部类导致内存泄露 4.未取消注册或回调导致内存泄露 5.定时器Timer 和 TimerTask 导致内存泄露 6.集合中的对象未清理造成内存泄露 7.资源未关闭或释放导致内存泄露 8.动画造成内存泄露 9.WebView 造成内存泄露 总结 一.常见的内存泄露应用场景? 1.单例的不恰当使用 单例是我们开发中最常见和使用最频繁的设计模式之一,所以如果使用不当就会导致内存泄露.因为单例的静态特性使得它

  • 详解Android性能优化之启动优化

    1.为什么要进行启动优化 网上流行一种说法,就是8秒定律,意思是说,如果用户在打开一个页面,在8秒的时间内还没有打开,那么用户大概的会放弃掉,意味着一个用户的流失.从这里就可以看出,启动优化的重要性了. 2.启动的分类 2.1 冷启动 先来看看冷启动的流程图 从图中可以看出,APP启动的过程是:ActivityManagerProxy 通过IPC来调用AMS(ActivityManagerService),AMS通过IPC启动一个APP进程,ApplicationThread通过反射来创建App

  • 详解python 内存优化

    写在之前 围绕类的话题,说是说不完的,仅在特殊方法,除了我们在前面遇到过的 __init__(),__new__(),__str__() 等之外还有很多.虽然它们只是在某些特殊的场景中才会用到,但是学会它们却可以成为你熟悉这门语言路上的铺路石. 所以我会在试图介绍一些「黑魔法」,让大家多多感受一下 Python 的魅力所在,俗话说「艺多不压身」就是这个道理了. 内存优化 首先先让我们从复习前面的类属性和实例属性的知识来引出另一个特殊方法: >>> class Sample: ... na

  • 详解Android性能优化之内存泄漏

    综述 内存泄漏(memory leak)是指由于疏忽或错误造成程序未能释放已经不再使用的内存.那么在Android中,当一个对象持有Activity的引用,如果该对象不能被系统回收,那么当这个Activity不再使用时,这个Activity也不会被系统回收,那这么以来便出现了内存泄漏的情况.在应用中内出现一次两次的内存泄漏获取不会出现什么影响,但是在应用长时间使用以后,若是存在大量的Activity无法被GC回收的话,最终会导致OOM的出现.那么我们在这就来分析一下导致内存泄漏的常见因素并且如何

  • 详解Android内存泄漏检测与MAT使用

    内存泄漏基本概念 内存检测这部分,相关的知识有JVM虚拟机垃圾收集机制,类加载机制,内存模型等.编写没有内存泄漏的程序,对提高程序稳定性,提高用户体验具有重要的意义.因此,学习Java利用java编写程序的时候,要特别注意内存泄漏相关的问题.虽然JVM提供了自动垃圾回收机制,但是还是有很多情况会导致内存泄漏. 内存泄漏主要原因就是一个生命周期长的对象,持有了一个生命周期短的对象的引用.这样,会导致短的对象在该回收时候无法被回收.Android中比较典型的有:1.静态变量持有Activity的co

  • 详解Android布局优化

    怎样才能写出优秀的Android App,是每一个程序员追求的目标.那么怎么才能写出一个优秀的App呢?相信很多初学者也会有这种迷茫.一句话来回答这个问题:细节很重要.今天我们就从最基础的XML布局来谈谈怎么提高Android性能问题吧! 也许你经常会遇到比较复杂的布局,这种情况下,最简单的方法就是多层嵌套实现效果,但是最简单的方法是否是最优的方法呢? 这里需要打一个大大的问号?????经验告诉我们,往往简单的方法,得到的结果不是最优解,那么我们通过一个例子来研究一下怎么去优化我们的XML布局吧

  • 详解android是如何管理内存的

    目录 前言 Java Heap 进程内存分配 内存不足管理 GC 垃圾回收 内核交换守护进程 低内存终止守护进程 最后 前言 很高兴遇见你~ 内存优化一直是 Android 开发中的一个非常重要的话题,他直接影响着我们 app 的性能表现.但这个话题涉及到的内容很广且都偏向底层,让很多开发者望而却步.同时,内存优化更加偏向于"经验知识",需要在实际项目中去应用来学习. 因而本文并不想深入到底层去讲内存优化的原理,而是着眼于宏观,聊聊 android 是如何分配和管理内存.在内存不足的时

  • 详解Android Ashmem匿名共享内存

    目录 1. 简述 2. 创建 MemoryFile 和 数据写入 3. 将文件描述符传递到其他进程 4. 在其他进程接收 FileDescriptor 并读取数据 1. 简述 Android 的 匿名共享内存(Ashmem) 基于 Linux 的共享内存,都是在临时文件系统(tmpfs)上创建虚拟文件,再映射到不同的进程.它可以让多个进程操作同一块内存区域,并且除了物理内存限制,没有其他大小限制.相对于 Linux 的共享内存,Ashmem 对内存的管理更加精细化,并且添加了互斥锁.Java 层

随机推荐