转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/107951273
本文出自【赵彦军的博客】

做Android 6年来,一直都没有对 Bitmap 做过深入研究。最近的工作需要,我认真的研究了一下Bitmap , 了却了多年的心愿。

本次研究的东西比较多,建议先收藏,再看。

补充:关于 bitmap 压缩问题,可以看看 Android Bitmap 研究与思考(中篇)

文章目录

  • 一:Bitmap 是什么?
  • 二:bitmap 到底占用多少内存?
    • 内存计算
  • 三:Bitmap 尺寸怎么算?
    • 实验1:
    • 实验2
    • 实验3
    • 实验4
  • 四:什么是 DPI ?
  • 五:什么是 PPI ?
  • 六:Android 系统 DPI 分级
  • 七:不同密度下的图片内存占用怎么计算
    • 实验1:把图片放在 xxhdpi
    • 实验2:把图片放在 xhdpi
    • 实验3:把图片放在 hdpi
    • 实验4:把图片放在 xxxhdpi

一:Bitmap 是什么?

从字面的意思上可以理解为 位图

在Android中是一种存储像素的数据结构,通过这个对象可以得到一系列的图像属性。还可以对图像进行旋转,切割,放大,缩小等操作。

我画了一张像素图,大家理解一下。

我们一般说说的手机分辨率 1080 * 1920 , 就代表手机屏幕横向是 1080 个像素点,竖向是 1920 个像素点,整个手机的像素点是两者相乘为 2073600 个像素点。

从代码上来讲 ,Bitmap 是一个 final 类,实现了 Parcelable 接口,因此不能被继承。Bitmap只有一个构造方法,且该构造方法是私有的,所以无法通过 new 的方式来建一个 Bitmap

在 Android 中,bitmap 只是一个存储像素信息的对象,是内存虚构出来的

二:bitmap 到底占用多少内存?

首先我们先来看看怎么创建一个 bitmap ,上面我们已经提到过,无法通过 new 的方式创建Bitmap 对象 。 幸运的是 Bitmap 提供了多个静态方法。

可以看到除了createBitmap(Bitmap src)createBitmap( Picture source) 这两个方法外,其他的方法都是需要一个 Config 参数。

Bitmap.Config google官方描述为:

Possible bitmap configurations. A bitmap configuration describes how pixels are stored. This affects the quality (color depth) as well as the ability to display transparent/translucent colors.

Bitmap.Config 描述了像素的存储方式,这会影响质量(颜色深度)以及显示透明/半透明颜色的能力。在Bitmap.Config文档中我们看到四个枚举变量。

 public enum Config {ALPHA_8     (1),/*** Each pixel is stored on 2 bytes and only the RGB channels are* encoded: red is stored with 5 bits of precision (32 possible* values), green is stored with 6 bits of precision (64 possible* values) and blue is stored with 5 bits of precision.** This configuration can produce slight visual artifacts depending* on the configuration of the source. For instance, without* dithering, the result might show a greenish tint. To get better* results dithering should be applied.** This configuration may be useful when using opaque bitmaps* that do not require high color fidelity.** <p>Use this formula to pack into 16 bits:</p>* <pre class="prettyprint">* short color = (R & 0x1f) << 11 | (G & 0x3f) << 5 | (B & 0x1f);* </pre>*/RGB_565     (3),/*** Each pixel is stored on 2 bytes. The three RGB color channels* and the alpha channel (translucency) are stored with a 4 bits* precision (16 possible values.)** This configuration is mostly useful if the application needs* to store translucency information but also needs to save* memory.** It is recommended to use {@link #ARGB_8888} instead of this* configuration.** Note: as of {@link android.os.Build.VERSION_CODES#KITKAT},* any bitmap created with this configuration will be created* using {@link #ARGB_8888} instead.** @deprecated Because of the poor quality of this configuration,*             it is advised to use {@link #ARGB_8888} instead.*/@DeprecatedARGB_4444   (4),/*** Each pixel is stored on 4 bytes. Each channel (RGB and alpha* for translucency) is stored with 8 bits of precision (256* possible values.)** This configuration is very flexible and offers the best* quality. It should be used whenever possible.** <p>Use this formula to pack into 32 bits:</p>* <pre class="prettyprint">* int color = (A & 0xff) << 24 | (B & 0xff) << 16 | (G & 0xff) << 8 | (R & 0xff);* </pre>*/ARGB_8888   (5),/*** Each pixels is stored on 8 bytes. Each channel (RGB and alpha* for translucency) is stored as a* {@link android.util.Half half-precision floating point value}.** This configuration is particularly suited for wide-gamut and* HDR content.** <p>Use this formula to pack into 64 bits:</p>* <pre class="prettyprint">* long color = (A & 0xffff) << 48 | (B & 0xffff) << 32 | (G & 0xffff) << 16 | (R & 0xffff);* </pre>*/RGBA_F16    (6),/*** Special configuration, when bitmap is stored only in graphic memory.* Bitmaps in this configuration are always immutable.** It is optimal for cases, when the only operation with the bitmap is to draw it on a* screen.*/HARDWARE    (7);final int nativeInt;private static Config sConfigs[] = {null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888, RGBA_F16, HARDWARE};Config(int ni) {this.nativeInt = ni;}static Config nativeToConfig(int ni) {return sConfigs[ni];}}
  • ALPHA_8 :
