Glide 是 Bump Technologies 公司开源的图片加载框架,适用于 Android 平台,是谷歌推荐的图片库。 它不仅能实现平滑的图片列表滚动效果,还支持远程图片的获取、大小调整和展示,并且可以加载 Gif 动态图,功能强大。
源码基于 com.github.bumptech.glide:glide:4.12.0
简介
Glide 对象是一个单例,使用 GlideBuilder
设置 BitmapPool
、内存和磁盘缓存、线程池和 engine
等参数;
GlideModule
全局配置。只能在主线程使用;
EngineJob
提供线程切换。Transformations
剪裁与变换。TransitionOptions
;
支持加载 gif 图片;
根据 ImageView 尺寸缓存图片;
优点:
Google 推荐;
专注平滑的滚动;
简单易用的 API;
高性能、可扩展。
图片加载流程
基本使用:
1 Glide.with(context).load(url).into(imageView);
Glide 初始化 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 public class Glide implements ComponentCallbacks2 { private static volatile Glide glide; public static Glide get (Context context) { if (glide == null ) { GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules(context.getApplicationContext()); synchronized (Glide.class) { if (glide == null ) { checkAndInitializeGlide(context, annotationGeneratedModule); } } } return glide; } private static GeneratedAppGlideModule getAnnotationGeneratedGlideModules (Context context) { Class<GeneratedAppGlideModule> clazz = (Class<GeneratedAppGlideModule>) Class.forName("com.bumptech.glide.GeneratedAppGlideModuleImpl" ); GeneratedAppGlideModule result = clazz.getDeclaredConstructor(Context.class).newInstance(context.getApplicationContext()); return result; } private static void checkAndInitializeGlide (Context context, GeneratedAppGlideModule generatedAppGlideModule) { if (isInitializing) { throw new IllegalStateException ( "You cannot call Glide.get() in registerComponents()," + " use the provided Glide instance instead" ); } isInitializing = true ; initializeGlide(context, generatedAppGlideModule); isInitializing = false ; } private static void initializeGlide (Context context, GeneratedAppGlideModule generatedAppGlideModule) { initializeGlide(context, new GlideBuilder (), generatedAppGlideModule); } private static void initializeGlide (Context context, GlideBuilder builder, GeneratedAppGlideModule annotationGeneratedModule) { Context applicationContext = context.getApplicationContext(); RequestManagerRetriever.RequestManagerFactory factory = annotationGeneratedModule != null ? annotationGeneratedModule.getRequestManagerFactory() : null ; builder.setRequestManagerFactory(factory); for (com.bumptech.glide.module .GlideModule module : manifestModules) { module .applyOptions(applicationContext, builder); } if (annotationGeneratedModule != null ) { annotationGeneratedModule.applyOptions(applicationContext, builder); } Glide glide = builder.build(applicationContext); for (com.bumptech.glide.module .GlideModule module : manifestModules) { module .registerComponents(applicationContext, glide, glide.registry); } if (annotationGeneratedModule != null ) { annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry); } applicationContext.registerComponentCallbacks(glide); Glide.glide = glide; } }
通过双重检锁的方式实现单例;
通过 AndroidManifest.xml 的配置和注解生成的类,设置参数信息;
1 2 3 4 5 6 7 8 9 <manifest ... > <application ... > <meta-data android:name ="com.mypackage.MyGlideModule" android:value ="GlideModule" /> </application > </manifest >
通过 GlideBuilder 构建 Glide 对象。
绑定生命周期 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 public class RequestManagerRetriever { public RequestManager get (Context context) { if (context == null ) { throw new IllegalArgumentException ("You cannot start a load on a null Context" ); } else if (Util.isOnMainThread() && !(context instanceof Application)) { if (context instanceof FragmentActivity) { return get((FragmentActivity) context); } else if (context instanceof Activity) { return get((Activity) context); } else if (context instanceof ContextWrapper && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null ) { return get(((ContextWrapper) context).getBaseContext()); } } return getApplicationManager(context); } public RequestManager get (FragmentActivity activity) { if (Util.isOnBackgroundThread()) { return get(activity.getApplicationContext()); } else { FragmentManager fm = activity.getSupportFragmentManager(); return supportFragmentGet(activity, fm, null , isActivityVisible(activity)); } } private RequestManager getApplicationManager (Context context) { if (applicationManager == null ) { synchronized (this ) { if (applicationManager == null ) { Glide glide = Glide.get(context.getApplicationContext()); applicationManager = factory.build(glide, new ApplicationLifecycle (), new EmptyRequestManagerTreeNode (), context.getApplicationContext()); } } } return applicationManager; } private RequestManager supportFragmentGet (Context context, FragmentManager fm, Fragment parentHint, boolean isParentVisible) { SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint); RequestManager requestManager = current.getRequestManager(); if (requestManager == null ) { Glide glide = Glide.get(context); requestManager = factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context); if (isParentVisible) { requestManager.onStart(); } current.setRequestManager(requestManager); } return requestManager; } private SupportRequestManagerFragment getSupportRequestManagerFragment (final FragmentManager fm, Fragment parentHint) { SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG); if (current == null ) { current = pendingSupportRequestManagerFragments.get(fm); if (current == null ) { current = new SupportRequestManagerFragment (); current.setParentFragmentHint(parentHint); pendingSupportRequestManagerFragments.put(fm, current); fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss(); handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget(); } } return current; } private static final RequestManagerFactory DEFAULT_FACTORY = new RequestManagerFactory () { @Override public RequestManager build (lide glide, Lifecycle lifecycle, ...) { return new RequestManager (glide, lifecycle, ...); } }; }
Context 不能为 null;
非 UI 线程或 Application 上下文绑定的是全局生命周期;
UI 线程,将 SupportRequestManagerFragment 实例添加到当前 Activity 中,实现对 Activity 生命周期的监听;
绑定资源加载类型 RequestManager
用于管理和发起请求,而且能感知组件的生命周期。基于 Lifecycle
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class RequestManager { public RequestBuilder<Drawable> load (String string) { return asDrawable().load(string); } public RequestBuilder<Drawable> asDrawable () { return as(Drawable.class); } public RequestBuilder<File> asFile () { return as(File.class).apply(skipMemoryCacheOf(true )); } public RequestBuilder<Bitmap> asBitmap () { return as(Bitmap.class).apply(DECODE_TYPE_BITMAP); } public <ResourceType> RequestBuilder<ResourceType> as (Class<ResourceType> resourceClass) { return new RequestBuilder <>(glide, this , resourceClass, context); } }
绑定资源类型,如 URL,Drawable, File, Bitmap 等。
实例化 ImageViewTarget,绑定 ImageView 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 public class RequestBuilder <TranscodeType>{ public RequestBuilder<TranscodeType> load (String string) { return loadGeneric(string); } private RequestBuilder<TranscodeType> loadGeneric (Object model) { if (isAutoCloneEnabled()) { return clone().loadGeneric(model); } this .model = model; isModelSet = true ; return selfOrThrowIfLocked(); } public ViewTarget<ImageView, TranscodeType> into (ImageView view) { Util.assertMainThread(); BaseRequestOptions<?> requestOptions = this ; if (!requestOptions.isTransformationSet() && requestOptions.isTransformationAllowed() && view.getScaleType() != null ) { switch (view.getScaleType()) { case CENTER_CROP: requestOptions = requestOptions.clone().optionalCenterCrop(); break ; case CENTER_INSIDE: requestOptions = requestOptions.clone().optionalCenterInside(); break ; case FIT_CENTER: case FIT_START: case FIT_END: requestOptions = requestOptions.clone().optionalFitCenter(); break ; case FIT_XY: requestOptions = requestOptions.clone().optionalCenterInside(); break ; case CENTER: case MATRIX: default : } } return into(glideContext.buildImageViewTarget(view, transcodeClass), null , requestOptions, Executors.mainThreadExecutor()); } private <Y extends Target <TranscodeType>> Y into (Y target, RequestListener<TranscodeType> targetListener, BaseRequestOptions<?> options, Executor callbackExecutor) { Request request = buildRequest(target, targetListener, options, callbackExecutor); Request previous = target.getRequest(); if (request.isEquivalentTo(previous) && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) { if (!Preconditions.checkNotNull(previous).isRunning()) { previous.begin(); } return target; } requestManager.clear(target); target.setRequest(request); requestManager.track(target, request); return target; } } public class ImageViewTargetFactory { public <Z> ViewTarget<ImageView, Z> buildTarget (mageView view, 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)" ); } } }
into(imageView)
必须在主线程调用;
设置裁剪模式;
通过 GlideContext 创建 ViewTarget。只支持 BitmapImageViewTarget 和 BitmapImageViewTarget;
构造 SingleRequest,并通过 ImageView#setTag() 绑定;
通过 RequestManager#track(target, request) 加载图片;
加载图片 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 public class RequestManager { synchronized void track (Target<?> target, Request request) { targetTracker.track(target); requestTracker.runRequest(request); } } public class RequestTracker { public void runRequest (Request request) { requests.add(request); if (!isPaused) { request.begin(); } else { request.clear(); pendingRequests.add(request); } } } public final class SingleRequest <R>{ public void begin () { synchronized (requestLock) { if (model == null ) { 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, false ); 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()); } } } public void onSizeReady (int width, int height) { if (status != Status.WAITING_FOR_SIZE) { return ; } status = Status.RUNNING; float sizeMultiplier = requestOptions.getSizeMultiplier(); this .width = maybeApplySizeMultiplier(width, sizeMultiplier); this .height = maybeApplySizeMultiplier(height, sizeMultiplier); loadStatus = engine.load(glideContext, model,... callbackExecutor); } }
根据请求状态执行不同操作;
宽高确定后回调 onSizeReady() ,通过 Engine 请求图片。
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 public class Engine { public <R> LoadStatus load (...) { EngineKey key = keyFactory.buildKey(); EngineResource<?> memoryResource = loadFromMemory(key, isMemoryCacheable, startTime); if (memoryResource == null ) { return waitForExistingOrStartNewJob(...); } cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE, false ); return null ; } private EngineResource<?> loadFromMemory( EngineKey key, boolean isMemoryCacheable, long startTime) { if (!isMemoryCacheable) { return null ; } EngineResource<?> active = loadFromActiveResources(key); if (active != null ) { return active; } EngineResource<?> cached = loadFromCache(key); if (cached != null ) { return cached; } return null ; } }
构造请求的唯一标识;
内存缓存存在,调用 onResourceReady() 回调;
否则网络加载资源;
缓存类型:
活动资源 (Active Resources):正在显示的资源
内存缓存 (Memory cache):显示过的资源
资源类型(Resource):被解码、转换后的资源
数据来源 (Data):源文件(未处理过)资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private <R> LoadStatus waitForExistingOrStartNewJob (...) { EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache); if (current != null ) { current.addCallback(cb, callbackExecutor); return new LoadStatus (cb, current); } EngineJob<R> engineJob = engineJobFactory.build(key, ...); DecodeJob<R> decodeJob = decodeJobFactory.build(engineJob, ...); jobs.put(key, engineJob); engineJob.addCallback(cb, callbackExecutor); engineJob.start(decodeJob); return new LoadStatus (cb, engineJob); }
如果请求已存在,设置回调返回;
否则,构造 EngineJob 和 DecodeJob,加入到 EngineJob 集合中;
新加入的 EngineJob 设置回调,启动 EngineJob。
1 2 3 4 5 6 7 8 9 10 11 12 13 public synchronized void start (DecodeJob<R> decodeJob) { this .decodeJob = decodeJob; GlideExecutor executor = decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor(); executor.execute(decodeJob); } private GlideExecutor getActiveSourceExecutor () { return useUnlimitedSourceGeneratorPool ? sourceUnlimitedExecutor : (useAnimationPool ? animationExecutor : sourceExecutor); }
根据缓存策略决定使用哪个线程池。默认情况返回 diskCacheExecutor。 共三种线程池:diskCacheExecutor, sourceUnlimitedExecutor, animationExecutor 或 sourceExecutor 执行请求。
Glide 默认通过 GlideExecutor.newSourceExecutor()
为 sourceExecutor 初始化。
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 public final class GlideExecutor implements ExecutorService { public static GlideExecutor newSourceExecutor () { return newSourceBuilder().build(); } public static GlideExecutor.Builder newSourceBuilder () { return new GlideExecutor .Builder(false ) .setThreadCount(calculateBestThreadCount()) .setName(DEFAULT_SOURCE_EXECUTOR_NAME); } public GlideExecutor build () { ThreadPoolExecutor executor = new ThreadPoolExecutor ( corePoolSize, maximumPoolSize, threadTimeoutMillis, TimeUnit.MILLISECONDS, new PriorityBlockingQueue <Runnable>(), new DefaultThreadFactory (name, uncaughtThrowableStrategy, preventNetworkOperations)); if (threadTimeoutMillis != NO_THREAD_TIMEOUT) { executor.allowCoreThreadTimeOut(true ); } return new GlideExecutor (executor); } }
设置 GlideExecutor 线程池;
线程池调度执行 DecodeJob;
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 class DecodeJob <R> implements Runnable ,... { @Override public void run () { if (isCancelled) { notifyFailed(); return ; } runWrapped(); } private void runWrapped () { switch (runReason) { case INITIALIZE: stage = getNextStage(Stage.INITIALIZE); currentGenerator = getNextGenerator(); runGenerators(); break ; case SWITCH_TO_SOURCE_SERVICE: runGenerators(); break ; case DECODE_DATA: decodeFromRetrievedData(); break ; default : throw new IllegalStateException ("Unrecognized run reason: " + runReason); } } 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); } } private void runGenerators () { currentThread = Thread.currentThread(); boolean isStarted = false ; while (!isCancelled && currentGenerator != null && !(isStarted = currentGenerator.startNext())) { stage = getNextStage(stage); currentGenerator = getNextGenerator(); if (stage == Stage.SOURCE) { reschedule(); return ; } } if ((stage == Stage.FINISHED || isCancelled) && !isStarted) { notifyFailed(); } } private enum Stage { INITIALIZE, RESOURCE_CACHE, DATA_CACHE, SOURCE, ENCODE, FINISHED, } }
判断当前任务在 INITIALIZE, RESOURCE_CACHE, DATA_CACHE, SOURCE 或 FINISHED 的哪个阶段;
根据不同的 Stage 执行不同的任务。
ResourceCacheGenerator 从转换后的缓存中读取资源;
DataCacheGenerator 从原始的缓存中读取资源;
SourceGenerator 从源文件加载资源。
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 class SourceGenerator { public boolean startNext () { if (dataToCache != null ) { Object data = dataToCache; dataToCache = null ; cacheData(data); } if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) { return true ; } sourceCacheGenerator = null ; 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 ; startNextLoad(loadData); } } return started; } private void startNextLoad (final LoadData<?> toStart) { loadData.fetcher.loadData(helper.getPriority(), new DataCallback <Object>() { @Override public void onDataReady (Object data) { if (isCurrentRequest(toStart)) { onDataReadyInternal(toStart, data); } } @Override public void onLoadFailed (Exception e) { if (isCurrentRequest(toStart)) { onLoadFailedInternal(toStart, e); } } }); } }
通过 HttpUrlFetcher 等 Fetcher 加载图片。
MultiModelLoader.MultiFetcher -> OkHttpStreamFetcher(需要引入 okhttp3-integration)
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 public class HttpUrlFetcher { public void loadData ( Priority priority, DataCallback<? super InputStream> callback) { InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0 , null , glideUrl.getHeaders()); callback.onDataReady(result); } private InputStream loadDataWithRedirects (URL url, ...) { urlConnection = buildAndConfigureConnection(url, headers); urlConnection.connect(); stream = urlConnection.getInputStream(); if (isCancelled) { return null ; } final int statusCode = getHttpStatusCodeOrInvalid(urlConnection); if (isHttpOk(statusCode)) { return getStreamForSuccessfulRequest(urlConnection); } else if (isHttpRedirect(statusCode)) { URl redirectUrl = new URL (url, redirectUrlString); cleanup(); return loadDataWithRedirects(redirectUrl, redirects + 1 , url, headers); } else { } } }
通过 HttpURLConnection 获取图片 Stream;
回调 DataCallback#onDataFetcherReady() -> SourceGenerator#onDataReadyInternal() -> DecodeJob#onDataFetcherReady()。
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 public void onDataFetcherReady (...) { if (Thread.currentThread() != currentThread) { runReason = RunReason.DECODE_DATA; callback.reschedule(this ); } else { decodeFromRetrievedData(); } } private void decodeFromRetrievedData () { Resource<R> resource = decodeFromData(currentFetcher, currentData, currentDataSource); if (resource != null ) { notifyEncodeAndRelease(resource, currentDataSource, isLoadingFromAlternateCacheKey); } else { runGenerators(); } } private void notifyEncodeAndRelease () { notifyComplete(result, ...); stage = Stage.ENCODE; } private void notifyComplete (Resource<R> resource, ...) { callback.onResourceReady(resource, dataSource, isLoadedFromAlternateCacheKey); }
回调 ImageViewTarget,显示图片 1 2 3 4 5 6 7 8 private void onResourceReady (Resource<R> resource...) { status = Status.COMPLETE; if (!anyListenerHandledUpdatingTarget) { Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource); target.onResourceReady(result, animation); } }
SingleRequest#onResourceReady()
方法最终会调用到 BitmapImageViewTarget.setResource()
方法。
1 2 3 4 5 6 7 8 9 public class BitmapImageViewTarget extends ImageViewTarget <Drawable> { @Override protected void setResource (Drawable resource) { view.setImageBitmap(resource); } }
GlideAnnotationProcessor 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public final class GlideAnnotationProcessor extends AbstractProcessor { @Override public synchronized void init (ProcessingEnvironment processingEnvironment) { super .init(processingEnvironment); processorUtil = new ProcessorUtil (processingEnvironment); IndexerGenerator indexerGenerator = new IndexerGenerator (processorUtil); libraryModuleProcessor = new LibraryModuleProcessor (processorUtil, indexerGenerator); appModuleProcessor = new AppModuleProcessor (processingEnvironment, processorUtil); extensionProcessor = new ExtensionProcessor (processingEnvironment, processorUtil, indexerGenerator); } @Override public Set<String> getSupportedAnnotationTypes () { Set<String> result = new HashSet <>(); result.addAll(libraryModuleProcessor.getSupportedAnnotationTypes()); result.addAll(extensionProcessor.getSupportedAnnotationTypes()); return result; } }
总结
通过向 Activity 添加无界面的 RequestManagerFragment
绑定生命周期;
将 ImageView 包装成 ImageViewTarget;
执行新的请求前,会清除这个 ImageViewTarget 之前绑定的请求,绑定新的请求;
默认使用 HttpUrlConnection
作为网络请求模块;
获取图片资源成功则会调用 ImageViewTarget#onResourceReady()
方法,否则调用 onLoadFailed() 方法。都会调用 ImageView.setImageBitmap(resource) 设置图片;
流程图
参考 [1] Glide - github [2] Glide Docs-Cn [3] glide-transformations [4] 深入理解 Glide [5] Glide 分析文章