您的位置首页>硬件>

Android如何梳理内存占用情况解决OOM问题

导读大家好,我是极客范的本期栏目编辑小友,现在为大家讲解Android如何梳理内存占用情况解决OOM问题问题。不同安卓版本处理图片内存的方式不同

大家好,我是极客范的本期栏目编辑小友,现在为大家讲解Android如何梳理内存占用情况解决OOM问题问题。

不同安卓版本处理图片内存的方式不同,使用不当会导致OOM。本文将向您展示如何整理内存使用情况,选择适合您的图片加载模式,并解决OOM问题。

一.背景

你知道吗

一张5.48MB、24位、宽高分别为4,896 * 6,528像素的静态图片被放置在Android项目目录下的不同文件夹RES/drawing able-[density]/下。占用的内存是多少?

用Glide加载一张5.48MB、宽高4896 * 6528像素的24位网络图片,占用了多少内存?

二、梳理概念。

在正式分析以下内容之前,我们先来看几个概念。

1.屏幕尺寸。

指屏幕的对角线长度,以英寸为单位,1英寸=2.54厘米。这个值是根据手机屏幕的长度和宽度,再利用勾股定理,就可以计算出斜边的长度。

2.每英寸屏幕像素。

即每英寸屏幕的像素数,英文缩写为ppi,每英寸的屏幕像素与屏幕大小和屏幕分辨率有关。屏幕密度越低,给定物理区域中的像素就越少。安卓屏幕密度分为六个通用密度组:ldpi(低)、mdpi(中)、hdpi(高)、xhdpi(超高)、xxhdpi(超高)和xxxhdpi(超超高)。

3.屏幕分辨率。

屏幕分辨率是指水平和垂直方向的像素数,单位为px,1px=1像素。比如我们常说的宽高像素是4896*6528。

以上三个概念是否模糊?我们可以看一下下面两张图片来阐明以上三个概念:

(图:分辨率计算公式)

下面分析一下,了解每英寸的屏幕像素很重要。

第三,屏幕密度(dpi)对应。

屏幕物理区域的像素数量,通常称为dpi(每英寸点数)。屏幕密度越低,给定物理区域中的像素就越少。安卓屏幕密度分为六个通用密度组:ldpi(低)、mdpi(中)、hdpi(高)、xhdpi(超高)、xxhdpi(超高)和xxxhdpi(超超高)。

六种一般密度遵循3:4: 6:8: 12:16的比例。

四.代码验证。

代码很简单,就是用一个ImageView包含一个背景图片,然后转换成Bitmap来查看内存大小。

布局文件ActIon _ main . XML

布局文件是一个ImageView控件,包含一个背景图片。

MainAcivity.java

安卓有一个特殊的文件夹res/drawable-nodpi/,里面的资源不会被放大或压缩。它按照原来的大小显示,我们也把测试资源放在这个文件夹里。

动词(verb的缩写)图片的记忆占用。

1.静态图片不区分文件夹内存占用。

还是以长宽高为4896*6528=31961088的图片为例,图片原尺寸为5.48M,图片资源放置在RES/drawing-nodpi/。这时,找到一部vivoX21手机,加载这张图片,占用内存127844352字节:

原始图片总像素数为31961088,为127844352。

byte好像没什么关系,但是真相是31961088*4=127844352(Byte),原始图片尺寸大小与最终的内存占用大小呈倍数的关系,所以在这里与内存占用大小有直接关系的就是原始图片尺寸大小(例如:480x800),道理我都懂,但是倍数关系是从哪里来的呢,这就要谈论到Bitmap的像素格式了。

Android系统支持4种格式的像素格式,源码在Bitmap.Config中:

为了保证图片质量,官方默认使用ARGB_8888格式,导致图片的每个像素会占用4个Byte大小,所以demo里面的图片占用内存大小就是像素总数*像素格式,就是384000*4=1536000(Byte),这个时候应该有点成就感了,可以帮助你解决一部分实际项目问题了。

2、静态图片区分文件夹内存占用现象

(1)静态图片区分文件夹在X21(Android8.0)上的内存占用

那么问题又来了,放在res/drawable-nodpi/文件夹下没问题,放在其他文件夹下呢?因为我们要适配不同的机器。

仍然以vivoX21举例,x21的目标图片文件夹是res/drawable-xxdpi/,屏幕密度480dpi。

看一下这个图片放在不同的文件夹下面,内存占用情况,单位:M。