每个像素都存储为单个半透明(alpha)通道。
这对于例如有效存储蒙版非常有用。
不存储颜色信息。
使用此配置,每个像素需要1个字节的内存。

说白了就是:这种模式每个像素只存储一个透明度值,不会存储其他颜色信息。所以我们在创建蒙版的时候,推荐使用这种模式。每个像素占 8 位,也就是 1 个字节。

  • ARGB_4444 :

即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位 , 也就是每个像素占 2 个字节。

  • ARGB_8888 :

即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位, 也就是一个像素占 4 个字节。

  • RGB_565 :

即R=5,G=6,B=5,没有透明度,那么一个像素点占5+6+5=16位, 也就是一个像素占 2 个字节。

内存计算

我们知道了,每种模式下,每个像素所占用的内存,就能很清楚的算清楚,每个bitmap 所占用的内存。比如:

Bitmap bitmap =  Bitmap.createBitmap(1024,1024, Bitmap.Config.ARGB_8888);

我们创建了一个 bitmap ,宽 1024 个像素,高 1024 个像素,模式 ARGB_8888 。所以我们很容易算出:

  • 这个bitmap 总共像素总量为:1024 x 1024
  • 每个像素占用的内存为 4 个 byte
  • 整个bitmap 占用内存为 :1024 x 1024 x 4 , 单位字节 byte 。 换算为 (1024 x 4 ) kb = 4 M

最后算出这个bigmap 占用内存为 4 M 。

总结:

  • 在 bitmap 存储模式相同的情况下,尺寸越大,占用内存越大。因为尺寸越大,bigmap 总像素点越多。

  • 在 bitmap 尺寸相同的情况下,ARGB_8888 占用内存 > ARGB_4444 占用内存 = RGB_565 占用内存 > ARGB_4444 占用内存。

三:Bitmap 尺寸怎么算?

在第二部分,我们知道了 bitmap 占用的内存 = bitmap 总像素个数 x 单个像素占用的内存

其中,单个像素占用的内存由存储模式决定,比如 Config.ARGB_8888
而 bitmap 的总像素 = bitmap 宽度 x bitmap 高度

现在我们要弄清楚 bitmap 尺寸怎么计算?

首先上一段代码,计算ImageView中加载图片的具体尺寸和内存占用大小。

import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.widget.ImageView;/*** @author yanjun.zhao* @time 2020/8/13 7:51 PM* @desc*/
class Util {/**** 计算ImageView中加载图片的具体尺寸和内存占用大小* @param imageView*/public static void calculateBitmapInfo(ImageView imageView) {Drawable drawable = imageView.getDrawable();if (drawable != null) {BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;Bitmap bitmap = bitmapDrawable.getBitmap();Log.d("bitmap--", " bitmap width = " + bitmap.getWidth() + " bitmap height = " + bitmap.getHeight());Log.d("bitmap--", " memory usage = " + bitmap.getAllocationByteCount());/**bitmap.getByteCount()方法不再使用*/} else {Log.d("bitmap--", "drawable is null!");}}
}

实验1:

