Android---Bitmap详解
每一个 Android App 中都会使用到 Bitmap,它也是程序中内存消耗的大户,当 Bitmap 使用内存超过可用空间,则会报 OOM。
Bitmap 占用内存分析
Bitmap 用来描述一张图片的长、宽、颜色等信息,可用使用 BitmapFactory 来将某一路径下的图片解析为 Bitmap 对象。
当一张图片加载到内存后,具体需要占用多大的内存?
getAllocationByteCount 探索
我们可用通过 bitmap.getAllocationByteCount() 方法获取 Bitmap 占用的字节大小。比如以下代码
上图中,rodma 是保存在 res.drawable 目录下的一张 600x600 、大小为 65KB 的图片。 打印结果如下
默认情况下,BitmapFactory 使用的 Bitmap.Config.ARGB_8888 的存储方式来加载图片内容。在这种存储模式下,每个像素占用4个字节(ARGB_8888代表A占8bit,R占8bit,G占8bit,B占8bit)。因此,上面图片 rodman 的大小可用使用如下公式计算:
宽 x 高 x 4 = 600 x 600 x 4 = 1440000 Byte
所以,一张图片占用的内存大小的计算公式为:宽 x 高 x 单位像素所占字节数。
屏幕自适应
在保证代码不修改的前提下,将图片 rodman 移动(注意不是拷贝)到 res/drawable-hpdi 目录下。重新运行代码,则打印日志如下:
可用看出,我们只是移动了图片的位置,Bitmap 所占用的内存竟然上涨了,这是为什么呢?
BitmapFactory 在解析图片的过程中,会根据当前设备屏幕密度和图片所在的 drawable 目录来做一个对比,根据这个对比值进行缩放操作。
具体公式为:
缩放比例 scale = 当前设备屏幕密度 / 图片所在 drawable 目录对应的屏幕密度。
Bitmap 实际大小 = (宽 * scale) x (高 * scale) x 单位像素所占字节数
在 Android 中,各个 drawable 目录对应的屏幕密度
运行的设备是 Nexus4,屏幕密度为320,如果将 rodman 放到 drawable-hdpi 目录下,最终的计算公式如下
Rodman 实际占用内存大小 = [600 * (320 / 240)] * [600 * (320/240)] * 4 = 2560000
assets 中的图片大小
Android 中的图片不仅可以保存在 drawable 目录中,还可以保存在 assets 目录下,然后通过 AssetManager 获取图片的输入流。
这种方式加载生成的 Bitmap 是多大呢?
同样是上面的 rodman.png,这次把它放到 assets 目录中,然后用如下代码加载
最终打印结果为
可以看出加载 assets 目录中的图片,系统并不会对其进行缩放操作。
Bitmap 加载优化
一张 65Kb 大小的图片被加载到内存后,占用了 2560000 个字节,也就是 2.5M 左右。适当时候,需要对加载的图片进行缩略优化。
修改图片加载的 Config
使用占用存储空间少的占用方式,可以快速有效降低图片占用内存。比如通过 BitmapFactory.Options.inPreferredConfig 选项,将存储方式设置为 Bitmap.Config.RGB_565。在这种存储方式中,一个像素占用2个字节。所以最终占用内存直接减半。
打印结果如下
Bitmap 复用
场景描述:如果在 Android 某个页面创建很多个 Bitmap,比如有两张图片 A 和 B,通过点击某一按钮,需要在 ImageView 上切换显示这两张图片。可以使用以下代码实现上述效果
每次调用 switchImage() 切换图片时,都需要通过 BitmapFactory 创建一个新的 Bitmap 对象,当方法执行完之后,这个 Bitmap 对象又会被 GC 回收。这就造成不断创建和销毁比较大的内存对象,从而导致频繁 GC,即内存抖动。
像 Android App 这种最终面向用户的产品,如何因为频繁的 GC,造成 UI 界面卡顿,还是会影响到用户体验的。可以在 Android Studio Profiler 中查看内存情况,多次切换图片后,显示的效果如下:
使用 Options.inBitmap 优化
实际上经过第一次显示之后,内存中已经存在了一个 Bitmap 对象,每次切换图片只是显示的内容不一样。我们可以重复利用已经占用内存空间的 Bitmap,具体做法就是使用 Options.inBitmap 参数,将 getBitmap() 方法修改如下
图中1处创建一个可以用来复用的 Biamap 对象;图中2处将 options.inBitmap 复制为之前创建的 reuseBitmap 对象,从而避免重新分配内存。
重新运行代码,并查看 Profiler 中的内存情况。可以发现,不管我们切换图片多少次,内存占用始终处于一个水平线状态。
注意:在上述 getBitmap 方法中,复用 inBitmap 之前,需要调用 canUseForInBitmap 方法来判断 reuseBitmap 是否可以被复用。这是因为 Bitmap 的复用有一定的限制
在 Android 4.4 版本之前,只能重用相同大小的 Bitmap 内存区域;
4.4 之后可以重用任何 Bitmap 的内存区域,只要这块内存区域比要分配内存的 bitmap 大就可以
canUseForInBitmap() 方法的具体内容如下
在上面复用 Bitmap 的代码中,除了 inBitmap 参数之外,还将 options.inMutable 置为 true。这里如果不置为 true 的话,BitmapFactory 将不会重复利用 Bitmap 内存,并输出相应 warning 日志,如下所示
BitmapRegionDecoder 图片分片显示
想要加载显示的图片很大或者很长,比如手机滚动截图功能生成的图片。针对这种情况,在不压缩图片的前提下,不建议一次性将整张图片加载到内存。而是采用分片加载的方式来显示图片部分内容,然后依据手势操作,放大缩小或者移动图片显示区域。
图片分片加载显示主要是使用 Android SDK 中的 BitmapRegionDecoder 来实现。
BitmapRegionDecoder 基本使用
首先需要使用 BitmapRegionDecoder 将图片加载到内存。图片可以以绝对路径、文件描述符、输入流等方式传递给 BitmapRegionDecoder。如下代码所示
可以通过自定义 View,添加 touch 事件来动态地设置 Bitmap 需要显示的区域 Rect,具体实现网上已经有很多成熟的轮子可以直接使用,比如 LargteImaveView。张鸿洋先生有一篇比较详细文章对此进行介绍:Android 高清加载巨图方案 拒绝压缩图片
Bitmap 缓存
当需要在界面上同时展示一大堆图片的时候,比如 ListView、RecyclerView 等,由于用户不断地上下滑动,某个 Bitmap 可能会被短时间内加载并销毁多次,这种情况通过使用适当的缓存,可以有效地减缓 GC 频率保证图片加载效率,提高界面的响应速度和流程性。
最常用的缓存方式就是 LruCache,基本使用方式如下
图中1处之处 LruCache 的最大可用空间为 20M。当超过 20M 时,LruCache 会根据内部缓存策略将多余 Bitmap 移除;图中2处指定了插入 Bitmap 时的大小。当我们往 LruCache 中插入数据时,LruCache 并不知道每个 Bitmap 对象占用多大内存,因此需要我们手动指定,并且根据缓存数据类型的不同也会有不同的计算方式。
总结
Bitmap开发中的几个常见问题:
● 一张图片被加载成Bitmap后实际占用内存是多大;
● 通过Options.inBitmap可以实现Bitmap的复用,但是有一定的限制;
● 当界面需要展示多张图片,尤其是在列表视图中,可以考虑使用Bitmap缓存;
● 如果需要展示的图片过大,可以考虑使用分片加载的策略。
相关文章:

