千家信息网

Bitmap知识点有哪些

发表于:2025-01-18 作者:千家信息网编辑
千家信息网最后更新 2025年01月18日,本篇内容主要讲解"Bitmap知识点有哪些",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"Bitmap知识点有哪些"吧!Bitmap是什么,怎么存储图片。B
千家信息网最后更新 2025年01月18日Bitmap知识点有哪些

本篇内容主要讲解"Bitmap知识点有哪些",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"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目录对应的屏幕密度对照表,来自郭霖的博客:

对照表

刚才的案例,我们是把图片放到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,那么decodebitmap为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内存,然后才能进行复用。

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

如果是高清大图,那就说明不允许进行图片压缩,比如微博长图,清明上河图。

所以我们就要对图片进行局部显示,这就用到BitmapRegionDecoder属性,主要用于显示图片的某一块矩形区域。

比如我要显示左上角的100 * 100区域:

    fun setImagePart() {        val inputStream: InputStream = assets.open("test.jpg")        val bitmapRegionDecoder: BitmapRegionDecoder =            BitmapRegionDecoder.newInstance(inputStream, false)        val options = BitmapFactory.Options()        val bitmap = bitmapRegionDecoder.decodeRegion(            Rect(0, 0, 100, 100), options)        image.setImageBitmap(bitmap)    }

实际项目使用中,我们可以根据手势滑动,然后不断更新我们的Rect参数来实现具体的功能即可。

具体实现源码可以参考鸿洋的博客:https://blog.csdn.net/lmj623565791/article/details/49300989

如何跨进程传递大图?

  • Bundle直接传递。bundle最常用于Activity间传递,也属于跨进程的一种方式,但是传递的大小有限制,一般为1M。

//intent.put的putExtra方法实质也是通过bundleintent.putExtra("image",bitmap);bundle.putParcelable("image",bitmap)

Bitmap之所以可以直接传递,是因为其实现了Parcelable接口进行了序列化。而Parcelable的传递原理是利用了Binder机制,将Parcel序列化的数据写入到一个共享内存(缓冲区)中,读取的时候也会从这个缓冲区中去读取字节流,然后再反序列化成对象使用。这个共享内存也就是缓存区有一个大小限制-1M,而且是公用的。所以传图片的话很容易就容易超过这个大小然后报错TransactionTooLargeException

所以这个方案不可靠。

  • 文件传输

将图片保存到文件,然后只传输文件路径,这样肯定是可以的,但是不高效。

  • putBinder

这个就是考点了。通过传递binder的方式传递bitmap。

//传递binderval bundle = Bundle()bundle.putBinder("bitmap", BitmapBinder(mBitmap))//接收binder中的bitmapval imageBinder: BitmapBinder = bundle.getBinder("bitmap") as BitmapBinderval bitmap: Bitmap? = imageBinder.getBitmap()//Binder子类class BitmapBinder :Binder(){    private var bitmap: Bitmap? = null    fun ImageBinder(bitmap: Bitmap?) {        this.bitmap = bitmap    }    fun getBitmap(): Bitmap? {        return bitmap    }}

为什么用putBinder就没有大小限制了呢?

  • 因为 putBinder中传递的其实是一个文件描述符fd,文件本身被放到一个共享内存中,然后获取到这个fd之后,只需要从共享内存中取出Bitmap数据即可,这样传输就很高效了。

  • 而用 Intent/bundle直接传输的时候,会禁用文件描述符fd,只能在parcel的缓存区中分配空间来保存数据,所以无法突破1M的大小限制。

文件描述符是一个简单的整数,用以标明每一个被进程所打开的文件和socket。第一个打开的文件是0,第二个是1,依此类推。

到此,相信大家对"Bitmap知识点有哪些"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

内存 图片 大小 像素 文件 字节 实际 复用 空间 内容 参数 文件夹 对象 就是 数据 方式 方法 比例 传输 限制 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 寺庙 软件开发 邯郸软件开发培训学校哪个好 软件开发职业情况介绍 服务器管理不能在外网打开 如何判断是否违法网络安全法 动环监控服务器r230 服务器安全狗 小红伞 用友t3数据库如何修改 2017年软件开发税点 昌平电脑服务器回收找哪家 华为软件开发能力提升 clso网络安全工程师 广州软件开发好找工作么 车帮互联网科技服务有限公司 bpl共享数据库连接 自建同步云盘服务器 开机自动打开服务器管理器 山西专业软件开发服务有哪些 数据库列的取值类型可以相同吗 华为服务器900W电源 数据库与数据库文件 华为服务器没有怎么进入raid 顺义区数据库耐磨材料销售价格 软件开发法人多久可以换一次 数据库为什么要用命令行来操作 东营微信公众号软件开发推荐 火鸟数据库怎么删 db2导出指定数据库 职友网深圳软件开发工资 国土资源局网络安全领导小组
0