我们做个试验,现在布局中定义个 ImageView , 尺寸是 100 dp x 100 dp

 <ImageViewandroid:id="@+id/image"android:layout_width="100dp"android:layout_height="100dp" />

给 imageView 设置一个图片,名字是 image.png , 尺寸是 300 x 300 。

 <ImageViewandroid:id="@+id/image"android:src="@drawable/image"android:layout_width="100dp"android:layout_height="100dp" />

然后输出 bitmap 宽高和占用内存。

import androidx.appcompat.app.AppCompatActivity
import com.example.myview.util.Utils
import kotlinx.android.synthetic.main.activity_main.*class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)//计算bitmap 宽高和占用内存Util.calculateBitmapInfo(image)}
}

在 红米4 手机上输出:

bitmap--:  bitmap width = 1200 bitmap height = 1200
bitmap--:  memory usage = 5760000

在我的杂牌手机上输出:

bitmap--:  bitmap width = 600 bitmap height = 600
bitmap--:  memory usage = 1440000

至此,我们可以得出结论:

  • 使用相同尺寸的图片放在 drawable 目录下,用 ImageView 加载出来。在不同的手机上,获取的 bitmap 宽高是不一样的,bitmap 所占用的内存也是不一样的。

查阅 ImageView 源码,发现 ImageView中图片是以 Bitmap 形式保存在内存中;查阅 Bitmap 源码,发现图像在内存中的实际 bitmap 尺寸和图像的原始尺寸(withPixel * heightPixel),资源文件像素密度(sourcedensity)以及目标手机的像素密度(targetDensity)有密不可分的关系。


其中 sourceDensity>>1 部分是为了对 targetDensity/sourceDensity 进行四舍五入

综上,

  • 结论1: ImageView中显示图像对内存的占用与原始图像尺寸,资源文件的dpi,以及实际设备的dpi有密切的关系,与图像在UI上实际显示的尺寸无关。只放置单 dpi 资源文件,对不同设备加载过程中的内存占用无影响。

实验2

测试方案:将尺寸为图片A(尺寸60*60 大小2.02K)放入drawable和drawable-xxhdpi文件夹,图片显示尺寸采用wrap_content,分别用mate 9手机进行测试;

测试结果:内存占用分别为129600Byte和14400Byte,图片在ImageView中的bitmap尺寸为180 * 180和 60 * 60;

结果分析:相同的图片放在不同的 drawable 目录下,bitmap 宽高不一样,bitmap 占用的内存也不一样。内存占用与图片的原始尺寸没有关系,与 bitmap尺寸有密切的关系。

实验3

测试方案:将尺寸为图片A(尺寸60 x 60 ,大小2.02K)放入drawable-xxhdpi文件夹,图片显示尺寸设置为30dp * 30dp和 60dp * 60dp,分别用mate 9手机进行测试;

测试结果:内存占用均为14400Byte,bitmap尺寸均为60*60;

结果分析:说明内存占用与图片的实际显示尺寸没有关系。

实验4

测试方案:将尺寸为图片A(尺寸 60 x 60 大小 2.02K ),图片B(尺寸 60 x 60 ,大小1.63K),将图片均放入drawable-xxhdpi文件夹,图片显示尺寸采用wrap_content,用华为mate 9(xxhdpi)手机进行测试;

测试结果:二者内存占用均为14400Byte,bitmap尺寸为 60 x 60;

结果分析:说明内存占用单独与图片原始大小没有关系。

四:什么是 DPI ?

先放一个维基百科 链接 DPI

DPI(英语:Dots Per Inch,每英寸点数)是一个量度单位,用于点阵数字图像,意思是指每一英寸长度中,取样或可显示或输出点的数目。如:打印机输出可达600DPI的分辨率,表示打印机可以在每一平方英寸的面积中可以输出600X600=360000个输出点。

打印机所设置之分辨率的DPI值越高,印出的图像会越精细。打印机通常可以调校分辨率。例如撞针打印机,分辨率通常是60至90 DPI。喷墨打印机则可达1200 DPI,甚至9600 DPI。激光打印机则有600至1200 DPI。

一般显示器为96 DPI,印刷所需位图的DPI数则视印刷网线数而定。一般150线印刷质量需要350 DPI的位图。而这里的D(dot)就是像素(pixel)。

五:什么是 PPI ?

