Android基础 Bitmap基础

前言

本文介绍图片格式和Android Bitmap相关知识点,具体还是需要深入native层源码看呀。
SKBitmap这块,Skia图像引擎

1. 图片格式

  • GIF: 基于索引色,无损压缩,支持动画、透明,只支持256种颜色
  • JPG: 有损压缩,无透明通道,无动画,适用于色彩丰富的图片
  • PNG: 无损压缩,有透明通道,无动画
  • WEBP: 支持有损无损压缩,支持透明、动画

PNG 适用于所含颜色很少、具有大块颜色相近的区域或亮度差异十分明显的较简单的图片
JPG 使用于需要高保真的较复杂的图像,因为PNG 虽然能无损压缩,但图片文件比较大;同时JPG的图像压缩算法比PNG耗时
WEBP:体积较小,但编解码速度较慢

2. 图片占用内存大小

  • 一个像素占多大内存
    //  表示一个像素,位数越高,那么可存储的颜色信息越多,图像也就越逼真。
    public enum Bitmap.Config {
        ALPHA_8; // 只有透明通道,1个字节
        RGB_565;  //  无透明通道,2个字节,表示不透明图片较好
        ARGB_4444; // Each channel(ARGB)分别用4个像素表示,2个字节
        ARGB_8888; // Each channel(ARGB)分别用8个像素表示,4个字节
        RGBA_F16;    //  ach channel(ARGB)分别用16个像素表示,8个字节
        HARDWARE; 
    }
  • densityDpi像素密度(dpi,dots per inch;或PPI,pixels per inch):
    每英寸上的像素点数,结合屏幕大小和屏幕分辨率如果5.0英寸的手机的屏幕分辨率为1280x720,那么像素密度为192dpi(计算过程为首先计算对角线上的像素数)。

  • density屏幕密度:
    屏幕密度就是像素密度的另外一种表示,是以160dpi=1.0为基准的。手机出厂之后屏幕密度,包括X,Y轴方向的像素密度都是固定值。
    android将实际的屏幕密度进行划分(low,medium,high,and extra high,extra extra high)
    一般情况下的普通屏幕:ldpi是120dpi,mdpi是160dpi,hdpi是240dpi,xhdpi是320dpi ,xxhdpi 是480dpi。android以像素密度160dpi为基准对屏幕进行划分,当像素密度为160dpi时屏幕密度为1.0,像素密度为120dpi时屏幕密度为0.75,像素密度为320dpi时屏幕密度为2.0.

density densityDpi 文件夹
1x ~160 mdpi
1.5x ~240 hdpi
2x ~320 xhdpi
3x ~480 xxhdpi
4x ~640 xxxhdpi
  • 一张 480*300 的png图片放在xxhdpi下,用RGB_8888加载,占用的内存大小为:
        /*
           density如下图,在哪个文件下取哪个density
           targetDensity = resources.displayMetrics.densityDpi,
           scaledWidth = int( 图片的像素宽 *inTargetDensity / inDensity(float) + 0.5) 
           scaledHeight = int(图片的像素高 *inTargetDensity / inDensity(float) + 0.5)
           占用内存大小= scaledWidth * scaledHeight * 单个像素所占内存的大小 
        */
        // 一张 480*300 的png图片放在xxhdpi下,用RGB_8888加载,占用的内存大小为:
        val inTargetDensity: Int = resources.displayMetrics.densityDpi
        val inDensity = 480F // 在哪个文件夹下

        val scaledWidth: Int = (480 * inTargetDensity / inDensity + 0.5F).toInt()
        val scaledHeight: Int = (300 * inTargetDensity / inDensity + 0.5F).toInt()

        LogUtil.e("allocationByteCount: ${bitmap.allocationByteCount}")    
        LogUtil.e("allocate: ${scaledWidth * scaledHeight * 4}")
  • 一张 480*300 的png图片放在assets下(不用缩放),用RGB_8888加载,占用的内存大小为:
        /*
            占用内存大小= imageWidth * imageHeight * 单个像素所占内存的大小
        */
        val bitmap = BitmapFactory.decodeStream(context.assets.open("test.png"))
        LogUtil.e("allocationByteCount: ${bitmap.allocationByteCount}")
        LogUtil.e("allocationByteCount: ${bitmap.byteCount}")

        LogUtil.e("config: ${bitmap.config}")
        LogUtil.e("hasAlpha: ${bitmap.hasAlpha()}")

        val imageWidth = 480
        val imageHeight = 300

        LogUtil.e("count: ${imageWidth * imageHeight * 4}")
  • 带alpha通道的图片使用565加载会怎么样
    如果不能被解析,解码器会选择最佳config进行解码。可以通过bitmap.getConfig查看真正的config
      /**
         * If this is non-null, the decoder will try to decode into this
         * internal configuration. If it is null, or the request cannot be met,
         * the decoder will try to pick the best matching config based on the
         * system's screen depth, and characteristics of the original image such
         * as if it has per-pixel alpha (requiring a config that also does).
         * 
         * Image are loaded with the {@link Bitmap.Config#ARGB_8888} config by
         * default.
         */
        public Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;
  • RGB_4444为何废弃
    通常情况下,RGB各有256级亮度,用数字表示为从0、1、2…直到255。注意虽然数字最高是255,但0也是数值之一,因此共256级。
    按照计算,256级的RGB色彩总共能组合出约1678万种色彩,即256×256×256=16777216。通常也被简称为1600万色或千万色。也称为24位色(2的24次方)。
    4444只能表示一半,形成的图片质量较差。

3. Bitmap内存存在哪里

On Android 2.3.3 (API level 10) and lower, the backing pixel data for a bitmap is stored in native memory. It is separate from the bitmap itself, which is stored in the Dalvik heap. The pixel data in native memory is not released in a predictable manner, potentially causing an application to briefly exceed its memory limits and crash.
From Android 3.0 (API level 11) through Android 7.1 (API level 25), the pixel data is stored on the Dalvik heap along with the associated bitmap.
In Android 8.0 (API level 26), and higher, the bitmap pixel data is stored in the native heap.

2.3.3版本之前存储在native heap;3,0 ~ 8.0在Java heap;8.0之后在native heap。

4. bitmap.recycle方法

  • This is an advanced call, and normally need not be called, since the normal GC process will free up this memory when there are no more references to this bitmap.
    在3.0版本之前需要手动调用,3.o之后不需要主动调用,GC会处理

  • Free the native object associated with this bitmap, and clear the reference to the pixel data. This will not free the pixel data synchronously; it simply allows it to be garbage collected if there are no other references.
    但主动调用后就会释放内存,但不是立即就回收了,而是等待GC回收

  • The bitmap is marked as “dead”, meaning it will throw an exception if getPixels() or setPixels() is called, and will draw nothing. This operation cannot be reversed, so it should only be called if you are sure there are no further uses for the bitmap.
    不可复用不可回撤的操作。只有当你确认了才调用该方法。Glide在处理时从缓存池BitmapPool里trimToSizes时才会调用该方法

猜你喜欢

转载自blog.csdn.net/u014099894/article/details/105522016