1. Glide总览 Glide图片加载框架从总体来看采用三段论来让用户轻松加载图片。
with()方法主要通过创建一个空白的Fragment盖在展示的页面上,感知展示页面的生命周期,来动态调整Glide内部请求等业务。详细参见Glide源码分析-生命周期管理
load()方法完成通用请求的构建
into()方法主要维护这运行队列和等待队列,当生命周期改变的时候,动态清除一些不必要加载的请求。同时还维护着活动缓存和内存缓存,多级缓存有利于节约资源和加载效率,最后如果没有缓存就采用网络请求的方式加载请求的内容。
2. with() with()方法的详细分析可以参见上一篇Glide的叙述:Glide源码分析-生命周期管理 。
总体来讲,with()方法中发生了如下事情:
Glide类:主要做一些 init 工作,比如缓存,线程池,复用池的构建等等。
RequestManagerRetriever类:主要是获得一个 RequestManager
请求管理类,然后绑定一个 Fragment
SupportRequestManagerFragment 类:用于管理请求的生命周期。
RequestManager:主要用于对请求的管理封装。
3. load() load()流程其实不是很复杂,主要做了通用资源类型的设置选项和启动负载,在RequestBuilder 中完成。流程如下图:
RequestBuilder : 这是一个通用请求构建类,可以处理通用资源类型的设置选项和启动负载。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class RequestManager implements LifecycleListener , ModelTypes <RequestBuilder <Drawable >> { ..... public RequestBuilder<Drawable> load (@Nullable String string) { return asDrawable().load(string); } public RequestBuilder<Drawable> asDrawable () { return as(Drawable.class); } @NonNull @CheckResult @Override public RequestBuilder<Drawable> load (@Nullable Uri uri) { return asDrawable().load(uri); } @NonNull @CheckResult @Override public RequestBuilder<Drawable> load (@Nullable File file) { return asDrawable().load(file); } public <ResourceType> RequestBuilder<ResourceType> as ( @NonNull Class<ResourceType> resourceClass) { return new RequestBuilder<>(glide, this , resourceClass, context); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class RequestBuilder <TranscodeType > extends BaseRequestOptions <RequestBuilder <TranscodeType >> implements Cloneable , ModelTypes <RequestBuilder <TranscodeType >> { public RequestBuilder<TranscodeType> load (@Nullable String string) { return loadGeneric(string); } @Nullable private Object model; private boolean isModelSet; private RequestBuilder<TranscodeType> loadGeneric (@Nullable Object model) { this .model = model; isModelSet = true ; return this ; } private RequestBuilder<TranscodeType> loadGeneric (@Nullable Object model) { this .model = model; isModelSet = true ; return this ; } }
到这里 RequestBuilder 就构建好了, RequestBuilder构建出来,接下来就是重头戏into()。
4.into() into()是所有流程中最最复杂的方法,有60多个步骤,此处我们分成几个环节来一一分析流程。
分析之前,我们脑子中一定记住从最初是状态开始翻代码,最初始状态就是:拿到了url,各级缓存都没有资源,需要从网上下载资源,然后加载到控件中。
into()整体流程图 在Glide工程中src目录中 。
4.1 确定控件大小 我们从网上加载的inputstram不可能是下载多少就加载多少,这样内存可能爆掉,或者效率很低,我们应该按需加载,控件多大,我们就将图片压缩到多大加载。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @NonNull public ViewTarget<ImageView, TranscodeType> into (@NonNull ImageView view) { Util.assertMainThread(); Preconditions.checkNotNull(view); BaseRequestOptions<?> requestOptions = this ; if (!requestOptions.isTransformationSet() && requestOptions.isTransformationAllowed() && view.getScaleType() != null ) { switch (view.getScaleType()) { ..... case FIT_CENTER: case FIT_START: case FIT_END: requestOptions = requestOptions.clone().optionalFitCenter(); break ; .... } } return into( glideContext.buildImageViewTarget(view, transcodeClass), null , requestOptions, Executors.mainThreadExecutor()); }
上面代码就两大步:
第一步:先拿到当前 ImageView getScaleType 类型的属性,然后重新 clone 一个进行配置;
第二步:调用 into 重载继续构建;
先来看下 glideContext.buildImageViewTarget 是怎么构建出来 ImageViewTarget 的:
1 2 3 4 5 6 @NonNull public <X> ViewTarget<ImageView, X> buildImageViewTarget ( @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) { return imageViewTargetFactory.buildTarget(imageView, transcodeClass); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class ImageViewTargetFactory { @NonNull @SuppressWarnings("unchecked") public <Z> ViewTarget<ImageView, Z> buildTarget (@NonNull ImageView view, @NonNull Class<Z> clazz) { if (Bitmap.class.equals(clazz)) { return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view); } else if (Drawable.class.isAssignableFrom(clazz)) { return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view); } else { throw new IllegalArgumentException( "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)" ); } } }
上面 生产 Target 的时候注意一下,只要调用了 asBitmap
才会执行生产 BitmapImageViewTarget ,所以这里我们关注 Drawable 类型就行了,我们就先简单看看这个 target 内部怎么实现的,因为最后当拿到请求资源之后,会在target中设置资源到view中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class DrawableImageViewTarget extends ImageViewTarget <Drawable > { public DrawableImageViewTarget (ImageView view) { super (view); } @SuppressWarnings({"unused", "deprecation"}) @Deprecated public DrawableImageViewTarget (ImageView view, boolean waitForLayout) { super (view, waitForLayout); } @Override protected void setResource (@Nullable Drawable resource) { view.setImageDrawable(resource); } }
从上面代码可以知道 DrawableImageViewTarget 继承的是 ImageViewTarget 重写的 setResource 函数,实现了显示 Drawable 图片的逻辑。
回到主流程,into()重载方法中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 private <Y extends Target<TranscodeType>> Y into ( @NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener, BaseRequestOptions<?> options, Executor callbackExecutor) { Preconditions.checkNotNull(target); if (!isModelSet) { throw new IllegalArgumentException("You must call #load() before calling #into()" ); } Request request = buildRequest(target, targetListener, options, callbackExecutor); Request previous = target.getRequest(); if (request.isEquivalentTo(previous) && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) { request.recycle(); if (!Preconditions.checkNotNull(previous).isRunning()) { previous.begin(); } return target; } requestManager.clear(target); target.setRequest(request); requestManager.track(target, request); return target; }
以上核心就两个点:
第一点:为 target buildRequest 构建一个 Glide request 请求;
第二点:将构建出来的 Request 交于 RequestManager 来执行;
我们先简单的来看下怎么构建的 Request:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private Request buildRequest ( Target<TranscodeType> target, @Nullable RequestListener<TranscodeType> targetListener, BaseRequestOptions<?> requestOptions, Executor callbackExecutor) { return buildRequestRecursive( target, targetListener, null , transitionOptions, requestOptions.getPriority(), requestOptions.getOverrideWidth(), requestOptions.getOverrideHeight(), requestOptions, callbackExecutor); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 private Request obtainRequest ( Target<TranscodeType> target, RequestListener<TranscodeType> targetListener, BaseRequestOptions<?> requestOptions, RequestCoordinator requestCoordinator, TransitionOptions<?, ? super TranscodeType> transitionOptions, Priority priority, int overrideWidth, int overrideHeight, Executor callbackExecutor) { return SingleRequest.obtain( context, glideContext, model, transcodeClass, requestOptions, overrideWidth, overrideHeight, priority, target, targetListener, requestListeners, requestCoordinator, glideContext.getEngine(), transitionOptions.getTransitionFactory(), callbackExecutor); }
最后我们发现是 SingleRequest.obtain
来为我们构建的 Request 请求对象,开始只是初始化一些配置属性。
继续回到上面看request的track()方法。
1 2 3 4 5 6 7 synchronized void track (@NonNull Target<?> target, @NonNull Request request) { targetTracker.track(target); requestTracker.runRequest(request); }
1 2 3 4 5 6 7 8 9 10 11 12 13 public void runRequest (@NonNull Request request) { requests.add(request); if (!isPaused) { request.begin(); } else { request.clear(); pendingRequests.add(request); } }
上面的逻辑是先为 requests
添加一个请求,看看是否是停止状态,如果不是就调用 request.begin();
执行。
这里的 Request
是一个接口,通过之前我们讲到 buildRequest
函数可知 Request
的实现类是 SingleRequest
我们就直接看它的 begin
函数.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 @Override public synchronized void begin () { assertNotCallingCallbacks(); stateVerifier.throwIfRecycled(); startTime = LogTime.getLogTime(); if (model == null ) { if (Util.isValidDimensions(overrideWidth, overrideHeight)) { width = overrideWidth; height = overrideHeight; } int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG; onLoadFailed(new GlideException("Received null model" ), logLevel); return ; } if (status == Status.RUNNING) { throw new IllegalArgumentException("Cannot restart a running request" ); } if (status == Status.COMPLETE) { onResourceReady(resource, DataSource.MEMORY_CACHE); return ; } status = Status.WAITING_FOR_SIZE; if (Util.isValidDimensions(overrideWidth, overrideHeight)) { onSizeReady(overrideWidth, overrideHeight); } else { target.getSize(this ); } if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE) && canNotifyStatusChanged()) { target.onLoadStarted(getPlaceholderDrawable()); } if (IS_VERBOSE_LOGGABLE) { logV("finished run method in " + LogTime.getElapsedMillis(startTime)); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Override public void onSizeReady (int width, int height) { ...... loadStatus = engine.load( glideContext, model, requestOptions.getSignature(), this .width, this .height, requestOptions.getResourceClass(), transcodeClass, priority, requestOptions.getDiskCacheStrategy(), requestOptions.getTransformations(), requestOptions.isTransformationRequired(), requestOptions.isScaleOnlyOrNoTransform(), requestOptions.getOptions(), requestOptions.isMemoryCacheable(), requestOptions.getUseUnlimitedSourceGeneratorsPool(), requestOptions.getUseAnimationPool(), requestOptions.getOnlyRetrieveFromCache(), this , callbackExecutor); ...... } }
4.2 从不同缓存中搜寻资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 public synchronized <R> LoadStatus load ( GlideContext glideContext, Object model, Key signature, int width, int height, Class<?> resourceClass, Class<R> transcodeClass, Priority priority, DiskCacheStrategy diskCacheStrategy, Map<Class<?>, Transformation<?>> transformations, boolean isTransformationRequired, boolean isScaleOnlyOrNoTransform, Options options, boolean isMemoryCacheable, boolean useUnlimitedSourceExecutorPool, boolean useAnimationPool, boolean onlyRetrieveFromCache, ResourceCallback cb, Executor callbackExecutor) { EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options); EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable); if (active != null ) { cb.onResourceReady(active, DataSource.MEMORY_CACHE); return null ; } EngineResource<?> cached = loadFromCache(key, isMemoryCacheable); if (cached != null ) { cb.onResourceReady(cached, DataSource.MEMORY_CACHE); return null ; } EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache); if (current != null ) { current.addCallback(cb, callbackExecutor); if (VERBOSE_IS_LOGGABLE) { logWithTimeAndKey("Added to existing load" , startTime, key); } return new LoadStatus(cb, current); } EngineJob<R> engineJob = engineJobFactory.build( key, isMemoryCacheable, useUnlimitedSourceExecutorPool, useAnimationPool, onlyRetrieveFromCache); DecodeJob<R> decodeJob = decodeJobFactory.build( glideContext, model, key, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, isScaleOnlyOrNoTransform, onlyRetrieveFromCache, options, engineJob); jobs.put(key, engineJob); engineJob.addCallback(cb, callbackExecutor); engineJob.start(decodeJob); return new LoadStatus(cb, engineJob); }
通过 engine.load
这个函数里面的逻辑,我们可以总结3点:
先构建请求或者缓存 KEY ;
根据 KEY 从内存缓存中查找对应的资源数据(ActiveResources(活动缓存,内部是一个 Map 用弱引用持有),LruResourceCache),如果有就回调 对应监听的 onResourceReady 表示数据准备好了。
从执行缓存中查找对应 key 的任务
如果找到了,就说明已经正在执行了,不用重复执行。
没有找到,通过 EngineJob.start 开启一个新的请求任务执行。
为了看主线,时刻记住开始强调的,所有缓存中都没有数据! 直接看 EngineJob.start
1 2 3 4 5 6 7 8 9 public synchronized void start (DecodeJob<R> decodeJob) { this .decodeJob = decodeJob; GlideExecutor executor = decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor(); executor.execute(decodeJob); }
通过 DecodeJob
源码得知,它是实现的 Runnable
接口,这里 GlideExecutor 线程池开始执行,就会启动 DecodeJob 的 run 函数。
细心的小伙伴可以看出engineJob之后就decodeJob,不难想出,这两个任务之间一定有获取到资源的过程。而decodeJob包裹了engineJob,decodeJob是一个runnable,所以不难猜出会在线程池中网络请求资源,并且解码资源。下面我们就来看看是如何加载资源和解码的。
4.3 寻找资源加载器 此处不得不佩服框架考虑得的确很周全,我们需要加载资源,肯定就需要资源加载器,那么是不是只有一个加载器呢,当然不是,只有一个加载器的代码可能是我写的,哈哈哈。透露小秘密:框架会根据我们load传入的不同信息源,获取不同的资源加载器。 下面我们来看一下流程。
上一小节结束我们可以看到包裹了engineJob的decodeJob任务会在线程池中执行,那么不难想出decodeJob的run()肯定会被调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class DecodeJob <R > implements DataFetcherGenerator .FetcherReadyCallback , Runnable , Comparable <DecodeJob <?>>, Poolable { @Override public void run () { GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)" , model); DataFetcher<?> localFetcher = currentFetcher; try { if (isCancelled) { notifyFailed(); return ; } runWrapped(); } catch (CallbackException e) { ..... } }
分析runWrapped:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 private void runWrapped () { switch (runReason) { case INITIALIZE: stage = getNextStage(Stage.INITIALIZE); currentGenerator = getNextGenerator(); runGenerators(); break ; ... } } private Stage getNextStage (Stage current) { switch (current) { case INITIALIZE: return diskCacheStrategy.decodeCachedResource() ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE); case RESOURCE_CACHE: return diskCacheStrategy.decodeCachedData() ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE); case DATA_CACHE: return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE; case SOURCE: case FINISHED: return Stage.FINISHED; default : throw new IllegalArgumentException("Unrecognized stage: " + current); } }
通过上面代码可以知道,我们在找资源的执行器,这里由于我们没有在外部配置缓存策略所以,直接从源数据加载,看下面代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private DataFetcherGenerator getNextGenerator () { switch (stage) { case RESOURCE_CACHE: return new ResourceCacheGenerator(decodeHelper, this ); case DATA_CACHE: return new DataCacheGenerator(decodeHelper, this ); case SOURCE: return new SourceGenerator(decodeHelper, this ); case FINISHED: return null ; default : throw new IllegalStateException("Unrecognized stage: " + stage); } }
由于我们什么都没有配置,返回的是 SourceGenerator
源数据执行器。继续下面代码执行:
1 2 3 4 5 6 7 8 9 10 11 private void runGenerators () { currentThread = Thread.currentThread(); startFetchTime = LogTime.getLogTime(); boolean isStarted = false ; while (!isCancelled && currentGenerator != null && !(isStarted = currentGenerator.startNext())) { .... }
上面代码先看 currentGenerator.startNext()
这句代码,DataFetcherGenerator
是一个抽象类,那么这里执行的实现类是哪一个,可以参考下面说明:
状态标记
作用
执行器
Stage.RESOURCE_CACHE
从磁盘中获取缓存的资源数据
ResourceCacheGenerator
Stage.DATA_CACHE
从磁盘中获取缓存的源数据
DataCacheGenerator
Stage.SOURCE
一次新的请求任务
SourceGenerator
因为这里我们没有配置缓存,那么直接看 SourceGenerator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Override public boolean startNext () { ... loadData = null ; boolean started = false ; while (!started && hasNextModelLoader()) { loadData = helper.getLoadData().get(loadDataListIndex++); if (loadData != null && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource()) || helper.hasLoadPath(loadData.fetcher.getDataClass()))) { started = true ; loadData.fetcher.loadData(helper.getPriority(), this ); } } return started; }
代码逻辑很简单,首先获取加载器,然后从加载器中使用fetcher来加载数据就可以了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 List<LoadData<?>> getLoadData() { if (!isLoadDataSet) { isLoadDataSet = true ; loadData.clear(); List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model); for (int i = 0 , size = modelLoaders.size(); i < size; i++) { ModelLoader<Object, ?> modelLoader = modelLoaders.get(i); LoadData<?> current = modelLoader.buildLoadData(model, width, height, options); if (current != null ) { loadData.add(current); } } } return loadData; }
获取到加载器之后,会通过加载器中的fetcher加载数据,然后我们进入loadData()一看,fetcher是一个接口(DataFetcher),实现类有一大堆(如下图),我们根本没法知道到底是哪个来加载数据。
此处就是框架设计巧妙的一点了,到底是怎么知道哪个fetcher呢?
此时我们要回到 Glide 初始的时候 通过 Registry.append()
添加的!!!!!! 下面我们来看看。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 Glide( @NonNull Context context, @NonNull Engine engine, @NonNull MemoryCache memoryCache, @NonNull BitmapPool bitmapPool, @NonNull ArrayPool arrayPool, @NonNull RequestManagerRetriever requestManagerRetriever, @NonNull ConnectivityMonitorFactory connectivityMonitorFactory, int logLevel, @NonNull RequestOptionsFactory defaultRequestOptionsFactory, @NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions, @NonNull List<RequestListener<Object>> defaultRequestListeners, boolean isLoggingRequestOriginsEnabled, boolean isImageDecoderEnabledForBitmaps) { ...... registry .append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory()) .append(URL.class, InputStream.class, new UrlLoader.StreamFactory()) .append(Uri.class, File.class, new MediaStoreFileLoader.Factory(context)) .append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory()) .append(byte [].class, ByteBuffer.class, new ByteArrayLoader.ByteBufferFactory()) .append(byte [].class, InputStream.class, new ByteArrayLoader.StreamFactory()) .append(Uri.class, Uri.class, UnitModelLoader.Factory.<Uri>getInstance()) .append(Drawable.class, Drawable.class, UnitModelLoader.Factory.<Drawable>getInstance()) .append(Drawable.class, Drawable.class, new UnitDrawableDecoder()) .register(Bitmap.class, BitmapDrawable.class, new BitmapDrawableTranscoder(resources)) .register(Bitmap.class, byte [].class, bitmapBytesTranscoder) .register( Drawable.class, byte [].class, new DrawableBytesTranscoder( bitmapPool, bitmapBytesTranscoder, gifDrawableBytesTranscoder)) .register(GifDrawable.class, byte [].class, gifDrawableBytesTranscoder); ...... }
从Glide的构造方法中,我们找到了线索,registry.append()会根据我们load()传入不同类型的参数,为我们注册不同的fetcher。
1 2 append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
我们可以这么理解,我们传入的参数是URL,使用HttpGlideUrlLoader来加载,最终返回一个InputStream流。看了注册加载器之后,我们继续回到如何构建加载器的主路线上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Override public LoadData<InputStream> buildLoadData ( @NonNull GlideUrl model, int width, int height, @NonNull Options options) { GlideUrl url = model; if (modelCache != null ) { url = modelCache.get(model, 0 , 0 ); if (url == null ) { modelCache.put(model, 0 , 0 , model); url = model; } } int timeout = options.get(TIMEOUT); return new LoadData<>(url, new HttpUrlFetcher(url, timeout)); }
此时就很清晰了,我们是通过HttpUrlFetcher
来加载资源的,然后我们进入资源加载环节。
4.4 加载资源 从上一小节中,我们知道了我们传入的url是通过HttpUrlFetcher
来加载资源。那么我们就进入HttpUrlFetcher
的loadData()。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Override public void loadData (@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) { long startTime = LogTime.getLogTime(); try { InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0 , null , glideUrl.getHeaders()); callback.onDataReady(result); } catch (IOException e) { callback.onLoadFailed(e); } finally { ... } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 private InputStream loadDataWithRedirects (URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException { if (redirects >= MAXIMUM_REDIRECTS) { throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!" ); } else { try { if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) { throw new HttpException("In re-direct loop" ); } } catch (URISyntaxException e) { } } urlConnection = connectionFactory.build(url); for (Map.Entry<String, String> headerEntry : headers.entrySet()) { urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue()); } urlConnection.setConnectTimeout(timeout); urlConnection.setReadTimeout(timeout); urlConnection.setUseCaches(false ); urlConnection.setDoInput(true ); urlConnection.setInstanceFollowRedirects(false ); urlConnection.connect(); stream = urlConnection.getInputStream(); if (isCancelled) { return null ; } final int statusCode = urlConnection.getResponseCode(); if (isHttpOk(statusCode)) { return getStreamForSuccessfulRequest(urlConnection); } ... }
从上面代码我们可以看出 HttpURLConnection 作为 Glide 底层成网络请求的。请求成功之后直接返回的是一个输入流,最后会通过 onDataReady
回调到 DecodeJob的onDataFetcherReady 函数中。
此时我们已经从网络上获取到资源了,现在来看是如何回调的。
1 2 3 4 5 6 7 8 9 10 11 @Override public void onDataReady (Object data) { DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy(); if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) { dataToCache = data; cb.reschedule(); } else { cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher, loadData.fetcher.getDataSource(), originalKey); } }
这里会有 else 因为我们没有配置缓存,继续回调:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class DecodeJob <R > implements DataFetcherGenerator .FetcherReadyCallback , Runnable , Comparable <DecodeJob <?>>, Poolable { ... @Override public void onDataFetcherReady (Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) { this .currentSourceKey = sourceKey; this .currentData = data; this .currentFetcher = fetcher; this .currentDataSource = dataSource; this .currentAttemptingKey = attemptedKey; if (Thread.currentThread() != currentThread) { runReason = RunReason.DECODE_DATA; callback.reschedule(this ); } else { GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData" ); try { decodeFromRetrievedData(); } finally { GlideTrace.endSection(); } } } ... }
至此,我们的数据已经从网络上加载完成。最后通过decodeFromRetrievedData()
方法来解析inputstream流。
4.5 解析资源 从上一小节,我们拿到了资源流,下面我们来看看是如何解析资源的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private void decodeFromRetrievedData () { Resource<R> resource = null ; try { resource = decodeFromData(currentFetcher, currentData, currentDataSource); } catch (GlideException e) { e.setLoggingDetails(currentAttemptingKey, currentDataSource); throwables.add(e); } if (resource != null ) { notifyEncodeAndRelease(resource, currentDataSource); } else { runGenerators(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 private <Data> Resource<R> decodeFromData (DataFetcher<?> fetcher, Data data, DataSource dataSource) throws GlideException { ... Resource<R> result = decodeFromFetcher(data, dataSource); .... return result; } finally { fetcher.cleanup(); } } @SuppressWarnings("unchecked") private <Data> Resource<R> decodeFromFetcher (Data data, DataSource dataSource) throws GlideException { LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass()); return runLoadPath(data, dataSource, path); } private <Data, ResourceType> Resource<R> runLoadPath (Data data, DataSource dataSource, LoadPath<Data, ResourceType, R> path) throws GlideException { Options options = getOptionsWithHardwareConfig(dataSource); DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data); try { return path.load( rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource)); } finally { rewinder.cleanup(); } }
注意上面代码,为了解析数据首先构建一个 LoadPath, 然后创建一个 InputStreamRewinder 类型的 DataRewinder, 最终将数据解析的操作放到了 LoadPath.load 方法中 ,接下来看下 LoadPath.load 方法的具体逻辑操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public Resource<Transcode> load (DataRewinder<Data> rewinder, @NonNull Options options, int width, int height, DecodePath.DecodeCallback<ResourceType> decodeCallback) throws GlideException { try { return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables); } finally { listPool.release(throwables); } } private Resource<Transcode> loadWithExceptionList (DataRewinder<Data> rewinder, @NonNull Options options, int width, int height, DecodePath.DecodeCallback<ResourceType> decodeCallback, List<Throwable> exceptions) throws GlideException { Resource<Transcode> result = null ; for (int i = 0 , size = decodePaths.size(); i < size; i++) { DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i); try { result = path.decode(rewinder, width, height, options, decodeCallback); } catch (GlideException e) { ... } ... return result; }
最终通过path.decode()根据尺寸来解析inputstream流。
1 2 3 4 5 6 7 8 9 public Resource<Transcode> decode (DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException { Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options); Resource<ResourceType> transformed = callback.onResourceDecoded(decoded); return transcoder.transcode(transformed, options); }
看看 decodeResource 怎么解析成中间资源的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @NonNull private Resource<ResourceType> decodeResource (DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options) throws GlideException { ... try { return decodeResourceWithList(rewinder, width, height, options, exceptions); } finally { ... } } @NonNull private Resource<ResourceType> decodeResourceWithList (DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options, List<Throwable> exceptions) throws GlideException { Resource<ResourceType> result = null ; for (int i = 0 , size = decoders.size(); i < size; i++) { ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i); try { DataType data = rewinder.rewindAndGet(); if (decoder.handles(data, options)) { data = rewinder.rewindAndGet(); result = decoder.decode(data, width, height, options); } } catch (IOException | RuntimeException | OutOfMemoryError e) { ... } return result; }
可以看到数据解析的任务最终是通过 DecodePath 来执行的, 它内部有三大步操作
第一大步:deResource 将源数据解析成资源(源数据: InputStream, 中间产物: Bitmap)
第二大步:调用 DecodeCallback.onResourceDecoded 处理资源
第三大步:调用 ResourceTranscoder.transcode 将资源转为目标资源(目标资源类型: Drawable)
可以发现,通过上面的 decoder.decode 源码可知,它是一个接口,由于我们这里的源数据是 InputStream,所以,它的实现类是 StreamBitmapDecoder类 ,我们就来看下 它内部的解码过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Override public Resource<Bitmap> decode (@NonNull InputStream source, int width, int height, @NonNull Options options) throws IOException { final RecyclableBufferedInputStream bufferedStream; final boolean ownsBufferedStream; .... try { return downsampler.decode(invalidatingStream, width, height, options, callbacks); } finally { .... } }
最终是通过downsampler去根据目标的宽高,来对输入数据流进行压缩。
回到主线中的三大步:
1 2 3 4 5 6 7 8 9 public Resource<Transcode> decode (DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException { Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options); Resource<ResourceType> transformed = callback.onResourceDecoded(decoded); return transcoder.transcode(transformed, options); }
解析完数据,看第二注释里面回调,最后会回调到 DecodeJob:
1 2 3 4 5 6 7 8 9 10 11 class DecodeJob <R > implements DataFetcherGenerator .FetcherReadyCallback , Runnable , Comparable <DecodeJob <?>>, Poolable { ... @Override public Resource<Z> onResourceDecoded (@NonNull Resource<Z> decoded) { return DecodeJob.this .onResourceDecoded(dataSource, decoded); } ... }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 @Synthetic @NonNull <Z> Resource<Z> onResourceDecoded (DataSource dataSource, @NonNull Resource<Z> decoded) { @SuppressWarnings("unchecked") Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass(); Transformation<Z> appliedTransformation = null ; Resource<Z> transformed = decoded; if (dataSource != DataSource.RESOURCE_DISK_CACHE) { appliedTransformation = decodeHelper.getTransformation(resourceSubClass); transformed = appliedTransformation.transform(glideContext, decoded, width, height); } ... final EncodeStrategy encodeStrategy; final ResourceEncoder<Z> encoder; if (decodeHelper.isResourceEncoderAvailable(transformed)) { encoder = decodeHelper.getResultEncoder(transformed); encodeStrategy = encoder.getEncodeStrategy(options); } else { encoder = null ; encodeStrategy = EncodeStrategy.NONE; } Resource<Z> result = transformed; boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey); if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource, encodeStrategy)) { if (encoder == null ) { throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass()); } final Key key; switch (encodeStrategy) { case SOURCE: key = new DataCacheKey(currentSourceKey, signature); break ; } LockedResource<Z> lockedResult = LockedResource.obtain(transformed); deferredEncodeManager.init(key, encoder, lockedResult); result = lockedResult; } return result; }
此处说白了, onResourceDecoded 中, 主要是对中间资源做了如下的操作:
第一步:对资源进行了转换操作。比如 Fit_Center,CenterCrop, 这些都是在请求的时候配置的;
第二步:构建磁盘缓存的 key。
资源解析完成,已经存入磁盘,此处开始对资源继续转换。
4.6 资源转换 从上一节我们通过解码inputstream流得到Bitmap,然而我们显示需要将Bitmap转化成Drawable。下面我们继续接着上一小节看看资源是如何转换的。
1 2 3 4 5 6 7 8 9 10 11 12 13 public class DecodePath <DataType , ResourceType , Transcode > { Resource<Transcode> decode (DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException { Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options); Resource<ResourceType> transformed = callback.onResourceDecoded(decoded); return transcoder.transcode(transformed, options); } }
我们继续看第三步。通过源码可知,ResourceTranscoder 是一个接口,又因为解析完的数据是 Bitmap 所以它的实现类是 BitmapDrawableTranscoder ,最后看下它的 transcode 具体实现:
1 2 3 4 5 6 7 8 public class BitmapDrawableTranscoder implements ResourceTranscoder <Bitmap , BitmapDrawable > { @Nullable @Override public Resource<BitmapDrawable> transcode (@NonNull Resource<Bitmap> toTranscode, @NonNull Options options) { return LazyBitmapDrawableResource.obtain(resources, toTranscode); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 public final class LazyBitmapDrawableResource implements Resource <BitmapDrawable >, Initializable { private final Resources resources; private final Resource<Bitmap> bitmapResource; @Deprecated public static LazyBitmapDrawableResource obtain (Context context, Bitmap bitmap) { return (LazyBitmapDrawableResource) obtain( context.getResources(), BitmapResource.obtain(bitmap, Glide.get(context).getBitmapPool())); } @Deprecated public static LazyBitmapDrawableResource obtain (Resources resources, BitmapPool bitmapPool,Bitmap bitmap) { return (LazyBitmapDrawableResource) obtain(resources, BitmapResource.obtain(bitmap, bitmapPool)); } @Nullable public static Resource<BitmapDrawable> obtain ( @NonNull Resources resources, @Nullable Resource<Bitmap> bitmapResource) { if (bitmapResource == null ) { return null ; } return new LazyBitmapDrawableResource(resources, bitmapResource); } private LazyBitmapDrawableResource (@NonNull Resources resources, @NonNull Resource<Bitmap> bitmapResource) { this .resources = Preconditions.checkNotNull(resources); this .bitmapResource = Preconditions.checkNotNull(bitmapResource); } @NonNull @Override public Class<BitmapDrawable> getResourceClass () { return BitmapDrawable.class; } @NonNull @Override public BitmapDrawable get () { return new BitmapDrawable(resources, bitmapResource.get()); } @Override public int getSize () { return bitmapResource.getSize(); } @Override public void recycle () { bitmapResource.recycle(); } @Override public void initialize () { if (bitmapResource instanceof Initializable) { ((Initializable) bitmapResource).initialize(); } } }
转化终于完成了 ,将我们解析到的 bitmap 存放到 LazyBitmapDrawableResource 内部, 然后外界通过 get 方法就可以获取到一个 BitmapDrawable 的对象了,解析完就到了展示数据了,请看下面代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class DecodeJob <R > implements DataFetcherGenerator .FetcherReadyCallback , Runnable , Comparable <DecodeJob <?>>, Poolable { private void decodeFromRetrievedData () { Resource<R> resource = null ; try { resource = decodeFromData(currentFetcher, currentData, currentDataSource); } catch (GlideException e) { e.setLoggingDetails(currentAttemptingKey, currentDataSource); throwables.add(e); } if (resource != null ) { notifyEncodeAndRelease(resource, currentDataSource); } else { runGenerators(); } }
第一步就解析完了数据, 现在第二步执行 notifyEncodeAndRelease 函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 private void notifyEncodeAndRelease (Resource<R> resource, DataSource dataSource) { ... notifyComplete(result, dataSource); stage = Stage.ENCODE; try { if (deferredEncodeManager.hasResourceToEncode()) { deferredEncodeManager.encode(diskCacheProvider, options); } } finally { ... } onEncodeComplete(); } private void notifyComplete (Resource<R> resource, DataSource dataSource) { setNotifiedOrThrow(); callback.onResourceReady(resource, dataSource); } }
可以看到上面的 DecodeJob.decodeFromRetrievedData 中主要做了三个处理:
第一个处理:解析返回回来的资源。
第二个处理:拿到解析的资源,如果配置了本地缓存,就缓存到磁盘。
第三个处理:通知上层资源准备就绪,可以使用了。
我们直接看 EngineJob 的 onResourceReady 回调函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 @Override public void onResourceReady (Resource<R> resource, DataSource dataSource) { synchronized (this ) { this .resource = resource; this .dataSource = dataSource; } notifyCallbacksOfResult(); } @Synthetic void notifyCallbacksOfResult () { ResourceCallbacksAndExecutors copy; Key localKey; EngineResource<?> localResource; synchronized (this ) { stateVerifier.throwIfRecycled(); if (isCancelled) { resource.recycle(); release(); return ; } else if (cbs.isEmpty()) { ... } engineResource = engineResourceFactory.build(resource, isCacheable); hasResource = true ; copy = cbs.copy(); incrementPendingCallbacks(copy.size() + 1 ); localKey = key; localResource = engineResource; } listener.onEngineJobComplete(this , localKey, localResource); for (final ResourceCallbackAndExecutor entry : copy) { entry.executor.execute(new CallResourceReady(entry.cb)); } decrementPendingCallbacks(); }
通过上面 EngineJob 的 onResourceReady 回调函数 主要做了 两个处理:
第一个处理:通知上层任务完成。
第二个处理:回调 ImageViewTarget 用于展示数据。
看下 listener.onEngineJobComplete 具体实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 @SuppressWarnings("unchecked") @Override public synchronized void onEngineJobComplete ( EngineJob<?> engineJob, Key key, EngineResource<?> resource) { if (resource != null ) { resource.setResourceListener(key, this ); if (resource.isCacheable()) { activeResources.activate(key, resource); } } jobs.removeIfCurrent(key, engineJob); }
最终通知 ImageViewTarget, 看下具体操作:
1 2 3 4 for (final ResourceCallbackAndExecutor entry : copy) { entry.executor.execute(new CallResourceReady(entry.cb)); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private class CallResourceReady implements Runnable { private final ResourceCallback cb; CallResourceReady(ResourceCallback cb) { this .cb = cb; } @Override public void run () { synchronized (EngineJob.this ) { if (cbs.contains(cb)) { ... callCallbackOnResourceReady(cb); removeCallback(cb); } decrementPendingCallbacks(); } } }
我们可以看到 CallResourceReady 实现 Runnable ,当 entry.executor.execute 线程池执行的时候就会调用 run ,最后我们继续跟 callCallbackOnResourceReady函数:
1 2 3 4 5 6 7 8 9 @Synthetic synchronized void callCallbackOnResourceReady (ResourceCallback cb) { try { cb.onResourceReady(engineResource, dataSource); } catch (Throwable t) { throw new CallbackException(t); } }
SingleRequest onResourceReady 回调实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public synchronized void onResourceReady (Resource<?> resource, DataSource dataSource) { stateVerifier.throwIfRecycled(); loadStatus = null ; ... 省略成吨的代码 Object received = resource.get(); if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) { releaseResource(resource); ... 省略成吨的代码 onLoadFailed(exception); return ; } if (!canSetResource()) { releaseResource(resource); status = Status.COMPLETE; return ; } onResourceReady((Resource<R>) resource, (R) received, dataSource); } private synchronized void onResourceReady (Resource<R> resource, R result, DataSource dataSource) { ... 省略成吨的代码 anyListenerHandledUpdatingTarget |= targetListener != null && targetListener.onResourceReady(result, model, target, dataSource, isFirstResource); if (!anyListenerHandledUpdatingTarget) { Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource); target.onResourceReady(result, animation); } } finally { isCallingCallbacks = false ; } notifyLoadSuccess(); }
这一步主要把准备好的资源回调给显示层!!!!!!! 终于快看到胜利的曙光了。
4.7 显示资源 资源终于完全准备好了,下面就是加载资源到控件上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public abstract class ImageViewTarget <Z > extends ViewTarget <ImageView , Z > implements Transition .ViewAdapter { ... @Override public void onResourceReady (@NonNull Z resource, @Nullable Transition<? super Z> transition) { if (transition == null || !transition.transition(resource, this )) { setResourceInternal(resource); } else { maybeUpdateAnimatable(resource); } } protected abstract void setResource (@Nullable Z resource) ; ... } private void setResourceInternal (@Nullable Z resource) { setResource(resource); ... }
大家还记得么?在最开始构建的时候,我们知道只有调用 asBitmap 的时候实现类是 BitmapImageViewTarget
,在这里的测试,并没有调用这个函数,所以它的实现类是 DrawableImageViewTarget
,具体看下它内部实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class DrawableImageViewTarget extends ImageViewTarget <Drawable > { public DrawableImageViewTarget (ImageView view) { super (view); } @SuppressWarnings({"unused", "deprecation"}) @Deprecated public DrawableImageViewTarget (ImageView view, boolean waitForLayout) { super (view, waitForLayout); } @Override protected void setResource (@Nullable Drawable resource) { view.setImageDrawable(resource); } }
这里看到抽象类中调用了 setResource ,子类实现并调用了 view.setImageDrawable(resource); 图片现在算是真正的显示出来了。