对Glide的加载图片流程不了解的可以看看之前写的Glide范例源码分析
这篇主要介绍Glide在网络获取流之后解析图片,这里讲静态图片。
我们一般在Android中解析一张图片一般是这样的:
public static Bitmap decodeBitmap(byte[] source,int reqesutWidth,int requestHeight){
Bitmap resultBitmap = null;
BitmapFactory.Options option = new BitmapFactory.Options();
//设置option.inJustDecodeBounds 为true获取图片宽高
option.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(source,0,source.length,option);
//通过BitmapFactory.Options.inSampleSize的值保存压缩比例值
option.inSampleSize = caulateInSampleSize(option,reqesutWidth,requestHeight);
option.inJustDecodeBounds = false;
resultBitmap = BitmapFactory.decodeByteArray(source,0,source.length,option);
return resultBitmap;
}
/**
* 计算压缩比例
* 通过BitmapFactory.Options.inSampleSize的值保存该值
* */
private static int caulateInSampleSize(BitmapFactory.Options option, int reqesutWidth, int requestHeight) {
//inSampleSize为1即图片原本大小 如:4 即宽高比为原来1/4
int inSampleSize = 1;
//原图片宽高
int width = option.outWidth;
int height = option.outHeight;
//原图片宽高其中一个大于要求的宽高才压缩比例
if(height > requestHeight || width > reqesutWidth){
inSampleSize = Math.min(Math.round((float)height / requestHeight), Math.round((float)width / reqesutWidth));
}
return inSampleSize;
}
BitmapFactory.decodeByteArray(source,0,source.length,option);
这句话可以换成BitmapFactory.decodeSteam()或BitmapFactory.decodeResource()
这里抛砖引玉:其实Glide的核心代码也是这样解析的,没有什么特别之处;只不过逼格高一点;
Glide的decode是Downsampler类

这个截图是上篇文章的,该类是一个抽象类:

是这里抽象:

该方法的作用就是上面抛出代码计算BitmapFactory.Options.inSampleSize的;
这样说吧:其实就是通过该字段进行缩放;Glide也是在Downsampler类上实现了三种getSampleSize()该方法:
其中AT_LEAST的计算就是和上面抛出的代码一毛一样:

另外两个:


BitmapFactory.inSampleSize是什么
这里要说一下inSampleSize干什么的,看官方解释:
/**
* If set to a value > 1, requests the decoder to subsample the original
* image, returning a smaller image to save memory. The sample size is
* the number of pixels in either dimension that correspond to a single
* pixel in the decoded bitmap. For example, inSampleSize == 4 returns
* an image that is 1/4 the width/height of the original, and 1/16 the
* number of pixels. Any value <= 1 is treated the same as 1. Note: the
* decoder uses a final value based on powers of 2, any other value will
* be rounded down to the nearest power of 2.
*/
public int inSampleSize;
简单理解就是该值越大,解析出来的Bitmap就越小。该值最小为1,取值为2的幂次。
下面是通过google翻译得出:
如果设置为大于1的值,则请求解码器对原始样本进行二次采样
图像,返回一个较小的图像,以节省内存。 样本大小是
任一维度中对应于单个像素的像素数量
像素在解码的位图中。 例如,inSampleSize == 4返回
原始宽度/高度的1/4,1/16的图像
像素数。 任何值<= 1的处理都与1相同。注意:
解码器使用基于2的幂的最终值,任何其他值将会
向下舍入到最接近的2的幂。
知道inSampleSize后,再回Downsampler类的decode()方法
Downsampler类decode()方法:
public Bitmap decode(InputStream is, BitmapPool pool, int outWidth, int outHeight, DecodeFormat decodeFormat) {}
该方法返回一个Bitmap即解析流后的位图。
- InputStream is 网络返回的流
- BitmapPool pool 用于解析Bitmap的缓存
- int outWidth ImageView要求的宽
- int outHeight ImageView要求的高
-
DecodeFormat decodeFormat 要求解析的格式(默认为RGB_565)
DecodeFormat类
RGB_565为一个像素占2个字节,而ARGB_8888为一个像素占4个字节;所以用ARGB_8888解析出来的图片会占内存。
decode()方法前期准备

