Bitmap

  1. 基本概念(是什么,应用场景)以及BitMap的编码原理(做引导)

  2. BitMap类在Android类中的基本实现(基本结构)

  3. recycle

Bitmap

位图的像素都分配有特定的位置和颜色值。每个像素的颜色信息由RGB组合或者灰度值表示。
根据位深度,可将位图分为1、4、8、16、24及32位图像等。每个像素使用的信息位数越多,可用的颜色就越多,颜色表现就越逼真,相应的数据量越大。例如,位深度为1的像素位图只有两个可能的值(黑色和白色),所以又称为二值位图。位深度为8的图像有28(即256)个可能的值。位深度为8的灰度模式图像有256个可能的灰色值。
Config解析:

Bitmap.Config.ALPHA_8:颜色信息只由透明度组成,占8位。
Bitmap.Config.ARGB_4444:颜色信息由透明度与R(Red),G(Green),B(Blue)四部分组成,每个部分都占4位,总共占16位。已经被废弃,因为显示质量不好。
Bitmap.Config.ARGB_8888:颜色信息由透明度与R(Red),G(Green),B(Blue)四部分组成,每个部分都占8位,总共占32位。是Bitmap默认的颜色配置信息,也是最占空间的一种配置。
Bitmap.Config.RGB_565:颜色信息由R(Red),G(Green),B(Blue)三部分组成,R占5位,G占6位,B占5位,总共占16位。如果不需要 alpha 通道,特别是资源本身为 jpg 格式的情况下,用这个格式比较理想

这个在skia库中可以看到


image.png

当需要做性能优化或者防止OOM(Out Of Memory),我们通常会使用Bitmap.Config.RGB_565这个配置。

大多数情况下其实我们并不需要argb中的alpha通道,在背景已知的情况下,rgb和argb是可以互相转换的。而多数情况下我们都是白色背景。(直接使用target的 rgb就可以了)
Source => Target = (BGColor + Source) =
Target.R = ((1 - Source.A) * BGColor.R) + (Source.A * Source.R)
Target.G = ((1 - Source.A) * BGColor.G) + (Source.A * Source.G)
Target.B = ((1 - Source.A) * BGColor.B) + (Source.A * Source.B)

小问题:RGB_565这个数字是怎么定的?为什么不取555?
文件读取是按byte来的。

和矢量图的比较
1.文件小,图像中保存的是线条和图块的信息,所以矢量图形文件与分辨率和图像大小无关,只与图像的复杂程度有关,图像文件所占的存储空间较小。
2矢量图无限放大不模糊,大部分位图都是由矢量导出来的
3.矢量图最大的缺点是难以表现色彩层次丰富的逼真图像效果。

移动端开发中位图的应用很少,因为很少遇到这种需要无限缩放的场景。对于少数有缩放需要的场景,Bitmap类提供了一种特殊而且有趣的方式。这就是(九点图)

Bitmap的相关类很多,但是只要按照一个基本思路梳理 ,就会很清晰

  1. 文件和Bitmap的相互转换
    1.1 文件转换为Bitmap

Bitmap是一个final类,因此不能被继承。Bitmap只有一个构造方法,且该构造方法是没有任何访问权限修饰符修饰,也就是说该构造方法是friendly,但是谷歌称Bitmap的构造方法是private(私有的),感觉有点不严谨。不管怎样,一般情况下,我们不能通过构造方法直接新建一个Bitmap对象。
从文件创建Bitmap类就离不开BitmapFactory

BitmapFactory类提供了四类方法:decodeFile、decodeRe-source、decodeStream和decodeByteArray,分别用于支持从文件系统、资源、输入流以及字节数组中加载出一个Bitmap对象,其中decodeFile和decodeResource又间接调用了decode-Stream方法,这四类方法最终是在Android的底层实现的,对应着BitmapFactory类的几个native方法。

其实核心思想也很简单,那就是采用BitmapFactory.Options来加载所需尺寸的图片。
通过BitmapFactory.Options来缩放图片,主要是用到了它的inSampleSize参数,即采样率。当inSampleSize为1时,采样后的图片大小为图片的原始大小;当inSampleSize大于1时,比如为2,那么采样后的图片其宽/高均为原图大小的1/2,而像素数为原图的1/4,其占有的内存大小也为原图的1/4。

(1)将BitmapFactory.Options的inJustDecodeBounds参数设为true并加载图片。
这一步并不会读取文件的像素区块。只会去从
(2)从BitmapFactory.Options中取出图片的原始宽高信息,它们对应于outWidth和outHeight参数。
(3)根据采样率的规则并结合目标View的所需大小计算出采样率inSampleSize。
(4)将BitmapFactory.Options的inJustDecodeBounds参数设为false,然后重新加载图片。