Android---Bitmap详解
每一个 Android App 中都会使用到 Bitmap,它也是程序中内存消耗的大户,当 Bitmap 使用内存超过可用空间,则会报 OOM。 Bitmap 占用内存分析 Bitmap 用来描述一张图片的长、宽、颜色等信息,可用使用 BitmapFactory 来将某一路径下…...

设计高信度和效度的问卷:关键要点与技巧
设计调查问卷是任何研究过程中至关重要的一部分,无论是出于学术目的还是商业目的。调查是用于收集数据的常用工具,它们可以为消费者行为、意见、客户满意度和其他重要因素提供有价值的见解。然而,调查的可靠性和有效性对于确保收集的数据准确…...

从工厂到社会:探索如何应用设计模式工厂模式
文章目录 🌟 将设计模式工厂模式运用到社会当中🍊 工厂模式在社会中的应用🎉 工厂🎉 餐厅🎉 运输 🍊 工厂模式的优势🎉 代码简洁🎉 扩展性强🎉 便于维护和管理 …...

slice()和splice()用法
前言: slice()和splice()都是JavaScript中数组的方法,但是它们的用法有所不同。接下来让我们详细分析一下他们的不同之处。 slice(): slice()方法返回一个数组的一部分,不会改变原始数组,而是返回一个新数组。 语法…...