每英寸像素(英语:Pixels Per Inch,缩写:PPI),又被称为像素密度,是一个表示打印图像或显示器单位面积上像素数量的指数。一般用来计量电脑显示器,电视机和手持电子设备屏幕的精细程度。通常情况下,每英寸像素值越高,屏幕能显示的图像也越精细。

电脑与手机屏幕的每英寸像素值取决于尺寸和分辨率,通常指的就是每英寸上的像素点数。同样的一台显示器,如果分辨率设置的不同,像素点数也不同。分辨率越高,每英寸像素值也越高。

六:Android 系统 DPI 分级

根据屏幕每英寸像素值的不同,Android系统的开发者将平板电脑和手机的屏幕分成五类:

随着屏幕技术的发展,出现了比 xxhdpi 更高密度的屏幕 xxxhdpi

名称 显示等级 每英寸像素
XXXHDPI 超超高像素密度 每英寸大约640像素 (192 x 192 px )

七:不同密度下的图片内存占用怎么计算

往下阅读前,可以首先阅读 郭霖的博客:
Android drawable微技巧,你所不知道的drawable的那些细节

在郭神的文章中,讨论了图片放在不同的 dpi 目录下,ImageView 会被缩放,ImageView 的宽高会改变。但是文章中没有谈论的是 ImageView 宽高改变了,ImageView 中的 bitmap 宽高会不会改变,因为 bitmap 的宽高会直接影响到所占用内存的大小。

我们先来做个实验,首先计算出当前手机屏幕密度,计算方法是:

var phoneDpi = resources.displayMetrics.densityDpi

计算的结果是 480 ,可以看出来,我的手机屏幕密是 超高像素密度 , 对应的是 xxhdpi

我现在有一张图片 image.png,尺寸是300 x 300

条件:ImageView 的宽高是 wrap_content

 <ImageViewandroid:id="@+id/image"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/image" />

实验1:把图片放在 xxhdpi

获取 bitmap 的宽高和占用内存

bitmap_width = 300 bitmap_height = 300
memory_usage = 360000

图片放在 xxhdpi , 屏幕上显示的是原图的大小,原来的尺寸是多少,在屏幕上就会显示多少。获取的 bitmap 宽高也是原图的宽高,尺寸没有发生变化,所占内存也没有变化

实验2:把图片放在 xhdpi

获取 bitmap 的宽高和占用内存

bitmap_width = 300 bitmap_height = 300
memory_usage = 360000

图片放在 xhdpi 获取的 bitmap 宽高 和 xxhdpi 一样 ,内存占用也一样。

实验3:把图片放在 hdpi

获取 bitmap 的宽高和占用内存

bitmap_width = 300 bitmap_height = 300
memory_usage = 360000

图片放在 hdpi 获取的 bitmap 宽高 和 xxhdpixhdpi 一样 ,内存占用也一样。

实验4:把图片放在 xxxhdpi

获取 bitmap 的宽高和占用内存

bitmap_width = 225 bitmap_height = 225
memory_usage = 202500

图片放在 xxxhdpi 获取的 bitmap 宽高都比原图要小,占用内存也小了。小了多少是怎么计算的。

bitmap宽度 = (手机屏幕密度 / 图片所在目录像素密度) * 原图宽度

结合到本例就是:

手机屏幕密度 = 480
图片放在 xxxhdpi , 像素密度 =  640
原图宽度 = 300 带入公式就是: bitmap 宽度 = (480 / 640 ) * 300  = 225 bitmap的模式是:Config.ARGB_888,每个像素占用 4 个bit
bitmap占用内存:225 x 225 x 4 = 202500

同理,bitmap 的高度也是这样计算的。

那么问题来了,为什么放在 xxxhdpi 目录下,bitmap 的尺寸发生了变化,而放在 hdpi 、xhdpi bitmap 的尺寸却没有发生变化 ???