可以看到,

对于分辨率为res/drawable-hdpi/、res/drawable-xhdpi/、res/drawable-xxdpi/三个分辨率来说,图片占据内存基本是一致的,Java层内存没有消耗,而是消耗了naTIve内存。

res/drawable-xxxdpi/分辨率下面的图片,占据内存是最高的,naTIve占据了200M。

(2)所有的机器,内存占用都是这个规律吗

或许你有这个疑问:

为什么在不同的文件夹下面,图片占据的内存资源基本一致,有的时候却发现不同文件夹下面,内存占据又是不一样的?

在回答这个问题前,你要搞清楚,google在图片加载时候,不同的Android版本,做了naTIve堆栈和Java堆栈的区分。

这里也有个有意思的现象,在Android4.4到Android8.0以下的机器,当你把这个图片放在不同的文件夹下面时,图片占据的内存是不一样的,那是因为图片内存的加载,是在Java堆栈,所以你可能会遇到Java层面的OOM。

1AndroidRuntime:java.lang.RuntimeException:Canvas:tryingtodrawtoolarge(127844352bytes)bitmap.

8.0之后的内存分配是在native,Java层的bitmap创建之后,实际上像素内存的分配是在native层直接调用calloc,所以其像素分配的是在nativeheap上,这也是为什么8.0之后的Bitmap消耗内存可以无限增长,直到耗尽系统内存,也不会提示JavaOOM的原因。

3、网络图片加载内存占用现象

(1)Glide加载图片的方法

glide加载图片资源的方式有两个:

无回调,使用如下方式加载

Glide.with(context)

.load(url)

.apply(requestOptions.override(width,height))

.into(imageView);

有回调,使用下面加载方式,区别在into传入simpleTarget,而不是imageview

Glide.with(context)

.asBitmap()

.load(url)

.apply(requestOptions)

.into(simpleTarget);

其中的simpleTarget有两种定义方式:

传入宽、高参数,且大于0

1simpleTarget=newSimpleTarget(width,height){}

宽、高都为0

1simpleTarget=newSimpleTarget(){}

(2)SimpleTarget使用错误带来的问题

A和B的区别

区别就在于,当你传入了宽高的时候,图片就按照你传入的大小,缓存到了内存(Glide更多级存储大小此处不讨论)。当你不设置宽、高的时候,图片就按照原始的像素大小进行了缓存。

这是因为加载网络图片的时候,我们经常不知道宽、高是多少,我们设置本地资源imageview像素的时候,使用了wrap_content或者match_content,不确定最终的宽高,所以我们选择传入width=0,height=0,使用glide下载好图片后,再去做对应的设置。

为什么我们一般情况下感受不到A、B的差异

这是因为,网络图片也好、本地图片也好,像素都不会太大,以像素类型为RGB_8888为例,一个1920*1080的图片,在内存占据内存为1920*1080*4Byte=829440Byte=7.9M。

此时设置宽、高(正常也就设置个几十dp)与不设置宽高,区别并不大。

崩溃来了

104-2717:39:53.15431269-31269/?E/art:ThrowingOutOfMemoryError“Failedtoallocatea227278860byteallocationwith1048576freebytesand126MBuntilOOM”

为什么崩溃?

因为本地的一张图片大小虽然为5.48M,像素为width=4896height=6528,但是在内存占据大小为4896*6528*4=127844352byte=120M。这个内存足以使官网app在本来使用内存就高的情况下闪退。

看一下加载这个本地图片时的内存情况,从320M到548M,飙升228M(还有后台事件带来内存波动,引起闪退的根本原因是Graphics的内存飙升)。

怎么解决崩溃?

想办法去掉simpleTarget的B定义方法

如果你不知道需要现实的资源宽高是多少,设置下面这个参数,这样就以当前屏幕宽、高作为最高显示像素,downsample设置为DownsampleStrategy.AT_MOST。

这个表示:

当你的资源原始尺寸大于width*height(屏幕宽、高像素)时,以width*height为准。

当你的资源原始尺寸小于width*height时,以原始尺寸为准。

width*height作为图片保存到内存时的最大像素值。

闪退问题同样解决,此时内存使用情况从290M到340M,增加50M(还有后台事件带来内存波动)。

六、总结

不同分辨率的静态资源图片放在不同的文件夹下面,不要随便放,会引起内存的异常。编辑:hfy

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。