从内存优化视角再看 Glide 图片加载库

前置背景

Glide 作为常用的图片加载框架,框架层面已经对内存方面有不少优化,但作为一个图片框架,确保正确性一定是第一位的,因此在应用层还可以在适当的场景做一些额外的优化,当然你需要了解优化设置可能产生的问题。另外框架设计复杂性是很高的,但暴露给上层的 API 又非常简单,这导致我们忽略一些基本的工作原理,从而有意无意错误的使用框架。

这不是一篇 Glide 源码分析类的文章,如果对 Glide 使用还不够熟悉,不建议直接阅读。 以下文档结论基于 Glide v4+ 版本。

Glide 的一些特性

  1. 在不经其他额外配置的情况下 Glide 加载到内存中的 bitmap 大小不会超过视图大小本身,因此不会存在内存浪费。例如,网络图片原图大小为 200x200,但显示在页面中的 ImageView 控件大小为 100x100,Glide 内部最终解码的图片大小为 100x100。
  2. 若网络图片大小 > 控件宽或高,则默认情况下会按比例放大,返回一个满足宽高比例的位图。
  3. Glide v4+ 开始图片加载回归 ARGB_8888 格式作为默认 DecodeFormat。
  4. 如果加载图片时使用 RGB_565 格式,则实际运行不一定能够生效,主要是受两方面影响:1是如果图片本身包含透明度则仍会使用 ARGB_8888 格式,2是包含一些圆角或关于Alpha 的 Transform 变换,也会保持 ARGB_8888 格式。
  5. Glide 占用系统哪部分的内存?从 Android 8.0 开始,Bitmap 内存分配在 Native 堆,在未开启硬件位图前,还会在硬件层复制一份位图的拷贝,因此是双倍内存。硬件位图特性从 Android O 版本开始,在不了解硬件位图弊端前,应该谨慎使用,详情参考:了解硬件位图 。由此,应用不会因为图片内存占用过高导致 OOM。

Glide 使用的一些细节点

手动设置 override(width, height) 时,要确保不要使用大于控件实际尺寸的 size。

手动调用 override 的方式,对于解码图片的大小优先级最高,会默认跳过采样策略计算,如果你明确知道显示区域需要多大的图片可以使用此方法,避免图片放大(第5条)。如果想使用原图大小,可以使用 override(Target.SIZE_ORIGINAL)。

使用 preload 预加载图片默认会 decode 原图大小

preload 相关API 是不需要指定 Target 的,Glide 内部会默认加载原图,如果想明确 UI 上需要的尺寸,需调用重载的方法 preload(int width, int height)定制图片尺寸。

ImageView 在布局文件中,避免设置不明确的 match_parent 作为宽高

Glide 解码图片时是不会考虑 ImageView 的 scale_type 属性的,来看这个例子:

<ImageView
    android:id="@+id/iv_cover_bg"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="centerCrop" />

由于图片设置了 centerCrop 属性,在直观看图片会按等比居中缩放,但由于 decode 发生在 scaleType 生效之前,因此使用 Glide 加载图片时会以 ImageView 的实际尺寸(即全屏) 的宽高来解码图片,由此产生了不必要的内存开销。

解决方案比较多,常规的可以提前通过原始图和容器宽高比,计算出实际需要显示的图片尺寸,并使用 override(width, height) 方法强制指定解码图片的大小。亦或前提知道图片的比例,可使用 layout_constraintDimensionRatio 约束 ImageView 控件的大小。

如果封面图有圆角需求,使用 CardView 的圆角属性会比 Glide rounderCorner 内存开销低。

原因是 Glide rounderCorner 发生在图片decode 之后,经一轮变换会产生一个新的位图(官方描述为所有基于BitmapTransformation 变换产生的位图会被存储在 BitmapPool 中,并最终按 LRU 算法释放),而 CardView 是通过canvas.drawRoundRect(mBoundsF, mRadius, mRadius, paint),drawRoundRect 会转化成一系列绘图指令到 GPU,不会在堆内存产生额外的内存开销。