基于windows10的pytorch环境部署及yolov8的安装及测试
第一章 pytorch环境部署留念 第一步:下载安装anaconda 官网地址 (也可以到清华大学开源软件镜像站下载:https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/) 我安装的是下面这个,一通下一步就完事儿。 第二步…...

面试算法40:矩阵中的最大矩形
题目 请在一个由0、1组成的矩阵中找出最大的只包含1的矩形并输出它的面积。例如,在图6.6的矩阵中,最大的只包含1的矩阵如阴影部分所示,它的面积是6。 分析 直方图是由排列在同一基线上的相邻柱子组成的图形。由于题目要求矩形中只包含数字…...

was下log4j设置日志不输出问题
was下log4j设置日志不输出问题 WAS 也是用的 commons-logging 日志框架 commons-logging 确定 LogFactory 实现的顺序是 从应用的 META-INF/services/org.apache.commons.logging.LogFactory 中获得 LogFactory 实现从系统环境中获得 org.apache.commons.logging.LogFactory…...

小米14系列, OPPO Find N3安装谷歌服务框架,安装Play商店,Google
10月26号小米发布了新款手机小米14,那么很多大家需求问是否支持谷歌服务框架,是否支持Google Play商店gms。因为毕竟小米公司现在安装的系统是HyperOS澎湃OS。但是我拿到手机之后会发现还是开机初始界面会显示power by android,证明这一点他还是支持安装谷歌,包括最近一段时间发…...

Servlet 与Spring对比!
前言: Spring相关的框架知识,算是目前公司在用的前沿知识了,很重要!! 那么以Spring为基础的框架有几个? 以Spring为基础的框架包括若干模块,其中主要的有Spring Framework、Spring Boot、Spring…...

粤嵌实训医疗项目--day03(Vue + SpringBoot)
往期回顾 粤嵌实训医疗项目day02(Vue SpringBoot)-CSDN博客 粤嵌实训医疗项目--day01(VueSpringBoot)-CSDN博客 目录 一、SpringBoot AOP的使用 二、用户模块-注册功能(文件上传) 三、用户模块-注册实现…...

spark3.3.x处理excel数据
环境: spark3.3.x scala2.12.x 引用: spark-shell --jars spark-excel_2.12-3.3.1_0.18.5.jar 或项目里配置pom.xml <!-- https://mvnrepository.com/artifact/com.crealytics/spark-excel --> <dependency><groupId>com.crealytics</groupId><art…...

哪一个更好?Spring boot还是Node.js
前言 本篇文章有些与众不同,由于我自己手头有些关于这个主题的个人经验,受其启发写出此文。虽然SpringBoot和Node.js服务于很不一样的场景,但是这两个框架共性惊人。其实每种语言都有不计其数的框架,但仅仅一部分是真正卓越的。如…...

AD7321代码SPI接口模数转换连接DAC0832输出verilog
名称:AD7321代码12位ADC,SPI接口模数转换连接DAC0832输出 软件:QuartusII 语言:VHDL 代码功能: 使用VHDL语言编写代码,实现AD7321的控制,将模拟信号转换为数字信号,再经过处理后…...

JavaScript_Pig Game切换当前玩家
const current0El document.getElementById(current--0); const current1El document.getElementById(current--1); if (dice ! 1) {currentScore dice;current0El.textContent currentScore;} else {} });这是我们上个文章写的代码,这个代码明显是有问题的&…...

EtherNet Ip工业RFID读写器与欧姆龙PLC 配置示例说明
一、准备阶段 POE交换机欧姆龙PLC 支持EtherNet Ip协议CX-Programmer 9.5配置软件 二、配置读卡器 1、打开软件 2、选择网卡,如果多网卡的电脑请注意对应所接的网卡,网卡名一般为“Network adapter Realtek PCIe GBE Family” 3、点击“选择网卡”&…...

UE5简化打包大小
UE5.3默认空项目带初学者包的打包后1G多 简化思路: 1.不打包初学者包(或者创建时不包括初学者包,跳过第一条) 导航:ProjectSettings->Project->Packaging->Packaging->Advanced->List of maps to incl…...

ThinkPHP8学习笔记
ThinkPHP8官方文档地址:ThinkPHP官方手册 一、composer换源 1、查看 composer 配置的命令composer config -g -l 2、禁用默认源镜像命令composer config -g secure-http false 3、修改为阿里云镜像源composer config -g repo.packagist composer https://mirror…...

NSSCTF做题第9页(2)
[SWPUCTF 2022 新生赛]ez_1zpop <?php error_reporting(0); class dxg { function fmm() { return "nonono"; } } class lt { public $impohi; public $md51weclome; public $md52to NSS; function __construct() { $this-&…...

Rust笔记【1】
元组和解构语法 let tup : (i32, f64, u8) (666, 2.0, 1);let tup (666, 2.0, 1); let (x, y, z) tup;let x tup.0; let y tup.1; let z tup.2;数组类型 数组定义是方括号:[ ] 元组定义是小圆括号:( ) 结构体定义是大括号:{ }…...

代码随想录训练营day3:链表part1
理论 链表的增删操作时间复杂度O(1),查询时间复杂度O(n),因为要从头结点开始。使用场景和数据完全相反 链表的储存地址是不连续的。也和数组不同。 移除链表元素 利用虚拟头结点可以同意操作。不然删除头结点需要额外写。 记得返回的是虚拟头结点的next而不是虚拟头结点retu…...

Bootstrap的咖啡网站实例代码阅读笔记
目录 01-index.html的完整代码02-图片可以通过类 rounded-circle 设置为圆形显示03-<li class"nav-item mt-1 a">中,类mt-1是什么意思?类a又是什么意思?04-href"javascript:void(0);"是什么意思?05-类f…...

2021年06月 Python(二级)真题解析#中国电子学会#全国青少年软件编程等级考试
Python等级考试(1~6级)全部真题・点这里 一、单选题(共25题,每题2分,共50分) 第1题 执行下列代码后,运行结果是? seq[hello,good,morning] s*.join(seq) print(s)A: hello*good*m…...

FileWriter文件字符输出流
一.概念 以内存为基准,把内存中的数据以字符形式写出到文件中 二.构造器 public FileWriter(Filefile) 创建字节输出流管道与源文件对象接通 public FileWriter(String filepath) 创建字节输出流管道与源文件路径接通 public Filewriter(File file,boolean append) …...

Vue的八个基础命令及作用
1.v-text 作用:获取data数据, 设置标签的内容,以纯文本进行显示v-text 会覆盖 标签中的内容,如果想要拼接数据,可以直接在v-text中拼接如果拼接的是数字:直接使用 “”如果拼接的是字符串,需要使用与外部不同的引号进…...

Log日志详解分析
目录 1、log日志的用途2、log日志级别3、什么时候需要输出日志1. 系统启动参数、环境变量2. 异常捕获处3. 函数获得期望之外的结果时4. 关键操作 4、日志输出的内容5、 注意事项1. 日志信息不明确2. 特殊异常处理3. 日志输出顺序4. 临时调试日志 6、xml文件配置7、linux下查看日…...

【API篇】九、Flink的水位线
文章目录 1、Flink时间语义2、事件时间和窗口3、水位线4、水位线和窗口的工作原理 1、Flink时间语义 事件时间处理时间 举个例子就是,一条数据在23:59:59产生,在00:00:01被处理,前者为事件时间,后者为处理时间。 从Flink1.12版本…...

#经典论文 异质山坡的物理模型 2 有效导水率
Binley, A., Beven, K., & Elgy, J. (1989). A physically based model of heterogeneous hillslopes: 2. Effective hydraulic conductivities. Water Resources Research, 25(6), 1227–1233. https://doi.org/10.1029/WR025i006p01227 这篇论文指出, 每个输…...

Java面试题-Redis-第一天(Redis简单介绍)
目录 一、Redis是什么?优缺点? 二、Redis为什么这么快? 三、Redis相较于Memcached有哪些优势? 四、为什么使用Redis做缓存? 五、为什么要用Redis而不用map/guava做缓存? 六、Redis的常用场景有哪些&am…...

Java 生成和读取JSON文件
下面的demo当中 ,是将json文件放到了zip包当中。如果不需要,可以拿掉。 1、生成对象JSON文件 public static void crateJson() {try {String orcPath "D:\\doc\\ts_service_orchestration.json";// 对象集合或者对象都可以List<DataPO>…...

k8s-----26、细粒度权限管理 RBAC
0、导读 每一个用户对API资源进行操作都需要通经过以下三个步骤: 第一步:对客户端访问进行认证操作,确认是否具有访问k8s权限(也就是通过serviceaccount) token(共享秘钥) SSL(双向SSL认证) …通过任何一个认证即表示认证通过,进入下一步第二步:授权检查,确认是否对资源…...