Bitmap知识点集合

Posted 海先生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Bitmap知识点集合相关的知识,希望对你有一定的参考价值。

前言
今天聊聊Bitmap相关的面试题/知识点,看看你是否都弄明白了呢?

Bitmap是什么,怎么存储图片?

Bitmap内存如何计算?

Bitmap内存 和drawable目录的关系。

Bitmap加载优化?不改变图片质量的情况下怎么优化?

inJustDecodeBounds是什么?

Bitmap内存复用怎么实现?

高清大图加载该怎么处理?

如何跨进程传递大图?

Bitmap是什么,怎么存储图片。
Bitmap,位图,本质上是一张图片的内容在内存中的表达形式。它将图片的内容看做是由存储数据的有限个像素点组成;每个像素点存储该像素点位置的ARGB值,每个像素点的ARGB值确定下来,这张图片的内容就相应地确定下来。其中,A代表透明度,RGB代表红绿蓝三种颜色通道值。
Bitmap内存如何计算
Bitmap一直都是android中的内存大户,计算大小的方式有三种:

getRowBytes() 这个在 API Level 1添加的,返回的是bitmap一行所占的大小,需要乘以bitmap的高,才能得出btimap的大小

getByteCount() 这个是在 API Level 12添加的,其实是对getRowBytes()乘以高的封装

getAllocationByteCount() 这个是在 API Level 19添加的

这里我将一张图片放到项目的drawable-xxhdpi文件夹中,然后通过方法获取图片所占的内存大小:
    var bitmap = BitmapFactory.decodeResource(resources, R.drawable.test)
    img.setImageBitmap(bitmap)
        
    Log.e(TAG,"dpi = ${resources.displayMetrics.densityDpi}")
    Log.e(TAG,"size = ${bitmap.allocationByteCount}")
打印出来的结果是
size=1960000
具体是怎么计算的呢?
图片内存=宽 每个像素所占字节。
这个像素所占字节又和Bitmap.Config有关,Bitmap.Config是个枚举类,用于描述每个像素点的信息,比如:

ARGB_8888。常用类型,总共32位,4个字节,分别表示透明度和RGB通道。

RGB_565。16位,2个字节,只能描述RGB通道。

所以我们这里的图片内存计算就得出:
宽700 高700 每个像素4字节=1960000
Bitmap内存 和drawable目录的关系
对照表
刚才的案例,我们是把图片放到drawable-xxhdpi文件夹,而drawable-xxhdpi文件夹对应的dpi就是我们测试手机的dpi—480。所以图片的内存就是我们所计算的宽 每个像素所占字节。
如果我们把图片放到其他的文件夹,比如drawable-hdpi文件夹(对应的dpi是240),会发生什么呢?
再次打印结果:
size = 7840000
这是因为一张图片的实际占用内存大小计算公式是:
占用内存 = 宽 缩放比例 缩放比例 每个像素所占字节
这个缩放比例就跟屏幕密度DPI有关了:
缩放比例 = 设备dpi/图片所在目录的dpi
所以我们这张图片的实际占用内存位:
宽700 (480/240) 高700 (480/240) 每个像素4字节 = 7840000
Bitmap加载优化?不改变图片质量的情况下怎么优化?
常用的优化方式是两种:

修改Bitmap.Config

这一点刚才也说过,不同的Conifg代表每个像素不同的占用空间,所以如果我们把默认的ARGB_8888改成RGB_565,那么每个像素占用空间就会由4字节变成2字节了,那么图片所占内存就会减半了。
可能一定程度上会降低图片质量,但是我实际测试看不出什么变化。

修改inSampleSize

inSampleSize,采样率,这个参数是用于图片尺寸压缩的,他会在宽高的维度上每隔inSampleSize个像素进行一次采集,从而达到缩放图片的效果。这种方法只会改变图片大小,不会影响图片质量。
    val options=BitmapFactory.Options()
    options.inSampleSize=2
    val bitmap = BitmapFactory.decodeResource(resources, R.drawable.test2,options)
    img.setImageBitmap(bitmap)
实际项目中,我们可以设置一个与目标图像大小相近的inSampleSize,来减少实际使用的内存:
    fun getImage(): Bitmap {
        var options = BitmapFactory.Options()
        options.inJustDecodeBounds = true
        BitmapFactory.decodeResource(resources, R.drawable.test2, options)
        // 计算最佳采样率
        options.inSampleSize = getImageSampleSize(options.outWidth, options.outHeight)
        options.inJustDecodeBounds = false
        return BitmapFactory.decodeResource(resources, R.drawable.test2, options)
    }
inJustDecodeBounds是什么?
上面的例子大家应该发现了,其中有个inJustDecodeBounds,又设置为true,又设置成false的,总感觉多此一举,那么他到底是干嘛呢?
因为我们要获取图片本身的大小,如果直接decodeResource加载一遍的话,那么就会增加内存了,所以官方提供了这样一个参数inJustDecodeBounds。如果inJustDecodeBounds为ture,那么decode的bitmap为null,也就是不返回实际的bitmap,只把图片的大小信息放到了options的值中。
所以这个参数就是用来获取图片的大小信息的同时不占用内存。
Bitmap内存复用怎么实现?
如果有个需求,是在同一个imageview中可以加载不同的图片,那我们需要每次都去新建一个Bitmap对象,占用新的内存空间吗?如果源码交易我们这样写的话:
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.actvitiy_bitmap)

        btn1.setOnClickListener {
            img.setImageBitmap(getBitmap(R.drawable.test))
        }

        btn2.setOnClickListener {
            img.setImageBitmap(getBitmap(R.drawable.test2))
        }
    }

    fun getBitmap(resId: Int): Bitmap {
        var options = BitmapFactory.Options()
        return BitmapFactory.decodeResource(resources, resId, options)
    }
这样就会Bitmap就会频繁去申请内存,释放内存,从而导致大量GC,内存抖动。
为了防止这种情况呢,我们就可以用到inBitmap参数,用于Bitmap的内存复用。这样同一块内存空间就可以被多个Bitmap对象复用,从而减少了频繁的GC。
    val options by lazy {
        BitmapFactory.Options()
    }

    val reuseBitmap by lazy {
        options.inMutable = true
        BitmapFactory.decodeResource(resources, R.drawable.test, options)
    }

    fun getBitmap(resId: Int): Bitmap {
        options.inMutable = true
        options.inBitmap = reuseBitmap
        return BitmapFactory.decodeResource(resources, resId, options)
    }
这里有几个要注意的点

inBitmap要和 inMutable属性配套使用,否则将无法复用。

在 Android 4.4之前,只能重用相同大小的 Bitmap 内存区域; 4.4之后只要复用内存空间的Bitmap对象大小比 inBitmap指向的内存空间要小即可。

所以一般在复用之前,还要判断下,新的Bitmap内存是不是小于可以复用的Bitmap内存,然后才能进行复用。

以上是关于Bitmap知识点集合的主要内容,如果未能解决你的问题,请参考以下文章

Android开发——Drawable与Bitmap知识

Android知识要点整理----Bitmap图片处理和展示

Android 知识要点整理(10)----Bitmap缓存策略

oracle知识点

作为Android开发,这个知识点一定要知道,官方也改了 2 次~

Android 自定义 View 知识点