以下是实验数据,在三种情况下做内存数据对比,前置条件是加载一张 750x750 大小的不带透明度的 jpeg 格式的网络图片。按常规来说,如果使用 ARGB_8888 格式解码,产生的内存开销为 750x750x4 = 2197 kb,如果使用 RGB_565 则能减少一半的内存开销。

场景Native Heap(KB)Graphics(KB)
RGB_565 & 默认直角127305410
RGB_565/ARGB_8888 & 圆角 Transform173266564
RGB_565 & CardView 圆角136335417

可以看到是 NativeHeap 有明显的降低,推测与RounderCorner 额外变换导致的位图临时开销有关,Graphics 降低 1M 左右与预期相符。

关注图片放大场景

在背景知识中提到如果原图比控件容器大,则会缩小图片,并不会导致内存浪费。但如果原图比容器小会发生什么呢?例如:原图为 100x100,但容器为 200x200,那么默认情况下 decode 出一个 200x200 的位图。事实上,Glide 整体的图片缩放机制是通过内部的 DownsampleStrategy 完成的,其内部定义了与 ScaleType 类似的采样策略,默认的采样策略为 CENTER_OUTSIDE: 当原图尺寸 < 控件尺寸时会成比例放大至控件大小。另外还有其他采样策略,使用 AT_LEAST、AT_MOST、CENTER_INSIDE 模式,当明确知道原图尺寸 < 控件尺寸时均不会放大原图;反之,当原图尺寸 > 控件尺寸是可能会出现解码出大于控件尺寸图的情况。

使用示例:

Glide.with(this)
    .asBitmap()
    .load(url)  
    .apply(RequestOptions.downsampleOf(DownsampleStrategy.AT_LEAST))
    .into(imageView)

RGB_565 模式不生效与 Alpha Transform 变换冲突问题。

有效降低内存,对于多图文或 Feed 列表的 App,开启该配置可降低 10-20M 内存。示例全局开启 RGB_565 解码配置。

@GlideModule
class GlideConfig : AppGlideModule() {

    override fun applyOptions(context: Context, builder: GlideBuilder) {
        super.applyOptions(context, builder)
        builder.setDefaultRequestOptions(RequestOptions().format(DecodeFormat.PREFER_RGB_565))
    }
    ...
}

但与此同时你需要了解在一些情况下 RGB_565 可能不生效,甚至产生奇怪的显示效果。

  1. 若原图包含 alpha 透明度,则配置失效。
  2. RGB_565 配置与 RoundedCorners/CircleCrop 变换同时使用时,配置失效。
  3. RGB_565 与一些包含硬件绘制指令的变换同时使用时,配置失效或显示异常。

1/2 比较好理解,对于3,常见的就是高斯模糊效果(毛玻璃),该效果目前是通过 RenderScript 相关API,此 API 要求应用视图必须开启硬件加速。在此情况下,同时开启 RGB_565 配置会导致出现异常,可能会返回一个空的位图,在部分设备上可能会返回一个异常的模糊效果。

图片降质与内存优化没有必然关联

根据以上 Glide 对图片的缩放处理可知,默认情况下一张图片占用的最大内存只与控件宽/高和解码格式有关。

在整个图片处理的过程中,图片降质只是减少图片的下载速度,对内存占用并无直接影响。


为了帮助到大家更好的掌握好 开源框架相关知识点,这准备了 Android 开源框架的学习手册↓↓↓

有需要的可以复制下方链接,传送直达!!!
https://qr21.cn/CaZQLo?BIZ=ECOMMERCE

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/125363.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

隧道技术的三种应用场景(IPv6,多播,VPN)