-
decode()方法开始会 创建两个64k大小的字节数组;
-
获取BitmapFactory.Options对象,该对象通过一个队列管理,尽量减少创建新对象:
BitmapFactory.Options对象管理
-
RecyclableBufferedInputStream 创建一个缓冲区,用于解析图片头部判别图片方向
-
ExceptionCatchingInputStream 用于在读取流中检索异常(感觉挺牛B的,可以学习学习)
-
MarkEnforcingInputStream最终通过此流解析图片
解析图片的方向

这里在流中设置一个标记位,目的就是通过最小读取流来判断图片的方向。标记位最大值为5M
设置临时保存bitmap的容量;获取图片宽高;获取图片旋转的角度

-
options.inTempStorage该值是用于临时储存Bitmap,官网建议是16K
image.png
-
getDimensions(invalidatingStream, bufferedStream, options);
该方法其实最开始已经有代码写了,就是获取宽高
image.png
decodeStream
该方法后面真正解析图片还会调用一次,这里调用就是为了获取宽高:

这里因为不是解析bitmap,所以result为null,目的之前说了就是为了获取宽高,所以也设置一个标记位减少读取时间。
- final int sampleSize = getRoundedSampleSize(degreesToRotate, inWidth, inHeight, outWidth, outHeight);
该方法就是之前说的通过计算宽高比获取sampleSize,最开头说了,Glide默认用AT_LEAST
核心方法downsampleWithSize 解析图片
这里直接上源码:
private Bitmap downsampleWithSize(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,
BitmapFactory.Options options, BitmapPool pool, int inWidth, int inHeight, int sampleSize,
DecodeFormat decodeFormat) {
// Prior to KitKat, the inBitmap size must exactly match the size of the bitmap we're decoding.
//在KitKat之前,inBitmap大小必须与我们正在解码的位图大小完全匹配
Bitmap.Config config = getConfig(is, decodeFormat);
//sampleSize,该值为1 即图片原大小
options.inSampleSize = sampleSize;
options.inPreferredConfig = config;
//当版本高于KITKAT 时候使用 BitmapPool
if ((options.inSampleSize == 1 || Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT) && shouldUsePool(is)) {
int targetWidth = (int) Math.ceil(inWidth / (double) sampleSize);
int targetHeight = (int) Math.ceil(inHeight / (double) sampleSize);
// BitmapFactory will clear out the Bitmap before writing to it, so getDirty is safe.
//在写入BitmapFactory之前,BitmapFactory会清除Bitmap,所以getDirty是安全的
setInBitmap(options, pool.getDirty(targetWidth, targetHeight, config));
}
return decodeStream(is, bufferedStream, options);
}
Android4.1版本使用:ARGB_8888;

如果图片有透明度使用:ARGB_8888;
当inSampleSize为1或大于Android4.4版本,并且属于:
JPEG,PNG_A,PNG类型使用BitmapPool和BitmapFactory.Options.inBitmap
当然如果你的版本是4.4但是inSampleSize为1是那三种图片类型还是可以使用上面说的两个做缓存;
最后其实就是一句:
final Bitmap result = BitmapFactory.decodeStream(is, null, options);
所以万变不离其中;当然Glide团队使用了各种缓存,优化解析工作。
最后总结一下:
- 其实解析图片还是 final Bitmap result = BitmapFactory.decodeStream(is, null, options);
- 通过ExceptionCatchingInputStream此包装类可以捕获在流读取过程中的异常,值的学习。
- 通过集合管理对象,尽量减少对象的创建,毕竟对象的创建对内存有一定开销。
最尾说一下:BitmapPool这个类

网友评论