Glide是一款由Bump Technologies开发的图片加载框架,使得我们可以在Android平台上以极度简单的方式来加载和展示图片。
使用Glide来加载网络图片非常简单,通过Glide.with(this).load(url).into(imageView)
这样的一句代码就可以搞定,虽然很简单,但还是需要知其所以然。下面就来梳理一下Glide是如何加载网络图片(不涉及生命周期、缓存等功能点,仅加载网络图片的实现)。
由于本文不涉及Glide的生命周期,所以直接来看Glide构造方法。
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 RequestOptions defaultRequestOptions,
@NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions) {
this.engine = engine;
this.bitmapPool = bitmapPool;
this.arrayPool = arrayPool;
this.memoryCache = memoryCache;
this.requestManagerRetriever = requestManagerRetriever;
this.connectivityMonitorFactory = connectivityMonitorFactory;
DecodeFormat decodeFormat = defaultRequestOptions.getOptions().get(Downsampler.DECODE_FORMAT);
bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat);
final Resources resources = context.getResources();
//管理组件注册以扩展或替换Glide的默认加载,解码和编码逻辑。
registry = new Registry();
// Right now we're only using this parser for HEIF images, which are only supported on OMR1+.
// If we need this for other file types, we should consider removing this restriction.
// Note that order here matters. We want to check the ExifInterface parser first for orientation
// and then fall back to DefaultImageHeaderParser for other fields.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
registry.register(new ExifInterfaceImageHeaderParser());
}
registry.register(new DefaultImageHeaderParser());
Downsampler downsampler = new Downsampler(registry.getImageHeaderParsers(),
resources.getDisplayMetrics(), bitmapPool, arrayPool);
ByteBufferGifDecoder byteBufferGifDecoder =
new ByteBufferGifDecoder(context, registry.getImageHeaderParsers(), bitmapPool, arrayPool);
ResourceDecoder<ParcelFileDescriptor, Bitmap> parcelFileDescriptorVideoDecoder =
VideoDecoder.parcel(bitmapPool);
ByteBufferBitmapDecoder byteBufferBitmapDecoder = new ByteBufferBitmapDecoder(downsampler);
StreamBitmapDecoder streamBitmapDecoder = new StreamBitmapDecoder(downsampler, arrayPool);
ResourceDrawableDecoder resourceDrawableDecoder =
new ResourceDrawableDecoder(context);
ResourceLoader.StreamFactory resourceLoaderStreamFactory =
new ResourceLoader.StreamFactory(resources);
ResourceLoader.UriFactory resourceLoaderUriFactory =
new ResourceLoader.UriFactory(resources);
ResourceLoader.FileDescriptorFactory resourceLoaderFileDescriptorFactory =
new ResourceLoader.FileDescriptorFactory(resources);
ResourceLoader.AssetFileDescriptorFactory resourceLoaderAssetFileDescriptorFactory =
new ResourceLoader.AssetFileDescriptorFactory(resources);
BitmapEncoder bitmapEncoder = new BitmapEncoder(arrayPool);
BitmapBytesTranscoder bitmapBytesTranscoder = new BitmapBytesTranscoder();
GifDrawableBytesTranscoder gifDrawableBytesTranscoder = new GifDrawableBytesTranscoder();
ContentResolver contentResolver = context.getContentResolver();
registry
.append(ByteBuffer.class, new ByteBufferEncoder())
//编码器,当缓存数据到本地时会用到
.append(InputStream.class, new StreamEncoder(arrayPool))
/* Bitmaps */
.append(Registry.BUCKET_BITMAP, ByteBuffer.class, Bitmap.class, byteBufferBitmapDecoder)
.append(Registry.BUCKET_BITMAP, InputStream.class, Bitmap.class, streamBitmapDecoder)
.append(
Registry.BUCKET_BITMAP,
ParcelFileDescriptor.class,
Bitmap.class,
parcelFileDescriptorVideoDecoder)
.append(
Registry.BUCKET_BITMAP,
AssetFileDescriptor.class,
Bitmap.class,
VideoDecoder.asset(bitmapPool))
.append(Bitmap.class, Bitmap.class, UnitModelLoader.Factory.<Bitmap>getInstance())
.append(
Registry.BUCKET_BITMAP, Bitmap.class, Bitmap.class, new UnitBitmapDecoder())
.append(Bitmap.class, bitmapEncoder)
/* BitmapDrawables */
.append(
Registry.BUCKET_BITMAP_DRAWABLE,
ByteBuffer.class,
BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, byteBufferBitmapDecoder))
.append(
Registry.BUCKET_BITMAP_DRAWABLE,
InputStream.class,
BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, streamBitmapDecoder))
.append(
Registry.BUCKET_BITMAP_DRAWABLE,
ParcelFileDescriptor.class,
BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, parcelFileDescriptorVideoDecoder))
.append(BitmapDrawable.class, new BitmapDrawableEncoder(bitmapPool, bitmapEncoder))
/* GIFs */
.append(
Registry.BUCKET_GIF,
InputStream.class,
GifDrawable.class,
new StreamGifDecoder(registry.getImageHeaderParsers(), byteBufferGifDecoder, arrayPool))
.append(Registry.BUCKET_GIF, ByteBuffer.class, GifDrawable.class, byteBufferGifDecoder)
.append(GifDrawable.class, new GifDrawableEncoder())
/* GIF Frames */
// Compilation with Gradle requires the type to be specified for UnitModelLoader here.
.append(
GifDecoder.class, GifDecoder.class, UnitModelLoader.Factory.<GifDecoder>getInstance())
.append(
Registry.BUCKET_BITMAP,
GifDecoder.class,
Bitmap.class,
new GifFrameResourceDecoder(bitmapPool))
/* Drawables */
.append(Uri.class, Drawable.class, resourceDrawableDecoder)
.append(
Uri.class, Bitmap.class, new ResourceBitmapDecoder(resourceDrawableDecoder, bitmapPool))
/* Files */
.register(new ByteBufferRewinder.Factory())
.append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
.append(File.class, InputStream.class, new FileLoader.StreamFactory())
.append(File.class, File.class, new FileDecoder())
.append(File.class, ParcelFileDescriptor.class, new FileLoader.FileDescriptorFactory())
// Compilation with Gradle requires the type to be specified for UnitModelLoader here.
.append(File.class, File.class, UnitModelLoader.Factory.<File>getInstance())
/* Models */
.register(new InputStreamRewinder.Factory(arrayPool))
.append(int.class, InputStream.class, resourceLoaderStreamFactory)
.append(
int.class,
ParcelFileDescriptor.class,
resourceLoaderFileDescriptorFactory)
.append(Integer.class, InputStream.class, resourceLoaderStreamFactory)
.append(
Integer.class,
ParcelFileDescriptor.class,
resourceLoaderFileDescriptorFactory)
.append(Integer.class, Uri.class, resourceLoaderUriFactory)
.append(
int.class,
AssetFileDescriptor.class,
resourceLoaderAssetFileDescriptorFactory)
.append(
Integer.class,
AssetFileDescriptor.class,
resourceLoaderAssetFileDescriptorFactory)
.append(int.class, Uri.class, resourceLoaderUriFactory)
.append(String.class, InputStream.class, new DataUrlLoader.StreamFactory<String>())
.append(Uri.class, InputStream.class, new DataUrlLoader.StreamFactory<Uri>())
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
.append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
.append(
String.class, AssetFileDescriptor.class, new StringLoader.AssetFileDescriptorFactory())
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
.append(Uri.class, InputStream.class, new AssetUriLoader.StreamFactory(context.getAssets()))
.append(
Uri.class,
ParcelFileDescriptor.class,
new AssetUriLoader.FileDescriptorFactory(context.getAssets()))
.append(Uri.class, InputStream.class, new MediaStoreImageThumbLoader.Factory(context))
.append(Uri.class, InputStream.class, new MediaStoreVideoThumbLoader.Factory(context))
.append(
Uri.class,
InputStream.class,
new UriLoader.StreamFactory(contentResolver))
.append(
Uri.class,
ParcelFileDescriptor.class,
new UriLoader.FileDescriptorFactory(contentResolver))
.append(
Uri.class,
AssetFileDescriptor.class,
new UriLoader.AssetFileDescriptorFactory(contentResolver))
.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())
/* Transcoders */
.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);
...
}
构造方法最重要的就是Register
这个类,它主要是用于管理组件注册以扩展或替换Glide的默认加载,解码和编码逻辑,比如我们可以使用giflib替换Glide自带的GIF解码器,来提高性能,可以使用OKHttp来替换Glide默认的下载实现,也可以自己定义比Glide默认性能更好的编解码器等。构造方法里默认注册了HttpGlideUrlLoader
这个类,为什么要说这个类尼,因为默认的下载实现就在这个类。
with
是Glide的一组静态方法,它里面有关于Glide生命周期的实现。来看看这组静态方法的实现。
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
...
从上面可以看出with
都返回了一个RequestManager
对象。
load
是RequestManager
里的一组方法,根据传入参数来不同的实现,这里就以传入一个字符串的url为例。
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
//这里代表返回一个Drawable类型的图片
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
//创建一个RequestBuilder对象。
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
//resourceClass对应着RequestBuilder的transcodeClass变量
return new RequestBuilder<>(glide, this, resourceClass, context);
}
RequestBuilder
对象创建成功后,在调用load
方法将图片路径赋值给model
这个变量。
前面快速介绍了Glide.with(context)
与load(url)
的实现,虽然它们也并不简单,但它们也没有实现图片的下载,那么图片的下载是从哪里开始的尼?就是通过into
方法来实现的,来看一下into
方法的实现。
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
...
return into(
//transcodeClass就是前面传递过来的Drawable.class
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions);
}
由于transcodeClass
是一个Drawable
类型,所以glideContext.buildImageViewTarget(view, transcodeClass)
创建了一个DrawableImageViewTarget
对象,来看看DrawableImageViewTarget
的实现。
public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {
public DrawableImageViewTarget(ImageView view) {
super(view);
}
/**
* @deprecated Use {@link #waitForLayout()} instead.
*/
// Public API.
@SuppressWarnings({"unused", "deprecation"})
@Deprecated
public DrawableImageViewTarget(ImageView view, boolean waitForLayout) {
super(view, waitForLayout);
}
//这里是不是很熟悉啊,就是展示图片
@Override
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
}
再来看into
方法的实现。
private <Y extends Target<TranscodeType>> Y into(
//target就是前面创建的DrawableImageViewTarget对象
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
@NonNull RequestOptions options) {
...
//创建一个Request对象,默认是SingleRequest对象
Request request = buildRequest(target, targetListener, options);
//target就是前面创建的DrawableImageViewTarget对象
//拿到target中的Request对象
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
//释放Request对象,因为target已存在同样的Request对象了
request.recycle();
if (!Preconditions.checkNotNull(previous).isRunning()) {
//如果没有加载就开始加载
previous.begin();
}
return target;
}
requestManager.clear(target);
//设置target的Request
target.setRequest(request);
//开始下载
requestManager.track(target, request);
return target;
}
默认创建的Request
对象是SingleRequest
,由于本文分析的是第一次加载图片,所以我们来看RequestManager
的track
方法。
void track(@NonNull Target<?> target, @NonNull Request request) {
...
requestTracker.runRequest(request);
}
//RequestTracker中
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {//当没有暂停时Request就开始执行
request.begin();
} else {//暂停执行
request.clear();
...
pendingRequests.add(request);
}
}
由于这里Request
的具体实现是SingleRequest
,所以我们来看它的begin
方法。
@Override
public void begin() {
...
//传入的model为null,在本文中就是传入的URL为null
if (model == null) {
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
...
//图片加载失败
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
//从缓存中拿数据
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
// Restarts for requests that are neither complete nor running can be treated as new requests
// and can run again from the beginning.
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
//根据View的宽高来计算出图片的宽高,最后回调的也是onSizeReady方法
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
//图片开始加载时的默认显示
target.onLoadStarted(getPlaceholderDrawable());
}
...
}
}
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
...
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);
...
}
}
如果图片的宽高已经确定就直接调用onSizeReady
,否先确定宽高再调用onSizeReady
方法,该方法中最关键的是调用Engine
的load
方法,来看一下实现。
public <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) {
...
//根据model计算出每张图片对应的key
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
//从ActiveResources中获取图片,ActiveResources是一个弱引用的HashMap
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
return null;
}
//从缓存中获取图片,采用了Lrucache
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);
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);
//从本地或者网络获取数据,会切换到子线程
engineJob.start(decodeJob);
return new LoadStatus(cb, engineJob);
}
Glide会首先从缓存中获取数据,如果没有的话再从网络获取。EngineJob
与DecodeJob
两个类非常重要,EngineJob
主要进行线程之间的切换,DecodeJob
主要是从本地或者网络获取数据的实现,来看EngineJob
的start
的实现。
public void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
//向子线程添加任务
executor.execute(decodeJob);
}
在这里就切换到下载图片线程,由于DecodeJob
实现了Runnable
接口,所以就来看run
的方法。
public void run() {
...
try {
if (isCancelled) {
notifyFailed();
return;
}
runWrapped();
} catch (Throwable t) {
...
} finally {
...
}
}
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);
}
}
很明显这里的重点是runGenerators
,来看看runGenerators
的实现。
private void runGenerators() {
...
boolean isStarted = false;
while (!isCancelled && currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
//退出循环
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
// We've run out of stages and generators, give up.
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
}
由于这里不涉及到缓存,所以调用SourceGenerator
的startNext
的方法,当网络返回数据时则dataToCache
不为null,就会存储数据到本地。否则就从网络获取数据。
public boolean startNext() {
//当下载成功后,dataToCache 则不为null,需要写入缓存,后面会用到
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;
//加载数据,loadData的实现是MultiModelLoader,loadData.fetcher的实现是MultiFetcher
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
这里的loadData
的实现是MultiModelLoader
,fetcher
的实现是MultiFetcher
,然后调用loadData
方法来加载数据。
@Override
public void loadData(
@NonNull Priority priority, @NonNull DataCallback<? super Data> callback) {
...
//这里的Fetcher是可以定制的,默认实现是HttpUrlFetcher
fetchers.get(currentIndex).loadData(priority, this);
}
由于我们没有任何定制fetcher,所以调用的是HttpUrlFetcher
的load
方法。
@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 {
...
}
}
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 {
// Comparing the URLs using .equals performs additional network I/O and is generally broken.
// See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
try {
if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
throw new HttpException("In re-direct loop");
}
} catch (URISyntaxException e) {
// Do nothing, this is best effort.
}
}
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);
// Stop the urlConnection instance of HttpUrlConnection from following redirects so that
// redirects will be handled by recursive calls to this method, loadDataWithRedirects.
urlConnection.setInstanceFollowRedirects(false);
// Connect explicitly to avoid errors in decoders if connection fails.
urlConnection.connect();
// Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
stream = urlConnection.getInputStream();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (isHttpOk(statusCode)) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (isHttpRedirect(statusCode)) {
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new HttpException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
// Closing the stream specifically is required to avoid leaking ResponseBodys in addition
// to disconnecting the url connection below. See #2352.
cleanup();
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else if (statusCode == INVALID_STATUS_CODE) {
throw new HttpException(statusCode);
} else {
throw new HttpException(urlConnection.getResponseMessage(), statusCode);
}
}
到这里想必大家就很熟悉了,就是连接网络并下载图片。将数据通过callback.onDataReady(result);
返回,这个callback其实就是MultiFetcher
。
public void onDataReady(@Nullable Data data) {
if (data != null) {
callback.onDataReady(data);
} else {
startNextOrFail();
}
}
这个callback
其实就是SourceGenerator
。
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
...
cb.reschedule();
} else {
...
}
}
这个cb其实就是DecodeJob
。
public void reschedule() {
//切换runReason的值
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
这个callback就是EngineJob
,再来看它的reschedule
方法。
@Override
public void reschedule(DecodeJob<?> job) {
//将任务提交给线程池
getActiveSourceExecutor().execute(job);
}
这里是切换到缓存数据线程,那么就会执行DecodeJob
的run
方法,前面介绍过,在该方法内执行的是runWrapped
方法,由于前面将runReason
的值修改为SWITCH_TO_SOURCE_SERVICE
,所以就会直接执行runGenerators
然后再次调用SourceGenerator
的startNext
方法,前面在介绍该方法时,说过如果有数据就写入缓存,这时候就会将数据写入缓存并调用DataCacheGenerator
的startNext
方法。
@Override
public boolean startNext() {
...
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
loadData =
modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(),
helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
//加载从缓存中获取数据
//loadData的实现类是ByteBufferFileLoader
//loadData.fetcher的实现类是ByteBufferFetcher
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
这里就调用了ByteBufferFetcher
的loadData
方法。
@Override
public void loadData(@NonNull Priority priority,
@NonNull DataCallback<? super ByteBuffer> callback) {
ByteBuffer result;
try {
//从缓存从获取数据
result = ByteBufferUtil.fromFile(file);
} catch (IOException e) {
...
//数据获取失败
callback.onLoadFailed(e);
return;
}
//数据获取成功
callback.onDataReady(result);
}
这里的callback就是DataCacheGenerator
。
@Override
public void onDataReady(Object data) {
cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
}
这里的cb就是SourceGenerator
。
@Override
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
DataSource dataSource, Key attemptedKey) {
...
cb.onDataFetcherReady(sourceKey, data, fetcher, loadData.fetcher.getDataSource(), sourceKey);
}
前面说过SourceGenerator
中的cb就是DecodeJob
。
@Override
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
DataSource dataSource, Key attemptedKey) {
...
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
...
try {
decodeFromRetrievedData();
} finally {
...
}
}
}
private void decodeFromRetrievedData() {
...
Resource<R> resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
...
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
...
//该将图片显示在ImageView上了
notifyComplete(result, dataSource);
...
}
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource);
}
前面说过这个callback就是EngineJob
。
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
...
//切换回主线程
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
void handleResultOnMainThread() {
...
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = cbs.size(); i < size; i++) {
ResourceCallback cb = cbs.get(i);
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource, dataSource);
}
}
...
release(false /*isRemovedFromQueue*/);
}
在EngineJob
的onResourceReady
中切换回主线程。并将数据传给cb的onResourceReady
方法,这里的cb是SingleRequest
。
public void onResourceReady(Resource<?> resource, DataSource dataSource) {
...
onResourceReady((Resource<R>) resource, (R) received, dataSource);
}
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
...
try {
...
if (!anyListenerHandledUpdatingTarget) {
Transition<? super R> animation =
animationFactory.build(dataSource, isFirstResource);
//展示图片
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}
notifyLoadSuccess();
}
在前面说过,target
的实现类是DrawableImageViewTarget
。但在该类中并没有onResourceReady
这个方法,于是去父类查找。
@Override
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
if (transition == null || !transition.transition(resource, this)) {
setResourceInternal(resource);
} else {
maybeUpdateAnimatable(resource);
}
}
private void setResourceInternal(@Nullable Z resource) {
//setResource是个抽象方法
setResource(resource);
maybeUpdateAnimatable(resource);
}
setResource
是个抽象方法,在DrawableImageViewTarget
中实现。
@Override
protected void setResource(@Nullable Drawable resource) {
//view是ImageView
view.setImageDrawable(resource);
}
这里相必大家都很熟悉了吧。
到此Glide加载网络图片的流程就完结了,太复杂了,特别是into
方法,由于很复杂,所以画了张时序图,如下:
建议结合源码一起阅读。