Glide原理之執行流程分析
Glide是一個優秀的圖片載入庫,它有如下優點:
1. Glide可以監聽Activity的生命週期管理,更加合理的管理圖片的載入和釋放。
2. 載入質量,Picasso預設採用的ARGB-8888, Glide預設採用的是RGB-565,記憶體佔用會減小一半。
3. Glide可以載入Gif圖。
4. 快取策略和載入速度。Picasso快取的是全尺寸,而Glide的快取的圖片和ImageView的尺寸相同。Glide的這個特點,讓載入顯得特別的快,而Picasso則因為需要在顯示之前重新調整大小而導致一些延遲。
5. Glide可以通過自定義GlideMoudle來完成特殊的載入需求,例如載入加密的圖片等。
這裡簡單分析一下Glide是怎麼樣的一個載入流程。
Glide生命週期監聽
Glide提供了眾多with方法,可以傳入Activity, Context, Fragment, FragmentActivity。然後根據不同情況會作不同的處理。
1. 建立RequestManagerFragment物件並新增到FragmentManager中負責監聽生命週期變化,建立RequestManager物件。
2. 建立SupportRequestManagerFragment物件並新增到FragmentManager中負責監聽生命週期變化,建立RequestManager物件。
3. 普通Context情況,只是建立RequestManager物件。
因此針對是Activity,Fragment的情況,可以監聽生命週期,啟動和暫停圖片的載入等等,更加智慧,這個比Picasso要更有優勢。
Glide初始化
也就是Glide.get(Context context)方法,負責解析AndroidManifest清單檔案中定義的GlideMoudle,並呼叫它的applyOptions和registerComponents介面方法,通常我們自定義的GlideMoudle就是要實現這兩個介面方法。
/**
* Get the singleton.
*
* @return the singleton
*/
public static Glide get(Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
Context applicationContext = context.getApplicationContext();
List<GlideModule> modules = new ManifestParser(applicationContext).parse();
GlideBuilder builder = new GlideBuilder(applicationContext);
for (GlideModule module : modules) {
module.applyOptions(applicationContext, builder);
}
glide = builder.createGlide();
for (GlideModule module : modules) {
module.registerComponents(applicationContext, glide);
}
}
}
}
return glide;
}
然後再看GlideBuilder如何初始化Glide的
Glide createGlide() {
if (sourceService == null) {
final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
sourceService = new FifoPriorityThreadPoolExecutor(cores);
}
if (diskCacheService == null) {
diskCacheService = new FifoPriorityThreadPoolExecutor(1);
}
MemorySizeCalculator calculator = new MemorySizeCalculator(context);
if (bitmapPool == null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
int size = calculator.getBitmapPoolSize();
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
if (memoryCache == null) {
memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
if (engine == null) {
engine = new Engine(memoryCache, diskCacheFactory, diskCacheService, sourceService);
}
if (decodeFormat == null) {
decodeFormat = DecodeFormat.DEFAULT;
}
return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
}
分別是建立獲取源資料的執行緒池sourceService,獲取磁碟快取執行緒池diskCacheService,Bitmap快取池bitmapPool,記憶體快取memoryCache,磁碟快取diskCacheFactory,資源處理引擎engine(負責獲取資源並處理),然後建立Glide物件。
Glide圖片載入開始流程
- Gilde.with(Context context)獲取RequestManager物件,該RequestManager物件建立之後儲存在RequestManagerFragment或SupportRequestManagerFragment中,或者儲存為applicationManager物件三種,也就是說RequestManager可能存在3個例項物件。以下是獲取supportFragment情況的RequestManager物件方式
public class RequestManagerRetriever implements Handler.Callback {
public RequestManager get(FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm);
}
}
RequestManager supportFragmentGet(Context context, FragmentManager fm) {
SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}
}
- RequestManager.load(String string)獲取DrawableTypeRequest物件。當然還有其他的load方式,大同小異
public class RequestManager implements LifecycleListener {
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}
}
- DrawableTypeRequest.into(ImageView view)。最終還是進入到DrawableTypeRequest.into(Target target)方法。
public class GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> implements Cloneable {
public Target<TranscodeType> into(ImageView view) {
Util.assertMainThread();
if (view == null) {
throw new IllegalArgumentException("You must pass in a non null View");
}
if (!isTransformationSet && view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
applyCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
applyFitCenter();
break;
//$CASES-OMITTED$
default:
// Do nothing.
}
}
return into(glide.buildImageViewTarget(view, transcodeClass));
}
public <Y extends Target<TranscodeType>> Y into(Y target) {
Util.assertMainThread();
if (target == null) {
throw new IllegalArgumentException("You must pass in a non null Target");
}
if (!isModelSet) {
throw new IllegalArgumentException("You must first set a model (try #load())");
}
Request previous = target.getRequest();
if (previous != null) {
previous.clear();
requestTracker.removeRequest(previous);
previous.recycle();
}
Request request = buildRequest(target);
target.setRequest(request);
lifecycle.addListener(target);
requestTracker.runRequest(request);
return target;
}
}
這裡會移除釋放target之前的請求,並且執行新的請求
4. RequestTracker.runRequest開始執行請求
public class RequestTracker {
/**
* Starts tracking the given request.
*/
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
}
如果暫停,新增到掛起佇列,否則begin開始執行請求。這裡的request通常是GenericRequest,當然還有ThumbnailRequestCoordinator(可以同時處理完整圖片和縮圖的請求),看它的組成,包含了full和thumb兩個GenericRequest物件
public class ThumbnailRequestCoordinator implements RequestCoordinator, Request {
private Request full;
private Request thumb;
private RequestCoordinator coordinator;
public ThumbnailRequestCoordinator() {
this(null);
}
public ThumbnailRequestCoordinator(RequestCoordinator coordinator) {
this.coordinator = coordinator;
}
public void setRequests(Request full, Request thumb) {
this.full = full;
this.thumb = thumb;
}
...
}
- 分析GenericRequest.begin(),這裡會呼叫onSizeReady
public void begin() {
startTime = LogTime.getLogTime();
if (model == null) {
onException(null);
return;
}
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
/**
* A callback method that should never be invoked directly.
*/
@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));
}
}
在onSizeReady方法中可以看到,通過loadProvider獲取ModelLoader物件和ResourceTranscoder物件,ModelLoader物件用於資源的載入,而ResourceTranscoder物件用於資源資料格式的轉換。通過ModelLoader物件獲取的DataFetcher物件就是用於獲取源資料的,比如從網路讀取圖片資料,或者從SD卡讀取本地圖片資料等。後面將它們一起交給engine的load方法進行圖片的載入處理。
Glide資源載入流程,記憶體快取載入
Glide資源載入從Engine的load方法開始,流程如下
1. 首先根據不同引數建立EngineKey物件作為鍵Key。
2. 然後用EngineKey去記憶體快取中查詢,如果查詢到,就回調ResourceCallback.onResourceReady表示資源找到了,並且會將當前資源從快取中移除,新增到Map
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
final String id = fetcher.getId();
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
EngineJob current = jobs.get(key);
if (current != null) {
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
EngineRunnable是一個Runnable,啟動一個EngineRunnable就是將它交給diskCacheService執行緒池處理。然後你會發現,這裡好像沒有從網路或者本地載入原始圖片資源的請求。它其實在EngineRunnable的run方法中有實現
@Override
public void run() {
if (isCancelled) {
return;
}
Exception exception = null;
Resource<?> resource = null;
try {
resource = decode();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception decoding", e);
}
exception = e;
}
if (isCancelled) {
if (resource != null) {
resource.recycle();
}
return;
}
if (resource == null) {
onLoadFailed(exception);
} else {
onLoadComplete(resource);
}
}
private Resource<?> decode() throws Exception {
if (isDecodingFromCache()) {
return decodeFromCache();
} else {
return decodeFromSource();
}
}
private Resource<?> decodeFromCache() throws Exception {
Resource<?> result = null;
try {
result = decodeJob.decodeResultFromCache();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Exception decoding result from cache: " + e);
}
}
if (result == null) {
result = decodeJob.decodeSourceFromCache();
}
return result;
}
private Resource<?> decodeFromSource() throws Exception {
return decodeJob.decodeFromSource();
}
以上分別有呼叫decodeJob的decodeResultFromCache(磁碟快取中獲取縮放處理後的圖片資源,需要開啟了DiskCacheStrategy.cacheResult),decodeSourceFromCache(磁碟快取中獲取原始圖片資源,,需要開啟了DiskCacheStrategy.cacheSource),decodeFromSource(從網路或者本地源獲取原始資源)三種情況。因此decodeJob是負責從磁碟,網路/本地源載入資源的,獲取之後採用回撥的方式通知資源獲取完畢。
資源載入完成之後,同時還會執行transform轉換和轉碼操作,也就是transformEncodeAndTranscode
public Resource<Z> decodeFromSource() throws Exception {
Resource<T> decoded = decodeSource();
return transformEncodeAndTranscode(decoded);
}
private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
long startTime = LogTime.getLogTime();
Resource<T> transformed = transform(decoded);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transformed resource from source", startTime);
}
writeTransformedToCache(transformed);
startTime = LogTime.getLogTime();
Resource<Z> result = transcode(transformed);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transcoded transformed from source", startTime);
}
return result;
}
就是從一種Resource轉換成另一種Resource。可以看到這裡有呼叫transform,也就是設定transformation時,這裡會產生作用的地方。
private Resource<T> transform(Resource<T> decoded) {
if (decoded == null) {
return null;
}
Resource<T> transformed = transformation.transform(decoded, width, height);
if (!decoded.equals(transformed)) {
decoded.recycle();
}
return transformed;
}
然後是是會判斷儲存transform轉換後的資源,然後是transcode轉碼成另一種Resource資源。
Glide的轉碼ResourceTranscoder
轉碼ResourceTranscoder介面也有很多實現,例如BitmapBytesTranscoder(將Bitmap轉換為byte[])
public class BitmapBytesTranscoder implements ResourceTranscoder<Bitmap, byte[]> {
private final Bitmap.CompressFormat compressFormat;
private final int quality;
public BitmapBytesTranscoder() {
this(Bitmap.CompressFormat.JPEG, 100);
}
public BitmapBytesTranscoder(Bitmap.CompressFormat compressFormat, int quality) {
this.compressFormat = compressFormat;
this.quality = quality;
}
@Override
public Resource<byte[]> transcode(Resource<Bitmap> toTranscode) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
toTranscode.get().compress(compressFormat, quality, os);
toTranscode.recycle();
return new BytesResource(os.toByteArray());
}
@Override
public String getId() {
return "BitmapBytesTranscoder.com.bumptech.glide.load.resource.transcode";
}
}
其實也比較簡單,就是一種資料型別的Resouce資源轉換為另一個種資料型別的資源。
Glide磁碟快取資源載入
無論是從磁碟快取中獲取原始圖片資源還是縮放後圖片資源,都是走的磁碟快取資源載入渠道,區別只是key不一樣。
private Resource<T> loadFromCache(Key key) throws IOException {
File cacheFile = diskCacheProvider.getDiskCache().get(key);
if (cacheFile == null) {
return null;
}
Resource<T> result = null;
try {
result = loadProvider.getCacheDecoder().decode(cacheFile, width, height);
} finally {
if (result == null) {
diskCacheProvider.getDiskCache().delete(key);
}
}
return result;
}
可以看到從磁碟快取中獲取資原始檔,然後解碼得到Resource。
1. 這裡的loadProvider是一個DataLoadProvider介面,它有很多實現,例如StreamFileDataLoadProvider(將輸入流轉換為檔案)StreamBitmapDataLoadProvider(將輸入流轉換為Bitmap)等。
2. 根據loadProvider.getCacheDecoder()獲取ResourceDecoder物件,同樣它也有對應的實現,例如FileDecoder(將檔案轉換為檔案,也就是不需要額外處理),StreamBitmapDecoder(將InputStream輸入流轉換為Bitmap)。如果沒獲取到,則刪除記錄。
Glide從網路/本地等源路徑載入圖片資源
這裡是從DecodeJob的decodeFromSource開始的
class DecodeJob<A, T, Z> {
public Resource<Z> decodeFromSource() throws Exception {
Resource<T> decoded = decodeSource();
return transformEncodeAndTranscode(decoded);
}
private Resource<T> decodeSource() throws Exception {
Resource<T> decoded = null;
try {
long startTime = LogTime.getLogTime();
final A data = fetcher.loadData(priority);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Fetched data", startTime);
}
if (isCancelled) {
return null;
}
decoded = decodeFromSourceData(data);
} finally {
fetcher.cleanup();
}
return decoded;
}
private Resource<T> decodeFromSourceData(A data) throws IOException {
final Resource<T> decoded;
if (diskCacheStrategy.cacheSource()) {
decoded = cacheAndDecodeSourceData(data);
} else {
long startTime = LogTime.getLogTime();
decoded = loadProvider.getSourceDecoder().decode(data, width, height);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded from source", startTime);
}
}
return decoded;
}
}
可以看到這裡有呼叫fetcher.loadData去獲取資料,fetcher是DataFetcher介面物件,它的實現類有很多,例如HttpUrlFetcher(根據網路url獲取輸入流)
public class HttpUrlFetcher implements DataFetcher<InputStream> {
@Override
public InputStream loadData(Priority priority) throws Exception {
return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());
}
...
}
再比如StreamAssetPathFetcher(從assets目錄的檔案中獲取輸入流)
public class StreamAssetPathFetcher extends AssetPathFetcher<InputStream> {
public StreamAssetPathFetcher(AssetManager assetManager, String assetPath) {
super(assetManager, assetPath);
}
@Override
protected InputStream loadResource(AssetManager assetManager, String path) throws IOException {
return assetManager.open(path);
}
@Override
protected void close(InputStream data) throws IOException {
data.close();
}
}
因此,你只要實現DataFetcher介面,也可實現從你自己定義的模型中獲取資料。
資源載入完成回撥
資源載入回撥是在EngineRunnable的run方法中處理的,載入失敗執行onLoadFailed,載入成功執行onLoadComplete
class EngineRunnable implements Runnable, Prioritized {
@Override
public void run() {
if (isCancelled) {
return;
}
Exception exception = null;
Resource<?> resource = null;
try {
resource = decode();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception decoding", e);
}
exception = e;
}
if (isCancelled) {
if (resource != null) {
resource.recycle();
}
return;
}
if (resource == null) {
onLoadFailed(exception);
} else {
onLoadComplete(resource);
}
}
private void onLoadComplete(Resource resource) {
manager.onResourceReady(resource);
}
private void onLoadFailed(Exception e) {
if (isDecodingFromCache()) {
stage = Stage.SOURCE;
manager.submitForSource(this);
} else {
manager.onException(e);
}
}
}
那這個EngineRunnableManager型別物件manager是什麼,它其實就是之前Engine類load方法中建立並傳進去的EngineJob物件
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
final String id = fetcher.getId();
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
EngineJob current = jobs.get(key);
if (current != null) {
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
//建立的EngineJob物件
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
//EngineJob物件添加回調
engineJob.addCallback(cb);
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
}
也就是圖片載入完成是會回撥EngineJob物件的onResourceReady方法,在onResourceReady方法中,會通過向Handler傳送訊息的方式,交給MainThreadCallback物件處理,然後又轉交回EngineJob物件handleResultOnMainThread方法處理,在handleResultOnMainThread方法中會遍歷之前註冊的ResourceCallback物件,回撥ResourceCallback物件的onResourceReady方法。
class EngineJob implements EngineRunnable.EngineRunnableManager {
private static final Handler MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper(), new MainThreadCallback());
@Override
public void onResourceReady(final Resource<?> resource) {
this.resource = resource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
private static class MainThreadCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message message) {
if (MSG_COMPLETE == message.what || MSG_EXCEPTION == message.what) {
EngineJob job = (EngineJob) message.obj;
if (MSG_COMPLETE == message.what) {
job.handleResultOnMainThread();
} else {
job.handleExceptionOnMainThread();
}
return true;
}
return false;
}
}
private void handleResultOnMainThread() {
if (isCancelled) {
resource.recycle();
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
}
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
// Hold on to resource for duration of request so we don't recycle it in the middle of notifying if it
// synchronously released by one of the callbacks.
engineResource.acquire();
listener.onEngineJobComplete(key, engineResource);
//這裡回撥之前註冊的ResourceCallback,通知資源載入完成
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource);
}
}
// Our request is complete, so we can release the resource.
engineResource.release();
}
}
通過看之前程式碼,可以知道這個ResourceCallback物件是GenericRequest類中onSizeReady方法中傳進去的,正是GenericRequest物件自己,這從邏輯上來也說的通,資源載入完成,通知請求的GenericRequest物件載入完成了。這裡繼續看回調完成的實現
public final class GenericRequest<A, T, Z, R> implements Request, SizeReadyCallback,
ResourceCallback {
/**
* A callback method that should never be invoked directly.
*/
@SuppressWarnings("unchecked")
@Override
public void onResourceReady(Resource<?> resource) {
if (resource == null) {
onException(new Exception("Expected to receive a Resource<R> with an object of " + transcodeClass
+ " inside, but instead got null."));
return;
}
Object received = resource.get();
if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
releaseResource(resource);
onException(new Exception("Expected to receive an object of " + transcodeClass
+ " but instead got " + (received != null ? received.getClass() : "") + "{" + received + "}"
+ " inside Resource{" + resource + "}."
+ (received != null ? "" : " "
+ "To indicate failure return a null Resource object, "
+ "rather than a Resource object containing null data.")
));
return;
}
if (!canSetResource()) {
releaseResource(resource);
// We can't set the status to complete before asking canSetResource().
status = Status.COMPLETE;
return;
}
onResourceReady(resource, (R) received);
}
/**
* Internal {@link #onResourceReady(Resource)} where arguments are known to be safe.
*
* @param resource original {@link Resource}, never <code>null</code>
* @param result object returned by {@link Resource#get()}, checked for type and never <code>null</code>
*/
private void onResourceReady(Resource<?> resource, R result) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
//回撥target.onResourceReady
if (requestListener == null || !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache,
isFirstResource)) {
GlideAnimation<R> animation = animationFactory.build(loadedFromMemoryCache, isFirstResource);
target.onResourceReady(result, animation);
}
notifyLoadSuccess();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Resource ready in " + LogTime.getElapsedMillis(startTime) + " size: "
+ (resource.getSize() * TO_MEGABYTE) + " fromCache: " + loadedFromMemoryCache);
}
}
private void notifyLoadSuccess() {
if (requestCoordinator != null) {
requestCoordinator.onRequestSuccess(this);
}
}
}
可以看到會回撥target.onResourceReady方法,這裡看它的其中一個實現類ImageViewTarget和BitmapImageViewTarget
public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z> implements GlideAnimation.ViewAdapter {
@Override
public void onResourceReady(Z resource, GlideAnimation<? super Z> glideAnimation) {
if (glideAnimation == null || !glideAnimation.animate(resource, this)) {
setResource(resource);
}
}
protected abstract void setResource(Z resource);
}
public class BitmapImageViewTarget extends ImageViewTarget<Bitmap> {
public BitmapImageViewTarget(ImageView view) {
super(view);
}
/**
* Sets the {@link android.graphics.Bitmap} on the view using
* {@link android.widget.ImageView#setImageBitmap(android.graphics.Bitmap)}.
*
* @param resource The bitmap to display.
*/
@Override
protected void setResource(Bitmap resource) {
view.setImageBitmap(resource);
}
}
可以看到這裡是資源針對資源Bitmap載入完成時,給ImageView設定Bitmap物件來顯示圖片的。
到這裡從開始圖片請求,到請求處理,到請求完成回撥的流程就走通了,當然其中還有很多細節是走的不同分支,大家可以自行分析。
我的GitHub
微信公眾號 hesong ,微信掃一掃下方二維碼即可關注: