上一篇博文中講了with,load的過程,因?yàn)榇蟛糠侄际浅跏荚?,所以總的來講還是比較好理解的,這篇博文講的是into的過程,這個過程就會用到前兩個步初始話的對象,這一步也是Glide的核心、難點(diǎn)所在,本文較長如果能耐心讀下去,應(yīng)該會有相應(yīng)的收獲的。
如果你發(fā)現(xiàn)本文中有任何錯誤,請?jiān)谠u論區(qū)留言或者私信我,我會第一時間改正,謝謝!
into
Glide.with(MainActivity.this).load(s).into(imageView);
傳入的一般都是ImageView,進(jìn)入到into源碼中看一下
//DrawableRequestBuilder.java
public Target<GlideDrawable> into(ImageView view) {
return super.into(view);
}
//GenericRequestBuilder.java
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));
}
首先,這里先獲取了ScaleType,并做了相應(yīng)的處理,然后執(zhí)行了glide.buildImageViewTarget(view, transcodeClass)這個方法,跟進(jìn)看一下,最后執(zhí)行了這個方法
public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
if (GlideDrawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new GlideDrawableImageViewTarget(view);
} else if (Bitmap.class.equals(clazz)) {
return (Target<Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException("Unhandled class: " + clazz
+ ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
而我們傳入的參數(shù)一個是ImageView,另外一個是在load中的loadGeneric()方法生成的 GlideDrawable.class,那么這里返回的是GlideDrawableImageViewTarget對象,這個類進(jìn)去看一下,發(fā)現(xiàn)這個類有對ImageView的setImageDrawable操作,所以盲猜一手最后會調(diào)用這個類設(shè)置圖片資源。
繼續(xù)回去看into方法集體干了什么
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())");
}
//獲取這個target之前的request
Request previous = target.getRequest();
//如果有的話 就進(jìn)行清理
if (previous != null) {
previous.clear();
requestTracker.removeRequest(previous);
previous.recycle();
}
//創(chuàng)建本次的request
Request request = buildRequest(target);//進(jìn)去看一下
target.setRequest(request);
lifecycle.addListener(target);
requestTracker.runRequest(request);
return target;
}
具體操作加了注釋了,關(guān)注一下buildrequest方法
private Request buildRequestRecursive(Target<TranscodeType> target, ThumbnailRequestCoordinator parentCoordinator) {
if (thumbnailRequestBuilder != null) {
if (isThumbnailBuilt) {
throw new IllegalStateException("You cannot use a request as both the main request and a thumbnail, "
+ "consider using clone() on the request(s) passed to thumbnail()");
}
// Recursive case: contains a potentially recursive thumbnail request builder.
if (thumbnailRequestBuilder.animationFactory.equals(NoAnimation.getFactory())) {
thumbnailRequestBuilder.animationFactory = animationFactory;
}
if (thumbnailRequestBuilder.priority == null) {
thumbnailRequestBuilder.priority = getThumbnailPriority();
}
if (Util.isValidDimensions(overrideWidth, overrideHeight)
&& !Util.isValidDimensions(thumbnailRequestBuilder.overrideWidth,
thumbnailRequestBuilder.overrideHeight)) {
thumbnailRequestBuilder.override(overrideWidth, overrideHeight);
}
ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
// Guard against infinite recursion.
isThumbnailBuilt = true;
// Recursively generate thumbnail requests.
Request thumbRequest = thumbnailRequestBuilder.buildRequestRecursive(target, coordinator);
isThumbnailBuilt = false;
coordinator.setRequests(fullRequest, thumbRequest);
return coordinator;
} else if (thumbSizeMultiplier != null) {
// Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
Request thumbnailRequest = obtainRequest(target, thumbSizeMultiplier, getThumbnailPriority(), coordinator);
coordinator.setRequests(fullRequest, thumbnailRequest);
return coordinator;
} else {
//最后進(jìn)入這里,也可以看到基本上都會調(diào)用這個方法
// Base case: no thumbnail.
return obtainRequest(target, sizeMultiplier, priority, parentCoordinator);
//生成一個GenericRequest對象返回
}
}
private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority,
RequestCoordinator requestCoordinator) {
return GenericRequest.obtain(
loadProvider,
model,
signature,
context,
priority,
target,
sizeMultiplier,
placeholderDrawable,
placeholderId,
errorPlaceholder,
errorId,
fallbackDrawable,
fallbackResource,
requestListener,
requestCoordinator,
glide.getEngine(),
transformation,
transcodeClass,
isCacheable,
animationFactory,
overrideWidth,
overrideHeight,
diskCacheStrategy);
}
可以看到最后創(chuàng)建了一個GenericRequest,返回到之前的into方法,看創(chuàng)建完request之后有干了什么
//創(chuàng)建本次的request
Request request = buildRequest(target);//進(jìn)去看一下
target.setRequest(request);
lifecycle.addListener(target);
requestTracker.runRequest(request);
return target;
requestTracker看做是一個管理request集合的類,看一下具體的方法
//RequestTracker.java
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
//執(zhí)行request
request.begin();
} else {
pendingRequests.add(request);
}
}
看一下具體的begin方法執(zhí)行了什么
//GenericRequest.java
/**
* {@inheritDoc}
*/
@Override
public void begin() {
startTime = LogTime.getLogTime();
if (model == null) {
onException(null);
return;
}
status = Status.WAITING_FOR_SIZE;
//測量長寬最終都會走到 onSizeReady方法
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
//設(shè)置占位符(圖片沒有加載成功時 加載中的那張圖片)
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
可以看到這里設(shè)置了占位圖片,同時也測量了寬高,并回調(diào)了onSizeReady方法。
@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);
//關(guān)鍵
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;
//通過engine來load圖片,我們需要關(guān)注的是這個方法及上面的傳值
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));
}
}
到這里,難點(diǎn)就來了,首先先要了解他這里的幾個參數(shù)具體是什么。
第一個 loadProvider
DrawableTypeRequest.java
private static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide,
ModelLoader<A, InputStream> streamModelLoader,
ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader, Class<Z> resourceClass,
Class<R> transcodedClass,
ResourceTranscoder<Z, R> transcoder) {
if (streamModelLoader == null && fileDescriptorModelLoader == null) {
return null;
}
if (transcoder == null) {
transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
}
DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class,
resourceClass);
ImageVideoModelLoader<A> modelLoader = new ImageVideoModelLoader<A>(streamModelLoader,
fileDescriptorModelLoader);
return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);
}
就是我們在load最后生成的FixedLoadProvider。那么
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader() 獲取到的ModelLoader為ImageVideoModelLoader
DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height); dataFetcher是ImageVideoFetcher
接下來就是主菜load過程了
//Engine.java
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();
//根據(jù)長寬等條件生成一個key
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
//根據(jù)上面生成的key向緩存中讀取資源
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);
}
這個方法很長,一步步看,首先看一下獲取緩存的方法
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
}
return cached;
}
@SuppressWarnings("unchecked")
private EngineResource<?> getEngineResourceFromCache(Key key) {
Resource<?> cached = cache.remove(key);
final EngineResource result;
if (cached == null) {
result = null;
} else if (cached instanceof EngineResource) {
// Save an object allocation if we've cached an EngineResource (the typical case).
result = (EngineResource) cached;
} else {
result = new EngineResource(cached, true /*isCacheable*/);
}
return result;
}
根據(jù)這一串代碼和下面的注釋可以推斷出是從內(nèi)存中直接獲取出來的,通過追蹤我們可以了解到這個是從LruResourceCache中獲取出來的,看一下LruResourceCache的構(gòu)造
public class LruResourceCache extends LruCache<Key, Resource<?>> implements MemoryCache {
private ResourceRemovedListener listener;
......
這里就不先展開了講了,先知道有緩存這個東西,重點(diǎn)走一遍完整的網(wǎng)絡(luò)請求流程。再重新回到load方法
//看看有沒有這個key正在執(zhí)行的方法,有的話就不new了,直接更新新的監(jiān)聽
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);
}
//通過engineJobFactory根據(jù)key新建一個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);
//創(chuàng)建一個EngineRunnable,這個是實(shí)現(xiàn)圖片加載主要的類
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
//放到正在請求的隊(duì)列中
jobs.put(key, engineJob);
engineJob.addCallback(cb);
//啟動EngineRunnable線程
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
可以看到這里又建了幾個類,我們拿出來看看
EngineJob:執(zhí)行獲取圖片的類,相當(dāng)于小包工頭
EngineRunnable:執(zhí)行從內(nèi)存,網(wǎng)絡(luò)中獲取圖片的類,相當(dāng)于小包工頭(EngineJob)下面的建筑工人組長
DecodeJob:真正執(zhí)行內(nèi)存,網(wǎng)絡(luò)中獲取圖片工作的類,相當(dāng)于上面兩個管理下的工人
先看一下engineJob.start的實(shí)現(xiàn)
private final ExecutorService diskCacheService;
public void start(EngineRunnable engineRunnable) {
this.engineRunnable = engineRunnable;
future = diskCacheService.submit(engineRunnable);
}
先用diskCacheService執(zhí)行了這個方法,diskCacheService這個對象根據(jù)名字可以看出是專門用來執(zhí)行硬盤緩存的線程池,那么接下來看一下EngineRunnable的方法
//EngineRunnable.java
@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);
}
}
這個方法看上去就很清晰了,通過 decode()方法獲取到資源,最后通過onLoadFailed,onLoadComplete來返回結(jié)果,需要看一下decode方法
private Resource<?> decode() throws Exception {
//不設(shè)置的話這個判斷第一次是會放回true
if (isDecodingFromCache()) {
return decodeFromCache();
} else {
return decodeFromSource();
}
}
//這個方法主要是用來判斷當(dāng)前執(zhí)行的是不是從cache中獲取,第一次進(jìn)來的時候默認(rèn)都是從cache中獲取
private boolean isDecodingFromCache() {
return stage == Stage.CACHE;
}
接下來看一下decodeFromCache方法
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;
}
//decodeJob.java
/**
* Returns a transcoded resource decoded from transformed resource data in the disk cache, or null if no such
* resource exists.
*
* @throws Exception
*/
public Resource<Z> decodeResultFromCache() throws Exception {
if (!diskCacheStrategy.cacheResult()) {
return null;
}
long startTime = LogTime.getLogTime();
Resource<T> transformed = loadFromCache(resultKey);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded transformed from cache", startTime);
}
startTime = LogTime.getLogTime();
Resource<Z> result = transcode(transformed);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transcoded transformed from cache", startTime);
}
return result;
}
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;
}
不難看出這一層是使用diskLruCache來獲取緩存的,這里先跳過,等到后面的博文在講解
如果獲取到了資源走onLoadComplete方法,如果獲取不到走onLoadFailed方法,我們看一下onLoadFailed的實(shí)現(xiàn)。
//EngineRunnable.java
private void onLoadFailed(Exception e) {
if (isDecodingFromCache()) {
//第一次失敗了之后會走到這里,從disk緩存獲取切換到從SOURCE獲取
stage = Stage.SOURCE;
//重新執(zhí)行一遍run方法
manager.submitForSource(this);
} else {
manager.onException(e);
}
}
根據(jù)注釋,可以了解到又執(zhí)行了一遍run方法,這時候就會走到decodeFromSource方法了
//EngineRunnable.java
private Resource<?> decodeFromSource() throws Exception {
return decodeJob.decodeFromSource();
}
//DecodeJob.java
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();
//這一步獲取到了相應(yīng)的數(shù)據(jù)
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;
}
看到這里,估計(jì)很多人都要奔潰了,這個fetcher代表什么?從哪里得到的?
我們回頭看,這個fetcher是從GenericRequest的onSizeReady中獲取出來的,而他的創(chuàng)建在更早之前,回過去找一下具體的類
//ImageVideoModelLoader
static class ImageVideoFetcher implements DataFetcher<ImageVideoWrapper> {
private final DataFetcher<InputStream> streamFetcher;
private final DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher;
public ImageVideoFetcher(DataFetcher<InputStream> streamFetcher,
DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher) {
this.streamFetcher = streamFetcher;
this.fileDescriptorFetcher = fileDescriptorFetcher;
}
@SuppressWarnings("resource")
// @see ModelLoader.loadData
@Override
public ImageVideoWrapper loadData(Priority priority) throws Exception {
InputStream is = null;
if (streamFetcher != null) {
try {
is = streamFetcher.loadData(priority);
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception fetching input stream, trying ParcelFileDescriptor", e);
}
if (fileDescriptorFetcher == null) {
throw e;
}
}
}
ParcelFileDescriptor fileDescriptor = null;
if (fileDescriptorFetcher != null) {
try {
fileDescriptor = fileDescriptorFetcher.loadData(priority);
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception fetching ParcelFileDescriptor", e);
}
if (is == null) {
throw e;
}
}
}
return new ImageVideoWrapper(is, fileDescriptor);
}
...
}
這樣一來,問題又成為了streamFetcher是啥。
//ImageVideoModelLoader.java
@Override
public DataFetcher<ImageVideoWrapper> getResourceFetcher(A model, int width, int height) {
DataFetcher<InputStream> streamFetcher = null;
if (streamLoader != null) {
streamFetcher = streamLoader.getResourceFetcher(model, width, height);
}
DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher = null;
if (fileDescriptorLoader != null) {
fileDescriptorFetcher = fileDescriptorLoader.getResourceFetcher(model, width, height);
}
if (streamFetcher != null || fileDescriptorFetcher != null) {
return new ImageVideoFetcher(streamFetcher, fileDescriptorFetcher);
} else {
return null;
}
}
回頭去看load的過程中創(chuàng)建的對象
//RequestManager.java
private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
Glide.buildFileDescriptorModelLoader(modelClass, context);
if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) {
throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for"
+ " which there is a registered ModelLoader, if you are using a custom model, you must first call"
+ " Glide#register with a ModelLoaderFactory for your custom model class");
}
return optionsApplier.apply(
new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
glide, requestTracker, lifecycle, optionsApplier));
}
//Glide.java
//傳入的class是String
public static <T> ModelLoader<T, InputStream> buildStreamModelLoader(Class<T> modelClass, Context context) {
return buildModelLoader(modelClass, InputStream.class, context);
}
可以看出是需要的是streamModelLoader,fileDescriptorModelLoader 這個對象,這個值獲取的較為復(fù)雜,具體的這里不講解,可以把這個方法看作是從注冊工廠中獲取出對應(yīng)的解析器即可,那么什么時候注冊的解析器的呢?答案就在第一步with中初始話Glide中完成,我這里貼一下對應(yīng)關(guān)系
//GenericLoaderFactory注冊
register(File.class, ParcelFileDescriptor.class, new FileDescriptorFileLoader.Factory());
register(File.class, InputStream.class, new StreamFileLoader.Factory());
register(int.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
register(int.class, InputStream.class, new StreamResourceLoader.Factory());
register(Integer.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
register(Integer.class, InputStream.class, new StreamResourceLoader.Factory());
register(String.class, ParcelFileDescriptor.class, new FileDescriptorStringLoader.Factory());
register(String.class, InputStream.class, new StreamStringLoader.Factory());
register(Uri.class, ParcelFileDescriptor.class, new FileDescriptorUriLoader.Factory());
register(Uri.class, InputStream.class, new StreamUriLoader.Factory());
register(URL.class, InputStream.class, new StreamUrlLoader.Factory());
register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
register(byte[].class, InputStream.class, new StreamByteArrayLoader.Factory());
//TranscoderRegistry 注冊
transcoderRegistry.register(Bitmap.class, GlideBitmapDrawable.class,
new GlideBitmapDrawableTranscoder(context.getResources(), bitmapPool));
transcoderRegistry.register(GifBitmapWrapper.class, GlideDrawable.class,
new GifBitmapWrapperDrawableTranscoder(
new GlideBitmapDrawableTranscoder(context.getResources(), bitmapPool)));
具體舉例,loadGeneric傳入的是String,再看Glide.buildStreamModelLoader另一個參數(shù)是InputStream,那么對應(yīng)的就是
register(String.class, InputStream.class, new StreamStringLoader.Factory());
再看一下代碼
public class StreamStringLoader extends StringLoader<InputStream> implements StreamModelLoader<String> {
/**
* The default factory for {@link com.bumptech.glide.load.model.stream.StreamStringLoader}s.
*/
public static class Factory implements ModelLoaderFactory<String, InputStream> {
@Override
public ModelLoader<String, InputStream> build(Context context, GenericLoaderFactory factories) {
return new StreamStringLoader(factories.buildModelLoader(Uri.class, InputStream.class));
}
@Override
public void teardown() {
// Do nothing.
}
}
public StreamStringLoader(Context context) {
this(Glide.buildStreamModelLoader(Uri.class, context));
}
public StreamStringLoader(ModelLoader<Uri, InputStream> uriLoader) {
super(uriLoader);
}
}
這里也調(diào)用了buildModelLoader這個方法,那么這里就形成了一個類似遞歸的東西
分別是
register(String.class, InputStream.class, new StreamStringLoader.Factory()); register(Uri.class, InputStream.class, new StreamUriLoader.Factory()); register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
看一下HttpUrlGlideUrlLoader的代碼
//HttpUrlFetcher.java
@Override
public InputStream loadData(Priority priority) throws Exception {
return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());
}
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers)
throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {
throw new IOException("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 IOException("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(2500);
urlConnection.setReadTimeout(2500);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
// Connect explicitly to avoid errors in decoders if connection fails.
urlConnection.connect();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (statusCode / 100 == 2) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (statusCode / 100 == 3) {
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new IOException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else {
if (statusCode == -1) {
throw new IOException("Unable to retrieve response code from HttpUrlConnection.");
}
throw new IOException("Request failed " + statusCode + ": " + urlConnection.getResponseMessage());
}
}
private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
throws IOException {
if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
int contentLength = urlConnection.getContentLength();
stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
} else {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding());
}
stream = urlConnection.getInputStream();
}
return stream;
}
這個方法就是真正網(wǎng)絡(luò)請求的方法了,也對結(jié)果進(jìn)行初步的處理,返回一個InputStream,那么在返回去看一下前面的邏輯,一層層的返回回去,最后到了
//DecodeJob.java
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;
}
//進(jìn)行讀取到的數(shù)據(jù)的存儲及轉(zhuǎn)化
decoded = decodeFromSourceData(data);
} finally {
fetcher.cleanup();
}
return decoded;
}
繼續(xù)看下去
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;
}
private void writeTransformedToCache(Resource<T> transformed) {
if (transformed == null || !diskCacheStrategy.cacheResult()) {
return;
}
long startTime = LogTime.getLogTime();
SourceWriter<Resource<T>> writer = new SourceWriter<Resource<T>>(loadProvider.getEncoder(), transformed);
diskCacheProvider.getDiskCache().put(resultKey, writer);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Wrote transformed from source to cache", startTime);
}
}
transformEncodeAndTranscode這個主要是做內(nèi)存緩存,圖片類型裝換的本文先不解析
EngineRunnable
@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) {
//成功時調(diào)用
manager.onResourceReady(resource);
}
private void onLoadFailed(Exception e) {
//失敗時調(diào)用
if (isDecodingFromCache()) {
stage = Stage.SOURCE;
manager.submitForSource(this);
} else {
manager.onException(e);
}
}
這個manager是在Engine中通過engineJob決定
//Engine.java
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);
//EngineRunnable
public EngineRunnable(EngineRunnableManager manager, DecodeJob<?, ?, ?> decodeJob, Priority priority) {
this.manager = manager;
this.decodeJob = decodeJob;
this.stage = Stage.CACHE;
this.priority = priority;
}
那么看一下EngineJob里面的內(nèi)容
//EngineJob.java
@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);
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource);
}
}
// Our request is complete, so we can release the resource.
engineResource.release();
}
EngineJob里面通過handler完成了線程的切換,然后執(zhí)行handleResultOnMainThread方法,本文主要關(guān)注的是cb.onResourceReady(engineResource);
這個cb是需要追溯到GenericRequest這個類中,具體的方法是
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);
可以看到他把自己作為一個callback傳了進(jìn)去,所以我們要關(guān)注的是他的相關(guān)方法
@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);
}
private void onResourceReady(Resource<?> resource, R result) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
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);
}
}
這邊邏輯就很清楚了,獲取除了資源文件在判空,然后獲取到對應(yīng)的動畫效果,并調(diào)用target.onResourceReady(result, animation);方法,這個target最早之前獲取的GlideDrawableImageViewTarget
//GlideDrawableImageViewTarget
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) {
if (!resource.isAnimated()) {
float viewRatio = view.getWidth() / (float) view.getHeight();
float drawableRatio = resource.getIntrinsicWidth() / (float) resource.getIntrinsicHeight();
if (Math.abs(viewRatio - 1f) <= SQUARE_RATIO_MARGIN
&& Math.abs(drawableRatio - 1f) <= SQUARE_RATIO_MARGIN) {
resource = new SquaringDrawable(resource, view.getWidth());
}
}
super.onResourceReady(resource, animation);
this.resource = resource;
resource.setLoopCount(maxLoopCount);
resource.start();
}
看一下onResourceReady方法
//ImageViewTarget.java
@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);
setResource的具體實(shí)現(xiàn)類還是他本身
@Override
protected void setResource(GlideDrawable resource) {
view.setImageDrawable(resource);
}
oh,終于把圖片放進(jìn)View里面,這樣一套從網(wǎng)絡(luò)加載圖片的流程也走完了!Congratulations!
當(dāng)然,由于篇幅緩存那一塊的邏輯本文暫時沒講,后續(xù)找機(jī)會補(bǔ)上。最后,老慣例總結(jié)一下內(nèi)容,into的主要流程是:
首先,先會處理ImageView的ScaleType,然后把ImageView處理成一個Target,接著看這個target是否有前一個圖片加載請求,如果有就標(biāo)記無效掉他并清空狀態(tài),如果沒有就創(chuàng)建出一個request并給這個request創(chuàng)建回調(diào)及監(jiān)聽(with中創(chuàng)建的空白的fragment),然后通過requestTracker執(zhí)行這個request。
緊接著,會測量出target的長寬并回調(diào)onSizeReady方法,在這個同時會設(shè)置占位圖。onSizeReady方法里面,除了賦值長寬之外,還會啟動Engine的load方法,在load中首先會獲取到一個EngineKey,這個key是由長,寬,地址等構(gòu)成,組件完成之后,先會查看是否有這個EngineKey的EngineJob在執(zhí)行,如果有的話,就不再請求了,采用addCallBack回調(diào)集合的方式完成圖片的下載。如果沒有的話,就通過EngineJobFactory創(chuàng)建一個新的EngineJob,同時創(chuàng)建decodeJob和EngineRunnable,然后通過EngineJob來執(zhí)行EngineRunning。
首先執(zhí)行的從disk緩存中獲取圖片,如果獲取不到,執(zhí)行從Source中獲取圖片的方法。這個方法就是請求網(wǎng)絡(luò)的方法,是由之前在load方法中創(chuàng)建的SteamFetcher來作為下載圖片的工具,這個SteamFetcher是由load的對象決定,返回的對象是InputSteam。
下載完成獲取到InputSteam之后,decodeJob把InputSteam轉(zhuǎn)化成Resource對象。接著調(diào)用EngineRunning的onLoadComplete方法,onLoadComplete會回調(diào)EngineJob的onResourceReady方法,onResourceReady會向EngineJob中的handler發(fā)送一條消息,并切換到主線程。切換到主線程之后,EngineJob又會回調(diào)之前callback集合,通知圖片已經(jīng)準(zhǔn)備完成,這個callback實(shí)現(xiàn)實(shí)在GenericRequest中。接下來,EngineJob中的callback會調(diào)用target.onResourceReady方法,這個方法最后也會調(diào)用到target.setResource方法,把獲取到的圖片設(shè)置到對應(yīng)的ImagView中。
當(dāng)然這這個總結(jié)是正對于網(wǎng)絡(luò)下載圖片的,沒涉及到緩存,估計(jì)下一篇博文講完緩存之后,會把緩存相關(guān)的補(bǔ)充到總結(jié)中。
看到這里,還不獎勵自己一下么?