Android 中一张图片占据的内存大小是如何计算
提问
阅读本篇之前, 先来想一些问题:
Q1: 一张 PNG 格式的图片, 图片文件大小为 55.8KB, 那么它加载进内存时所占的大小是多少?
Q2: 为什么有时候, 同一个 App,App 内的同个界面, 界面上同张图片, 但在不同设备上所耗内存却不一样?
Q3: 图片占用的内存大小公式: 图片分辨率 * 每个像素点大小, 这种说法正确吗, 或者严谨吗?
Q4: 优化图片的内存大小有哪些方向可以着手?
正文
在 Android 开发中, 经常需要对图片进行优化, 因为图片很容易耗尽内存. 那么, 就需要知道, 一张图片的大小是如何计算的, 当加载进内存中时, 占用的空间又是多少?
先来看张图片:
这是一张普通的 PNG 图片, 来看看它的具体信息:
图片的分辨率是 1080*452, 而我们在电脑上看到的这张 PNG 图片大小仅有 55.8KB, 那么问题来了:
我们看到的一张大小为 55.8KB 的 PNG 图片, 它在内存中占有的大小也是 55.8KB 吗?
理清这点蛮重要的, 因为碰到过有人说, 我一张图片就几 KB, 虽然界面上显示了上百张, 但为什么内存占用却这么高?
所以, 我们需要搞清楚一个概念: 我们在电脑上看到的 PNG 格式或者 jpg 格式的图片, PNG(jpg) 只是这张图片的容器, 它们是经过相对应的压缩算法将原图每个像素点信息转换用另一种数据格式表示, 以此达到压缩目的, 减少图片文件大小.
而当我们通过代码, 将这张图片加载进内存时, 会先解析图片文件本身的数据格式, 然后还原为位图, 也就是 Bitmap 对象, Bitmap 的大小取决于像素点的数据格式以及分辨率两者了.
所以, 一张 PNG 或者 jpg 格式的图片大小, 跟这张图片加载进内存所占用的大小完全是两回事. 你不能说, 我 jpg 图片也就 10KB, 那它就只占用 10KB 的内存空间, 这是不对的.
那么, 一张图片占用的内存空间大小究竟该如何计算?
末尾附上的一篇大神文章里讲得特别详细, 感兴趣可以看一看. 这里不打算讲这么专业, 还是按照我粗坯的理解来给大伙讲讲.
图片内存大小
网上很多文章都会介绍说, 计算一张图片占用的内存大小公式: 分辨率 * 每个像素点的大小.
这句话, 说对也对, 说不对也不对, 我只是觉得, 不结合场景来说的话, 直接就这样表达有点不严谨.
在 Android 原生的 Bitmap 操作中, 某些场景下, 图片被加载进内存时的分辨率会经过一层转换, 所以, 虽然最终图片大小的计算公式仍旧是分辨率 * 像素点大小, 但此时的分辨率已不是图片本身的分辨率了.
我们来做个实验, 分别从如下的几种考虑点相互组合的场景中, 加载同一张图片, 看一下占用的内存空间大小分别是多少:privatevoidloadResImage(ImageViewimageView){
BitmapFactory.Optionsoptions=newBitmapFactory.Options();
Bitmapbitmap=BitmapFactory.decodeResource(getResources(),R.drawable.weixin,options);
//Bitmap bitmap = BitmapFactory.decodeFile("mnt/sdcard/weixin.png", options);
imageView.setImageBitmap(bitmap);
Log.i("!!!!!!","bitmap:ByteCount ="+bitmap.getByteCount()+":::bitmap:AllocationByteCount ="+bitmap.getAllocationByteCount());
Log.i("!!!!!!","width:"+bitmap.getWidth()+":::height:"+bitmap.getHeight());
Log.i("!!!!!!","inDensity:"+options.inDensity+":::inTargetDensity:"+options.inTargetDensity);
Log.i("!!!!!!","imageview.width:"+imageView.getWidth()+":::imageview.height:"+imageView.getHeight());
}ALPHA_8--(1B)
RGB_565--(2B)
ARGB_4444--(2B)
ARGB_8888--(4B)
RGBA_F16--(8B)//fresco, 默认使用 ARGB_8888
Fresco.initialize(context,ImagePipelineConfig.newBuilder(context).setBitmapsConfig(Bitmap.Config.RGB_565).build());
//Glide, 不同版本, 像素点格式不一样
publicclassGlideConfigurationimplementsGlideModule{
@Override
publicvoidapplyOptions(Contextcontext,GlideBuilderbuilder){
builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
}
@Override
publicvoidregisterComponents(Contextcontext,Glideglide){
}
}
// 在 AndroidManifest.xml 中将 GlideModule 定义为 meta-data
//Picasso, 默认 ARGB_8888
Picasso.with(imageView.getContext()).load(url).config(Bitmap.Config.RGB_565).into(imageView);//fresco
ImageRequestBuilder.newBuilderWithSource(uri)
.setResizeOptions(newResizeOptions(500,500)).build()