Glide RequestBuilder
RequestBuilder家族:
RequestBuilder家族的基类:GenericRequestBuilder.java
代码有小1000行 :weary:
先来看看类的定义一上来四个泛型
public class GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType>
private ModelType model;这个就是你请求图片的源,如果是glide.with(context).load(string)那么,modeclass就是string
protected final Class
protected final RequestTracker requestTracker;控制请求的类
DataLoadProvider:一个拥有图片读取和存储实现的提供者,通过它能获取图片存取实现细节的对象:encoder,decoder,cachedecode,而glide初始化的时候就注入了很多的provider
Glide 构造函数:
dataLoadProviderRegistry.register(InputStream.class, Bitmap.class, streamBitmapLoadProvider);
FileDescriptorBitmapDataLoadProvider fileDescriptorLoadProvider =
new FileDescriptorBitmapDataLoadProvider(bitmapPool, decodeFormat);
dataLoadProviderRegistry.register(ParcelFileDescriptor.class, Bitmap.class, fileDescriptorLoadProvider);
ImageVideoDataLoadProvider imageVideoDataLoadProvider =
new ImageVideoDataLoadProvider(streamBitmapLoadProvider, fileDescriptorLoadProvider);
dataLoadProviderRegistry.register(ImageVideoWrapper.class, Bitmap.class, imageVideoDataLoadProvider);
GifDrawableLoadProvider gifDrawableLoadProvider =
new GifDrawableLoadProvider(context, bitmapPool);
dataLoadProviderRegistry.register(InputStream.class, GifDrawable.class, gifDrawableLoadProvider);
dataLoadProviderRegistry.register(ImageVideoWrapper.class, GifBitmapWrapper.class,
new ImageVideoGifDrawableLoadProvider(imageVideoDataLoadProvider, gifDrawableLoadProvider, bitmapPool));
dataLoadProviderRegistry.register(InputStream.class, File.class, new StreamFileDataLoadProvider());
来赏析一下Glide引擎是怎么decode成bitmap的:
StreamBitmapDataLoadProvider里有个
decoder = new StreamBitmapDecoder(bitmapPool, decodeFormat);
cacheDecoder = new FileToStreamDecoder
encoder = new BitmapEncoder();
BitmapEncoder.java的encode方法
@Override
public boolean encode(Resource<Bitmap> resource, OutputStream os) {
final Bitmap bitmap = resource.get();
long start = LogTime.getLogTime();
Bitmap.CompressFormat format = getFormat(bitmap);
bitmap.compress(format, quality, os);
//...ignore code
return true;
}
encode其实就是compress
而decoder和cacheDecoder 最终都是用一个decoder去解析
StreamBitmapDecoder.java
@Override
public Resource<Bitmap> decode(InputStream source, int width, int height) {
Bitmap bitmap = downsampler.decode(source, bitmapPool, width, height, decodeFormat);
return BitmapResource.obtain(bitmap, bitmapPool);
}
Downsampler.java
@Override
public Bitmap decode(InputStream is, BitmapPool pool, int outWidth,
int outHeight, DecodeFormat decodeFormat) {
final ByteArrayPool byteArrayPool = ByteArrayPool.get();
final byte[] bytesForOptions = byteArrayPool.getBytes();
final byte[] bytesForStream = byteArrayPool.getBytes();
final BitmapFactory.Options options = getDefaultOptions();
RecyclableBufferedInputStream bufferedStream = new RecyclableBufferedInputStream(
is, bytesForStream);
ExceptionCatchingInputStream exceptionStream =
ExceptionCatchingInputStream.obtain(bufferedStream);
MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
try {
exceptionStream.mark(MARK_POSITION);
int orientation = 0;
try {
orientation = new ImageHeaderParser(exceptionStream).getOrientation();
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Cannot determine the image orientation from header", e);
}
} finally {
try {
exceptionStream.reset();
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Cannot reset the input stream", e);
}
}
}
options.inTempStorage = bytesForOptions;
final int[] inDimens = getDimensions(invalidatingStream, bufferedStream, options);
final int inWidth = inDimens[0];
final int inHeight = inDimens[1];
final int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);
final int sampleSize = getRoundedSampleSize(degreesToRotate,
inWidth, inHeight, outWidth, outHeight);
final Bitmap downsampled =
downsampleWithSize(invalidatingStream, bufferedStream, options, pool, inWidth,
inHeight, sampleSize,decodeFormat);
final Exception streamException = exceptionStream.getException();
if (streamException != null) {
throw new RuntimeException(streamException);
}
Bitmap rotated = null;
if (downsampled != null) {
rotated = TransformationUtils.rotateImageExif(downsampled, pool, orientation);
if (!downsampled.equals(rotated) && !pool.put(downsampled)) {
downsampled.recycle();
}
}
return rotated;
} finally {
byteArrayPool.releaseBytes(bytesForOptions);
byteArrayPool.releaseBytes(bytesForStream);
exceptionStream.release();
releaseOptions(options);
}
}
private static Bitmap decodeStream(MarkEnforcingInputStream is,
RecyclableBufferedInputStream bufferedStream,
BitmapFactory.Options options) {
if (options.inJustDecodeBounds) {
is.mark(MARK_POSITION);
} else {
bufferedStream.fixMarkLimit();
}
final Bitmap result = BitmapFactory.decodeStream(is, null, options);
try {
if (options.inJustDecodeBounds) {
is.reset();
}
} catch (IOException e) {
//...ignore code
}
return result;
}
说白了最终还是BitmapFactory decode.
启名为Builder自然有builder的一些特性,看看它都可以设置哪些属性
thumbnail
thumbnail(GenericRequestBuilder):请求缩略图的builder,最终会在创建Request的时候生成一对request
thumbnail(float):请求缩略图也可以不用builder,只要告诉它缩略图的比例就行,通过thumbSizeMultiplier缩放比例值,最终通过创建thumb builder的时候传递给sizeMultiplier
else if (thumbSizeMultiplier != null) {
// Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
Request thumbnailRequest = obtainRequest(target, thumbSizeMultiplier, getThumbnailPriority(), coordinator);
coordinator.setRequests(fullRequest, thumbnailRequest);
return coordinator;
}
private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority,
RequestCoordinator requestCoordinator) {
return GenericRequest.obtain(
loadProvider,
model,
signature,
context,
priority,
target,
sizeMultiplier,
placeholderDrawable,
placeholderId,
errorPlaceholder,
errorId,
fallbackDrawable,
fallbackResource,
requestListener,
requestCoordinator,
glide.getEngine(),
transformation,
transcodeClass,
isCacheable,
animationFactory,
overrideWidth,
overrideHeight,
diskCacheStrategy);
}
而且thumbSizeMultiplier要求[0,1]之间
public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> thumbnail(
float sizeMultiplier) {
if (sizeMultiplier < 0f || sizeMultiplier > 1f) {
throw new IllegalArgumentException("sizeMultiplier must be between 0 and 1");
}
this.thumbSizeMultiplier = sizeMultiplier;
return this;
}
你会看到decode代码的时候会根据sizeMultiplier来decode
onSizeReady是在activity/fragment的onStart方法调过来的; 如果用applicationContext当句柄,那么会在into的时候调过来,为什么activity在into的时候不会调onSizeReady,因为ActivityFragmentLifecycle的addListener方法让它stop了
@Override
public void addListener(LifecycleListener listener) {
lifecycleListeners.add(listener);
if (isDestroyed) {
listener.onDestroy();
} else if (isStarted) {
listener.onStart();
} else {
listener.onStop();
}
}
而requestTracker做了pause判断,begin的request才会走sizeReady
/**
* Starts tracking the given request.
*/
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
看看onSizeReady通过sizeMultiplier做大小的缩放
@Override
public void onSizeReady(int width, int height) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
width = Math.round(sizeMultiplier * width);
height = Math.round(sizeMultiplier * height);
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
if (dataFetcher == null) {
onException(new Exception("Failed to load model: \'" + model + "\'"));
return;
}
ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadedFromMemoryCache = true;
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);
loadedFromMemoryCache = resource != null;
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
sizeMultiplier
同上,生成request的时候会通过它去缩放
decoder
其实是provider的sourceDecoder,如果缓存策略是不缓存,sourceDecoder才会登场,直接把网络Stream转成bitmap GenericRequestBuilder.java:
public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> decoder(
ResourceDecoder<DataType, ResourceType> decoder) {
if (loadProvider != null) {
loadProvider.setSourceDecoder(decoder);
}
return this;
}
设置读图的方式,系统有自带的读图方式StreamBitmapDecoder,也可以自己扩展读图的方式
cacheDecoder
从缓存读成bitmap,形变前和形变后都可以用它来读
diskCacheStrategy
缓存策略:
它有四种:原图和结果图都存,都不存,只是原图,只是结果图(默认)
/**
* Set of available caching strategies for media.
*/
public enum DiskCacheStrategy {
/** Caches with both {@link #SOURCE} and {@link #RESULT}. */
ALL(true, true),
/** Saves no data to cache. */
NONE(false, false),
/** Saves just the original data to cache. */
SOURCE(true, false),
/** Saves the media item after all transformations to cache. */
RESULT(false, true);
private final boolean cacheSource;
private final boolean cacheResult;
DiskCacheStrategy(boolean cacheSource, boolean cacheResult) {
this.cacheSource = cacheSource;
this.cacheResult = cacheResult;
}
/**
* Returns true if this request should cache the original unmodified data.
*/
public boolean cacheSource() {
return cacheSource;
}
/**
* Returns true if this request should cache the final transformed result.
*/
public boolean cacheResult() {
return cacheResult;
}
}
encoder
把图片保存成文件:网络下载好的源图,经过形变显示在View上,此时如果要把形变后的bitmap保存本地,需要通过此变量来进行 DecodeJob.java
private void writeTransformedToCache(Resource<T> transformed) {
if (transformed == null || !diskCacheStrategy.cacheResult()) {
return;
}
long startTime = LogTime.getLogTime();
SourceWriter<Resource<T>> writer = new SourceWriter<Resource<T>>(loadProvider.getEncoder(), transformed);
diskCacheProvider.getDiskCache().put(resultKey, writer);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Wrote transformed from source to cache", startTime);
}
}
sourceEncoder
文件下载保存的过程由它来负责,注:并不一定要转成bitmap,可能是网络inputStream->fileOutStream
private Resource<T> cacheAndDecodeSourceData(A data) throws IOException {
long startTime = LogTime.getLogTime();
SourceWriter<A> writer = new SourceWriter<A>(loadProvider.getSourceEncoder(), data);
diskCacheProvider.getDiskCache().put(resultKey.getOriginalKey(), writer);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Wrote source to cache", startTime);
}
startTime = LogTime.getLogTime();
Resource<T> result = loadFromCache(resultKey.getOriginalKey());
if (Log.isLoggable(TAG, Log.VERBOSE) && result != null) {
logWithTimeAndKey("Decoded source from cache", startTime);
}
return result;
}
sourceEncoder,encoder这两怎么区分?
sourceEncoder是原图的保存方式,encoder是原图转换后的保存方式
可以看DecodeJob的实现 DecodeJob.java:
*/
public Resource<Z> decodeFromSource() throws Exception {
Resource<T> decoded = decodeSource();
return transformEncodeAndTranscode(decoded);
}
decodeSource最终用的是sourceEncoder;而transformEncodeAndTranscode最终用的是encoder
后面的章节我们会讲DecodeJob的一些细节
priority
优先级,它的实现是通过FifoPriorityThreadPoolExecutor.newTaskFor方法实现重新创建带优先级的task,priority越小越在前面
且用的是优先级的队列
public FifoPriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAlive, TimeUnit timeUnit,
ThreadFactory threadFactory, UncaughtThrowableStrategy uncaughtThrowableStrategy) {
super(corePoolSize, maximumPoolSize, keepAlive, timeUnit, new PriorityBlockingQueue<Runnable>(), threadFactory);
this.uncaughtThrowableStrategy = uncaughtThrowableStrategy;
}
@Override
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new LoadTask<T>(runnable, value, ordering.getAndIncrement());
}
transformation
如BitmapTransformation 的两个子类FitCenter,CenterCrop,把原Resource转成另外一个Resource
transcoder
把原bitmap转成另外一种格式 如:bitmap->bytearray;bitmap->GlideDrawable等
animate
动画
placeholder
没加载成功时代替的图
fallback
图片的model为null时,用来替代的图也就是Glide.with(context).load(model);model=null
以下是源码:
private void setErrorPlaceholder(Exception e) {
if (!canNotifyStatusChanged()) {
return;
}
Drawable error = model == null ? getFallbackDrawable() : null;
if (error == null) {
error = getErrorDrawable();
}
if (error == null) {
error = getPlaceholderDrawable();
}
target.onLoadFailed(e, error);
}
error()
当model不为空,且没加载到图片的时候用的替代图
listener
回调函数: onException,onResourceReady
skipMemoryCache
不用内存缓存
override
用指定大小的bitmap
signature ???
load
load 为model赋值
into
生成一个target并通过tracker去发请求
因为在requestBuilder这一层的这些个api都会在真正发请求的时候用到,所以不讲太深,后面会重新把它们拿出来再讲一次