我们现在有个需求,要求将一张图片进行模糊,然后作为 ImageView 的 src 呈现给用户,而我们的原始图片大小为 1080*1920,如果我们直接拿来模糊的话,一方面模糊的过程费时费力,另一方面生成的图片又占用内存,实际上在模糊运算过程中可能会存在输入和输出并存的情况,此时内存将会有一个短暂的峰值。

1.2 从Bitmap转换为文件
Bitmap支持的文件格式

Bitmap在内存中的大小是可以简单计算出来的了。但是文件不同,文件可以进行压缩

一种比较典型的压缩方式比如

image.png
image.png

位图的格式有很多种, 每种的压缩算法都不同。


image.png

但是目前Android中的Bitmap只支持三种

image.png
image.png

CompressFormat解析:
和刚才的Bitmap.Config相比,这个内部类只会在压缩文件等时被用到

Bitmap.CompressFormat.JPEG:表示以JPEG压缩算法进行图像压缩,压缩后的格式可以是".jpg"或者".jpeg",是一种有损压缩。

Bitmap.CompressFormat.PNG:表示以PNG压缩算法进行图像压缩,压缩后的格式可以是".png",是一种无损压缩。这意味着在解析时,可能会忽略掉 质量。

Bitmap.CompressFormat.WEBP:是一种同时提供了有损压缩无损压缩(可逆压缩)的图片文件格式
而在日常应用中发现,同样的图片质量(70%)下,这三种格式的大小是有差异的:

泡泡图文发布不同格式对比

如果对存储性能要求更严格的话(存储器空间不足)或者有大量图片存储,可以考虑使用webp格式。

题外话,如果本地有大量的图片资源文件,可以考虑批量将png图转换成webp格式。

2.Bitmap的调整
Bitmap自身的调整也是一件非常有意思的事情。 说道bitmap就不得不提Matrix。
可以说,这俩如影相随。
???

这里写什么# 矩阵变换?

3. 手动recycler()是否有必要?

Google对这个问题其实已经解释,但是比较含混:
首先我们看下这个方法到底做了些什么:
我们刚才已经看过Bitmap的java类中所包含的只是一些方便我们取用的信息。

主要方法包括recycler都在能去JNI层查看。

Paste_Image.png
Paste_Image.png

这段话其实看得人也比较迷糊。前一句还比较简单。大致是说其主要数据都是存在native的内存中,无轮是malloc了native memory哪些,轮不到dalvik来管。所以在“交互接口”上得自己管理好资源的分配和释放。 如果处理得不好,有可能java虚拟机自己跑得还挺欢,进程首先内存就不够用了。
怎么办 ,就需要我们显式去调用recycler()

所以2.3.3 之前的代码应该怎么写呢,得靠你自己来实现一个引用计数器。

Paste_Image.png

对于我们java程序员来说,这个真的有点难。我就想做个图片,你还得让我实现一个引用计数器?
即便是对于今天的C++ 程序员来说,也已经有智能指针来帮助他做这些事情了。

那么 现在的版本里,recycler是否有效果?我们写一个小demo先试试吧:
我选用的targetApi为25 ,源码非常简单

Paste_Image.png

结果如图:


Paste_Image.png

我们发现,其实手动调用recycler并没有将内存释放掉。

那是以前,现在又提到到了3.0之后 Dalvik 又把这些东西都收到自己的堆里, 并且和Bitmap联系起来。
怎么联系起来的?
我们先看看Bitmap的构造方法,这是个私有的构造方法。是在native层构建了之后,再回调过来的。

Paste_Image.png

在这个方法中 ,有这样一句话(API25):

Paste_Image.png

厉害了,native层分配的内存大小居然是业务中自己计算出来的,连同析构函数一同给了这个注册器。

这个注册器最终会调用到VMRunTime的registerNativeAllocation
会将native对象的大小通知给dalvik,如果当前的native内存分配过大,可能会引发一次GC,这也是为什么我们看到了上面的效果、

Paste_Image.png

(https://android.googlesource.com/platform/libcore/+/master/libart/src/main/java/dalvik/system/VMRuntime.java)

最终实际进行gc的地方:

Paste_Image.png

(https://android.googlesource.com/platform/dalvik/+/kitkat-release/vm/alloc/HeapSource.cpp)

当然这只是其中一种的gc触发路径。在别的很多情况下都有可能,但是recycler并不会触发gc,或者说recycler 方法并不能在性能上带来提升。gc的事情还是去交给gc去做吧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,312评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,578评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,337评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,134评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,161评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,303评论 1 280
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,761评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,421评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,609评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,450评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,504评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,194评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,760评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,836评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,066评论 1 257
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,612评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,178评论 2 341

推荐阅读更多精彩内容