Android Bitmap 研究与思考(上篇)相关推荐

  1. android bitmap转图片_这是一份面向Android开发者的复习指南

    来自:简书,作者:九心 链接:https://www.jianshu.com/p/b3c1b9c6dd40 前言 相信很多同学都会有这样的感受,前三天刚刚复习的知识点,今天问的时候怎么就讲不出个所以然 ...

  2. Android Bitmap转换WebP图片导致损坏的分析及解决方案

    Android Bitmap转换WebP图片导致损坏的分析及解决方案 参考文章: (1)Android Bitmap转换WebP图片导致损坏的分析及解决方案 (2)https://www.cnblog ...

  3. Android人脸支付研究,智能手机上人脸支付系统的设计与实现

    摘要: 随着通信技术的不断发展,3G,4G通信时代接踵而来,不断提高的通话质量及通信速度也推动了手机业务的发展.在智能手机支付问题上,人们对支付的安全性,个人信息的保密性,以及实际操作的便捷性提出了更 ...

  4. Android bitmap图片处理

    一.View转换为Bitmap         在Android中所有的控件都是View的直接子类或者间接子类,通过它们可以组成丰富的UI界面.在窗口显示的时候Android会把这些控件都加载到内存中 ...

  5. Android安全研究经验谈

    安全研究做什么 从攻击角度举例,可以是:对某个模块进行漏洞挖掘的方法,对某个漏洞进行利用的技术,通过逆向工程破解程序.解密数据,对系统或应用进行感染.劫持等破坏安全性的攻击技术等. 而防御上则是:查杀 ...

  6. Android—Bitmap图片大小计算、压缩与三级缓存

    Bitmap对象占用内存大小: bitmap.getByteCount() 图片所占内存大小计算方式:图片长度 x 图片宽度 x 一个像素点占用的字节数. Android Bitmap使用的三种颜色格 ...

  7. android bitmap string,Android Bitmap到Base64字符串(Android Bitmap to Base64 String)

    Android Bitmap到Base64字符串(Android Bitmap to Base64 String) 如何将一个大的Bitmap(用手机相机拍摄的照片)转换为Base64 String? ...

  8. Android Bitmap OutOfMemory 解决办法

    Android Bitmap OutOfMemory 解决办法 置顶 2014年07月01日 14:41:22 阅读数:3072 标签: OutOfMemoryBitmapandroid图片优化更多 ...

  9. android bitmap对比,Android Bitmap和Drawable的对比

    Android Bitmap和Drawable的对比 Bitmap - 称作位图,一般位图的文件格式后缀为bmp,当然编码器也有很多如RGB565.RGB888.作为一种逐像素的显示对象执行效率高,但 ...

最新文章

  1. cocos2dx spine之一 :spine缓存 (c++ lua)
  2. python中怎么绘制柱状簇_用Python绘制簇的质心
  3. Spring 的优秀工具类盘点
  4. TensorFlow 1.0正式发布
  5. android布局技巧:创建高效布局
  6. 媒体查询使用方法@media
  7. 超赞 | 计算机视觉联盟全新Logo!近期精华回顾!
  8. Install And Configure ColdFusion MX 6.1 on Windows
  9. 【SpringBoot】spring boot + mybatis + druid
  10. 把html压缩成dll,一篇文章带你浅入webpack的DLL优化打包
  11. java基本数据类型填空题_java基本数据类型练习题
  12. python 高性能http服务器_Python高性能HTTP客户端
  13. 生物信息学的现状与展望
  14. 服务器 异常自动关机,服务器自动关机
  15. 网易云音乐接口大全(亲测可用)
  16. python查找excel中重复数据_Python pandas 获取Excel重复记录
  17. AI明星上市受阻,是继续融资还是割肉?
  18. 爬虫(二) parse、各类请求和伪装UA
  19. 计算机主机不过电,电脑主板不通电的解决方法
  20. 【grpc02】安装protobuf和protoc

热门文章

  1. 怎么给web 服务器 传文件,web文件传到服务器
  2. 计算机专业开学周记,【热门】开学周记集锦5篇
  3. 计算机系学生的职业生涯作文,医学生职业生涯规划的作文800字
  4. 机器学习付费专栏的一些简介
  5. 九、Golang并发和线程模型
  6. 会议交流 - CCKS2020 | 2020年全国知识图谱与语义计算大会
  7. 从信息瓶颈理论一瞥机器学习的“大一统理论”
  8. 牛客网 短最优升级路径 【Dijkstra算法】+【路径记录】
  9. MsSQL学习第五章---排序和分页
  10. centos 6.5安装mysql5.7,centos6.5安装mysql5.7