目录 1.IPv6的隧道技术 2.多播路由选择 (1)洪泛 (2)隧道技术 (3)基于核心的发现技术 3.隧道技术实现&#xff08;VPN&#xff09;虚拟专用网 1.IPv6的隧道技术 IPv6与IPv4的过渡技术中包含了IPv6的隧道技术&#xff1a; http://t.csdnimg.cn/wuvXY 2.多播路由选择 转发…

通达OA V12 引入thinkphp5.1框架,读取OA的.ini文件

通达OA V12 引入thinkphp5.1框架&#xff0c;读取OA的.ini文件 内容绝对原创&#xff0c;希望对您有帮助。您的打赏&#xff0c;是让我持续更新的牛奶和面包 找到ini文件的绝对路径。$path“”;使用parse_ini_file($path,true,INI_SCANNER_RAW)&#xff0c;读取ini文件。 代码如…

【Python3】【力扣题】232. 用栈实现队列

【力扣题】题目描述&#xff1a; 栈&#xff1a;线性集合。后进先出。 队列&#xff1a;线性集合。先进先出。 【Python3】代码&#xff1a; 解题思路&#xff1a;两个栈&#xff0c;一个入队的栈&#xff0c;一个出队的栈。出栈时&#xff0c;若出队的栈为空&#xff0c;才将…

Unreal UnLua + Lua Protobuf

Unreal UnLua Lua Protobuf https://protobuf.dev/ protobuf wire format&#xff1a;pb 编译到底层的数据协议 https://github.com/starwing/lua-protobuf/blob/master/README.zh.md buffer 处理 lua string 可以当 buffer 用&#xff0c;# len 不会遇到 0 截断&#xf…

【Word自定义配置,超简单,图文并茂】自定义Word中的默认配置,比如标题大小与颜色(参考科研作图配色),正文字体等

▚ 01 自定义样式Styles中的默认标题模板 自定义标题的显示效果&#xff0c;如下图所示&#xff1a; 1.1 自定义标题的模板Normal.dotm 1.1.1 选择所需修改的标题 新建一个空白Word文档&#xff0c;依次选择菜单栏的开始Home&#xff0c;样式Styles&#xff0c;鼠标右键选择…

[工业自动化-8]:西门子S7-15xxx编程 - PLC主站 - CPU模块

目录 前言&#xff1a; 一、概述 二、CPU操作和显示 三、安装 四、CPU的选择 前言&#xff1a; 一、概述 西门子S7-1500系列是一系列高性能工业自动化控制器&#xff0c;广泛应用于制造业、自动化生产、物流等领域。这个系列的控制器是设计用来满足高性能、高效能要求的复…

代码审计(某个人发卡系统V6.0(php))

一、前台漏洞 1、前台文件包含漏洞(如果开启了gbc,可远程包含) 注入点1&#xff1a; tyid没任何过滤&#xff0c;存在注入 payload:http://faka.com/ajax.php?actselgo POST传参: tyid1/**/union/**/select/**/*/**/from/**/if_km/**/limit/**/0,1# 注入点2: 也是没加任何…

ElasticSearch的文档、字段、映射和高级查询

1. 文档&#xff08;Document&#xff09; 在ES中一个文档是一个可被索引的基础信息单元&#xff0c;也就是一条数据 比如&#xff1a;你可以拥有某一个客户的文档&#xff0c;某一个产品的一个文档&#xff0c;当然&#xff0c;也可以拥有某个订单的一个文档。文档以JSON&…

Code Review最佳实践

Code Review最佳实践 Code Review 我一直认为Code Review&#xff08;代码审查&#xff09;是软件开发中的最佳实践之一&#xff0c;可以有效提高整体代码质量&#xff0c;及时发现代码中可能存在的问题。包括像Google、微软这些公司&#xff0c;Code Review都是基本要求&…

系统的讲解 - PHP 接口签名验证

概览 工作中&#xff0c;我们时刻都会和接口打交道&#xff0c;有的是调取他人的接口&#xff0c;有的是为他人提供接口&#xff0c;在这过程中肯定都离不开签名验证。 在设计签名验证的时候&#xff0c;一定要满足以下几点&#xff1a; 可变性&#xff1a;每次的签名必须是不…

线性代数(六)| 二次型 标准型转换 正定二次型 正定矩阵

文章目录 1. 二次型化为标准型1.1 正交变换法1.2 配方法 2 . 正定二次型与正定矩阵 1. 二次型化为标准型 和第五章有什么样的联系 首先上一章我们说过对于对称矩阵&#xff0c;一定存在一个正交矩阵Q&#xff0c;使得$Q^{-1}AQB $ B为对角矩阵 那么这一章中&#xff0c;我们…

PyTorch语音识别的理论基础——MFCC

在语音识别研究领域&#xff0c;音频特征的选择至关重要。本书大部分内容中都在使用一种非常成功的音频特征—梅尔频率倒谱系数&#xff08;Mel-Frequency Cepstrum Coefficient&#xff0c;MFCC&#xff09;。 MFCC特征的成功很大程度上得益于心理声学的研究成果&#xff0c;…

字符三角形-第10届蓝桥杯国赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第6讲。 字符三角形&#x…

07、SpringBoot+微信支付 -->处理超时订单(定时查询、核实微信支付平台的订单、调用微信支付平台查单接口、更新本地订单状态、记录支付日志)

目录 Native 支付处理超时订单定时的讲解需求分析代码定时任务&#xff1a;WxPayTask定时查询的方法&#xff1a;核实订单状态等操作 &#xff1a;WxPayServiceImpl查单接口方法&#xff1a;queryOrder更新本地订单状态&#xff1a;updateStatusByOrderNo记录支付日志&#xff…

TCP网络编程

一)TCP Socket介绍: 1)TCP和UDP有着很大的不同&#xff0c;TCP想要进行网络通信的话首先需要通信双方建立连接以后然后才可以进行通信&#xff0c;TCP进行网络编程的方式和文件中的读写字节流类似&#xff0c;是以字节为单位的流进行传输 2)针对于TCP的套接字来说&#xff0c;J…

ubuntu 火焰图脚本

环境ubuntu1804 x86_64 #!/bin/bash if [ "$2_" "_" ];thenecho "usage ./fire.sh oncpu/offcpu pid"exit fiif [ "$1_" "oncpu_" ];thensudo perf record -F 99 -p $2 -g -- sleep 10syncsudo perf script > out.pe…

144. 二叉树的前序遍历

描述 : 给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 题目 : LeetCode 二叉树的前序遍历 : 144. 二叉树的前序遍历 分析 : 我们先选一个最小的子树: 先判断5节点不是null之后把5添加到集合里 , 再把5的左节点递归 , 判断7节点不是null把7添加到集合中 …

Git简介和安装

一&#xff0c;Git简介 Git 是一个分布式版本控制工具&#xff0c;通常用来对软件开发过程中的源代码文件进行管理。通过Git 仓库来存储和管理这些文件&#xff0c;Git 仓库分为两种&#xff1a; 本地仓库&#xff1a;开发人员自己电脑上的 Git 仓库 远程仓库&#xff1a;远程…

云MES优势有哪些?

云MES可以说是近几年来的行业焦点&#xff0c;云MES使用公共云提供商(如华为云、腾讯云、阿里云等)的全球数据中心中的广泛硬件和基础架构&#xff0c;在公共云中的Internet上运行&#xff0c;线下线上互联互通&#xff0c;有效帮助企业实现生产数字化智能化。 近几年来云服务…

Linux 多线程编程详解

目录 为什么要使用多线程 线程概念 线程的标识 pthread_t 线程的创建 向线程传入参数 线程的退出与回收 线程主动退出 线程被动退出 线程资源回收(阻塞方式) 线程资源回收(非阻塞方式) 为什么要使用多线程 在编写代码时&#xff0c;是否会遇到以下的场景会感觉到难以…