前言
移動應用幾乎都有圖片加載的需求,很多時候需要從遠程加載,有時也需要從本地加載,以前都往往是自己實現,這就需要考慮各種各樣的情況,比如緩存策略,需要綜合考慮內存使用,不同的圖片有不同時間,不同空間的緩存策略,其次是加載策略,是原圖加載,還是需要裁剪,是一次生成多種尺寸的縮略,還是不生成,有時還需要考慮網絡狀況來加載更小尺寸的圖。加載的圖片是否是gif,還需要對圖片的展示進程處理。有時還是從assert中加載的,或者從sd卡中加載的,不同的加載路徑怎麼拆分,怎麼纔有更好的代碼設計,這都是對代碼能力的一個考量。
之後就逐步出現了很多的圖片加載框架,這些框架現在也應該使用在衆多的APP中,他們實現的更全面,功能更強大,有良好的擴展能力。把程序員從衆多的邏輯中解放出來,雖然使用更簡單了,但是我們要知其然也要知其所以然。接下來我們就梳理一下Glide的加載流程。
使用
在分析之前我們先來看一個使用的樣例。這裏我們來加載一個遠程url的圖像,並且生成爲圓形頭像。
/**
* 加載圓形頭像
*
* @param context
* @param url
* @param view
*/
public static final void loadCircleImage(Context context, String url, ImageView view, int defaultAvatar) {
Glide.with(context).load(url).centerCrop().bitmapTransform(new
CropCircleTransformation(context)).diskCacheStrategy(DiskCacheStrategy.ALL).placeholder
(defaultAvatar).error(defaultAvatar).into(view);
}
這裏佔位圖與錯誤圖都使用一個默認的資源。上面的代碼的意思很清楚明瞭,就是加載地址爲url的圖片,並縮放類型爲CenterCrop,之後transformation爲圓形,緩存策略爲所有尺寸緩存,佔位圖,錯誤圖都指定爲defaultAvatar,最終展示到傳入的view。
代碼跟蹤
這裏我們就解析上面url的加載類型,其他的都是類似的。
第一步Glide.with(context)
public static RequestManager with(Context context) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(context);
}
這裏主要是獲得一個RequestManager,首先獲取一個RequestManagerRetriever,RequestManagerRetriever是一個單例,get就是獲取RequestManagerRetriever的實例,這更加context獲取一個RequestManager,retriever.get(context)代碼如下:
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) {
return get(((ContextWrapper) context).getBaseContext());
}
}
return getApplicationManager(context);
}
public RequestManager get(FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm);
}
}
public RequestManager get(Fragment fragment) {
if (fragment.getActivity() == null) {
throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached");
}
if (Util.isOnBackgroundThread()) {
return get(fragment.getActivity().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getActivity(), fm);
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public RequestManager get(Activity activity) {
if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm);
}
}
上面的代碼只是根據不同的context來創建不同的requestManager,如果不在u線程,context不是Application則全局創建一個,比如這裏傳入的Context類型爲Activity,且在ui線程,那會加載參數類型爲Activity的get函數,這裏又會進入的fragmentGet函數:
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
RequestManagerFragment current = getRequestManagerFragment(fm);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}
這裏首先根據tag "com.bumptech.glide.manager"
獲取Fragment,這裏的Fragment的類型RequestManagerFragment,獲取不到會創建一個家人的FragmentManager中,這裏假設首次調用,因此requestManager爲空,則會創建一個並且設置到Fragment,RequestManager構造如下:
public RequestManager(Context context, Lifecycle lifecycle, RequestManagerTreeNode treeNode) {
this(context, lifecycle, treeNode, new RequestTracker(), new ConnectivityMonitorFactory());
}
RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode,
RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
this.context = context.getApplicationContext();
this.lifecycle = lifecycle;
this.treeNode = treeNode;
this.requestTracker = requestTracker;
this.glide = Glide.get(context);
this.optionsApplier = new OptionsApplier();
ConnectivityMonitor connectivityMonitor = factory.build(context,
new RequestManagerConnectivityListener(requestTracker));
// If we're the application level request manager, we may be created on a background thread. In that case we
// cannot risk synchronously pausing or resuming requests, so we hack around the issue by delaying adding
// ourselves as a lifecycle listener by posting to the main thread. This should be entirely safe.
if (Util.isOnBackgroundThread()) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
lifecycle.addListener(RequestManager.this);
}
});
} else {
lifecycle.addListener(this);
}
lifecycle.addListener(connectivityMonitor);
}
傳入的參數爲Context,lifecycle,已經請求樹結點,這個就不解析, 之後三參數的調用了無參數的構造函數,構造了一個請求跟蹤器RequestTracker,並在在lifecycle中加入了網絡連接的廣播,後面會根據網絡的鏈接是否重發請求:
private static class RequestManagerConnectivityListener implements ConnectivityMonitor.ConnectivityListener {
private final RequestTracker requestTracker;
public RequestManagerConnectivityListener(RequestTracker requestTracker) {
this.requestTracker = requestTracker;
}
@Override
public void onConnectivityChanged(boolean isConnected) {
if (isConnected) {
requestTracker.restartRequests();
}
}
}
上述代碼就是當網絡從斷開到鏈接變化時,重發請求。
設置請求參數
前面解析了RequestManager的構造過程,之後我們load了url,設置縮放類型,加入了緩存策略,設置了圖片裁剪方式,設置了佔位圖與加載失敗後的錯誤圖,這裏我們看看load url的過程。
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}
public DrawableTypeRequest<String> fromString() {
return loadGeneric(String.class);
}
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));
}
首先調用了load參數爲string,返回一個DrawableTypeRequest, 這裏注意參數類型,之後接着調用了fromString的load,而fromString又調用了loadGeneric, 我們來看看loadGeneric的過程:
1.Glide.buildStreamModelLoader(modelClass, context);
首先根據modelClass構造了streamModelLoader,這裏的modelClass類型爲String,之後我們會看看構造過程
2. Glide.buildFileDescriptorModelLoader(modelClass, context)
根據modelClass構造了fileDescriptorModelLoader,這裏的modelClass類型爲String,
根據上面的構造的兩種loader創建DrawableTypeRequest,
3.optionsApplier爲RequestManager中構造的,但是這裏它裏面調用的options爲空,因此不做任何操作。
我們繼續看看buildStreamModelLoader,他裏面繼續調用瞭如下的函數:
public static <T> ModelLoader<T, InputStream> buildStreamModelLoader(Class<T> modelClass, Context context) {
return buildModelLoader(modelClass, InputStream.class, context);
}
public static <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, Class<Y> resourceClass,
Context context) {
if (modelClass == null) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Unable to load null model, setting placeholder only");
}
return null;
}
return Glide.get(context).getLoaderFactory().buildModelLoader(modelClass, resourceClass);
}
這裏繼續調用了三個參數的buildModelLoader, modelClass爲String, ResourceClass爲InputStream,context還是外部傳過來的上下文。裏面繼續調用了Glide.get(context).getLoaderFactory().buildModelLoader(),這裏首先獲取Glide的實例,Glide是一個單例這裏要單獨說說他們的實例話過程,後續會用到Glide實例的參數。等Glide分析後再回來解析DrawableTypeRequest的創建過程。
Glide初始化
Glide.get(context)首先實例化:
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;
}
這裏實例化雙重檢查,因爲我們前面已經看到了可能會多線程加載,ui線程,後臺線程同時加載,雙重檢查構造單例。首先解析AndroidManifest中配置的Meta_data節點,key爲GlideModule,這裏我們沒有配置。之後構造一個GlideBuilder,主要是設置了context,在之後調用createGlide生成一個Glide實例。createGlide如下:
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,首先設置一個先進先出的ThreadPoolExecutor
- diskCacheService, 磁盤緩存先進先出的Executor
- bitmapPool, bitmap緩存,現在一般都是api11以上,因此是LruBitmapPool
- memoryCache 內存緩存
- diskCacheFactory, 磁盤緩存工廠
- engine,加載管理緩存引擎
- decodeFormat,解碼格式,默認RGB_565,佔用兩個字節
最後根據上訴的參數調用Glide的構造函數:
Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, Context context, DecodeFormat decodeFormat) {
this.engine = engine;
this.bitmapPool = bitmapPool;
this.memoryCache = memoryCache;
this.decodeFormat = decodeFormat;
loaderFactory = new GenericLoaderFactory(context);
mainHandler = new Handler(Looper.getMainLooper());
bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat);
dataLoadProviderRegistry = new DataLoadProviderRegistry();
StreamBitmapDataLoadProvider streamBitmapLoadProvider =
new StreamBitmapDataLoadProvider(bitmapPool, decodeFormat);
dataLoadProviderRegistry.register(InputStream.class, Bitmap.class, streamBitmapLoadProvider);
FileDescriptorBitmapDataLoadProvider fileDescriptorLoadProvider =
new FileDescriptorBitmapDataLoadProvider(bitmapPool, decodeFormat);
dataLoadProviderRegistry.register(ParcelFileDescriptor.class, Bitmap.class, fileDescriptorLoadProvider);
ImageVideoDataLoadProvider imageVideoDataLoadProvider =
new ImageVideoDataLoadProvider(streamBitmapLoadProvider, fileDescriptorLoadProvider);
dataLoadProviderRegistry.register(ImageVideoWrapper.class, Bitmap.class, imageVideoDataLoadProvider);
GifDrawableLoadProvider gifDrawableLoadProvider =
new GifDrawableLoadProvider(context, bitmapPool);
dataLoadProviderRegistry.register(InputStream.class, GifDrawable.class, gifDrawableLoadProvider);
dataLoadProviderRegistry.register(ImageVideoWrapper.class, GifBitmapWrapper.class,
new ImageVideoGifDrawableLoadProvider(imageVideoDataLoadProvider, gifDrawableLoadProvider, bitmapPool));
dataLoadProviderRegistry.register(InputStream.class, File.class, new StreamFileDataLoadProvider());
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.register(Bitmap.class, GlideBitmapDrawable.class,
new GlideBitmapDrawableTranscoder(context.getResources(), bitmapPool));
transcoderRegistry.register(GifBitmapWrapper.class, GlideDrawable.class,
new GifBitmapWrapperDrawableTranscoder(
new GlideBitmapDrawableTranscoder(context.getResources(), bitmapPool)));
bitmapCenterCrop = new CenterCrop(bitmapPool);
drawableCenterCrop = new GifBitmapWrapperTransformation(bitmapPool, bitmapCenterCrop);
bitmapFitCenter = new FitCenter(bitmapPool);
drawableFitCenter = new GifBitmapWrapperTransformation(bitmapPool, bitmapFitCenter);
}
這裏設置的參數後面都會用到,可以進行反查
可以看到不僅設置了衆多的參數,還register了衆多的ModelLoaderFactory,註冊過程就不詳細解析了,只是爲了說明初始化了這些參數,現在我們繼續回到之前的Glide.buildStreamModelLoader(modelClass, context)。
buildStreamModelLoader
上面中斷的過程主要是因爲buildStreamModelLoader用到了Glide初始化中的參數,這裏我們繼續往下看。他先調用了getLoaderFactory()獲得loaderFactory,loaderFactory是在Glide初始化創建的,並且註冊了衆多的ModelLoaderFactory。之後調用了buildModelLoader生成一個ModelLoader:
public synchronized <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, Class<Y> resourceClass) {
ModelLoader<T, Y> result = getCachedLoader(modelClass, resourceClass);
if (result != null) {
// We've already tried to create a model loader and can't with the currently registered set of factories,
// but we can't use null to demonstrate that failure because model loaders that haven't been requested
// yet will be null in the cache. To avoid this, we use a special signal model loader.
if (NULL_MODEL_LOADER.equals(result)) {
return null;
} else {
return result;
}
}
final ModelLoaderFactory<T, Y> factory = getFactory(modelClass, resourceClass);
if (factory != null) {
result = factory.build(context, this);
cacheModelLoader(modelClass, resourceClass, result);
} else {
// We can't generate a model loader for the given arguments with the currently registered set of factories.
cacheNullLoader(modelClass, resourceClass);
}
return result;
}
這裏modelClass爲String,resourceClass爲InputStream,首先在cache中獲取,第一次調用cache中是沒有的,因此會調用getFactory(modelClass, resourceClass)獲得ModelLoaderFactory,會根據modelClass, resourceClass來獲取對應的ModelLoaderFactory,我們去Glide的構造函數中看看註冊爲String與InputStream的Factory是什麼?
register(String.class, InputStream.class, new StreamStringLoader.Factory());
因此這裏的ModelLoaderFactory爲StreamStringLoader.Factory(), 因此我們看看他的Build函數:
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.
}
}
這裏又繼續調用了factories.buildModelLoader(Uri.class, InputStream.class),只不過第一個參數已經轉變成了Uri類型,因此我們查查註冊爲Uri與InputStream的ModelLoaderFactory,因此又會調用到StreamUriLoader.Factory()的build函數,這裏又級聯調用了factories.buildModelLoader(GlideUrl.class, InputStream.class),因此最終又調用到了HttpUrlGlideUrlLoader.Factory(),最終返回了HttpUrlGlideUrlLoader。
buildFileDescriptorModelLoader
buildFileDescriptorModelLoader的整個流程與buildStreamModelLoader類似,因此這個就不展開了。
DrawableTypeRequest
構造了streamModelLoader與fileDescriptorModelLoader後根據這些參數構造了DrawableTypeRequest,DrawableTypeRequest首先調用了DrawableRequestBuilder的構造函數:
DrawableTypeRequest(Class<ModelType> modelClass, ModelLoader<ModelType, InputStream> streamModelLoader,
ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader, Context context, Glide glide,
RequestTracker requestTracker, Lifecycle lifecycle, RequestManager.OptionsApplier optionsApplier) {
super(context, modelClass,
buildProvider(glide, streamModelLoader, fileDescriptorModelLoader, GifBitmapWrapper.class,
GlideDrawable.class, null),
glide, requestTracker, lifecycle);
this.streamModelLoader = streamModelLoader;
this.fileDescriptorModelLoader = fileDescriptorModelLoader;
this.optionsApplier = optionsApplier;
}
這裏有一個buildProvider的過程,根據參數創建了FixedLoadProvider。我們又回到最初設置參數的地方,load url,前面只是解析了fromString,還沒有設置最終的url參數,最後根據DrawableTypeRequest設置各種參數,比如url,設置bitmapTransform參數,各種參數就不展開了。
加載過程
我們的調用樣例中最後一步是into(view),我們可以看到裏面調用了super.into(view)也就是GenericRequestBuilder的into函數,代碼如下:
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));
}
首先判斷view是否爲空,如果爲空,拋出異常,之後判斷是否設置了ScaleType,根據設置的效果來處理。最後繼續調用into函數,不過先將View重新build成ViewTarget,調用:
<R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
}
//
public class ImageViewTargetFactory {
@SuppressWarnings("unchecked")
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)");
}
}
}
這裏imageView就是傳入的view,transcodedClass的值又是什麼吶? 前面我們得到了DrawableTypeRequest,他的父類爲DrawableRequestBuilder,DrawableRequestBuilder的父類爲GenericRequestBuilder,我們在DrawableTypeRequest的構造函數中調用了DrawableRequestBuilder,這裏我們傳入了transcodedClass,爲GlideDrawable.class,因此最終這裏的返回的是GlideDrawableImageViewTarget,我們繼續看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())");
}
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;
}
首先判斷是不是ui線程,androd中各種控件的展示都是在UI線程操作的,之後判斷target是否爲空,爲空拋出異常,之後獲取上一次request,這裏第一次調用previous爲空,之後調用buildRequest構造一個request,buildRequest首先設置了優先級,之後繼續調用buildRequestRecursive這裏就不貼代碼了,先判斷縮略請求是否爲空,多尺寸縮略是否爲空,否則就調用obtainRequest獲的request,obtainRequest裏面又繼續調用GenericRequest.obtain獲得request,obtain中的參數就是之前構造時候設置的參數。這裏就不展開了。
之後將request設置到target,lifecycle添加一個listener,最後requestTracker調用runRequest開始執行該request:
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
首先將request加入到請求隊列中,如果當前頁面不是paused狀態,則開始調用request的begin函數,否則加入pending隊列,當狀態變爲resume時,繼續執行,我們繼續看看begin函數:
@Override
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));
}
}
這裏實際調用的是GenericRequest的begin函數,首先記錄開始時間,之後設置status爲WAITING_FOR_SIZE,等待獲取圖片的尺寸,如果當前寬高已經獲取到了,調用onSizeReady,否則調用getSize,getSize在尺寸確定後會調用回調SizeReadyCallback的onSizeReady函數,之後如果還沒有加載完成活失敗,則先顯示佔位圖。
注意展位圖只能是resource資源
我們繼續看看他是怎麼獲得圖片的尺寸的,getSize會調用父類的getSize,這裏是一個接口,實際調用的是ViewTarget的getSize,爲什麼是ViewTarget,還記得我們之前buildTarget過程吧!這裏又繼續調用了 sizeDeterminer.getSize(cb):
public void getSize(SizeReadyCallback cb) {
int currentWidth = getViewWidthOrParam();
int currentHeight = getViewHeightOrParam();
if (isSizeValid(currentWidth) && isSizeValid(currentHeight)) {
cb.onSizeReady(currentWidth, currentHeight);
} else {
// We want to notify callbacks in the order they were added and we only expect one or two callbacks to
// be added a time, so a List is a reasonable choice.
if (!cbs.contains(cb)) {
cbs.add(cb);
}
if (layoutListener == null) {
final ViewTreeObserver observer = view.getViewTreeObserver();
layoutListener = new SizeDeterminerLayoutListener(this);
observer.addOnPreDrawListener(layoutListener);
}
}
}
這裏add系統OnPreDrawListener回調,根據系統回調來確定的寬高。
onSizeReady
我們來着重看看onSizeReady函數:
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));
}
}
先判斷狀態是不是等待獲取尺寸,如果不是表示已經執行過了,之後將狀態設置Running狀態,之後重新計算寬高,不過這裏sizeMultiplier爲1,尺寸其實不變,之後獲取ModelLoader,ModelLoader我們在上面已經分析過了。
- 首先是StreamStringLoader
- 其次是StreamUriLoader
- 最後是HttpUrlGlideUrlLoader
之後調用modelLoader.getResourceFetcher(model, width, height),那這裏首先調用的StreamStringLoader的getResourceFetcher,可以發現他根本沒有getResourceFetcher這個函數,他實際調用的是StringLoader的getResourceFetcher:
@Override
public DataFetcher<T> getResourceFetcher(String model, int width, int height) {
Uri uri;
if (TextUtils.isEmpty(model)) {
return null;
} else if (model.startsWith("/")) {
uri = toFileUri(model);
} else {
uri = Uri.parse(model);
final String scheme = uri.getScheme();
if (scheme == null) {
uri = toFileUri(model);
}
}
return uriLoader.getResourceFetcher(uri, width, height);
}
modle就是傳入的url,如果url爲空直接返回空,如果傳入串是’/’開頭的,表示從本地讀取,會在串的前面加入“file”標識,否則轉換爲Uri,獲取Scheme,接着調用uriLoader.getResourceFetcher(uri, width, height),這裏的uriLoader爲StreamUriLoader,因此調用的是StreamUriLoader的getResourceFetcher,最終調用的是UriLoader的getResourceFetcher:
@Override
public final DataFetcher<T> getResourceFetcher(Uri model, int width, int height) {
final String scheme = model.getScheme();
DataFetcher<T> result = null;
if (isLocalUri(scheme)) {
if (AssetUriParser.isAssetUri(model)) {
String path = AssetUriParser.toAssetPath(model);
result = getAssetPathFetcher(context, path);
} else {
result = getLocalUriFetcher(context, model);
}
} else if (urlLoader != null && ("http".equals(scheme) || "https".equals(scheme))) {
result = urlLoader.getResourceFetcher(new GlideUrl(model.toString()), width, height);
}
return result;
}
如果scheme爲file,content,android:resource都表示從本地獲取,否則如果urlLoader不爲空,切scheme爲http或者https,則調用urlLoader.getResourceFetcher(new GlideUrl(model.toString()), width, height),這裏的urlLoader爲HttpUrlGlideUrlLoader,因此最終調用的是HttpUrlGlideUrlLoader的getResourceFetcher:
@Override
public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
// GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time spent parsing urls.
GlideUrl url = model;
if (modelCache != null) {
url = modelCache.get(model, 0, 0);
if (url == null) {
modelCache.put(model, 0, 0, model);
url = model;
}
}
return new HttpUrlFetcher(url);
}
前擾萬繞終於到HttpUrlFetcher了,之後獲取transcoder,transcoder是前面我們構造DrawableTypeRequest生成的,這裏resourceClass爲GifBitmapWrapper,transcodedClass爲GlideDrawable,獲取出來爲空,之後調用engine.load函數,參數爲之前構造的參數:
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());
。。。。
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);
}
這裏代碼太長,將其他代碼給註釋了,我們主要看會構建一個engineJob,之後構建一個decodeJob,構建一個EngineRunnable,之後調用addCallback設置回調ResourceCallback,最後調用start將Runnable submit,最終會回到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);
}
}
我們繼續跟蹤decode函數,獲取圖片資源,獲取成功後會通知ResourceCallback的onResourceReady,同時view顯示,我們先來看看decode函數,會先判斷是否從緩存中獲取,否則調用decodeFromSource(),最終調用DecodeJob的decodeFromSource():
public Resource<Z> decodeFromSource() throws Exception {
Resource<T> decoded = decodeSource();
return transformEncodeAndTranscode(decoded);
}
先decodeSource,在對resource進程變換,decodeSource如下:
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;
}
終於又看到了fetcher了,這裏fetcher就是前面生成的HttpUrlFetcher,因此調用的是HttpUrlFetcher的loadData,這裏主要是網絡操作了獲取一個InputStream流。之後對獲取的InputStream進行decodeFromSourceData:
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;
}
先判斷是否cache,則先緩存data,這裏我們設置的需要緩存,則會調用cacheAndDecodeSourceData:
private Resource<T> cacheAndDecodeSourceData(A data) throws IOException {
long startTime = LogTime.getLogTime();
SourceWriter<A> writer = new SourceWriter<A>(loadProvider.getSourceEncoder(), data);
diskCacheProvider.getDiskCache().put(resultKey.getOriginalKey(), writer);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Wrote source to cache", startTime);
}
startTime = LogTime.getLogTime();
Resource<T> result = loadFromCache(resultKey.getOriginalKey());
if (Log.isLoggable(TAG, Log.VERBOSE) && result != null) {
logWithTimeAndKey("Decoded source from cache", startTime);
}
return result;
}
先構造一個SourceWriter,參數是loadProvider.getSourceEncoder()與data,這裏第一個參數是我們之前構造DrawableTypeRequest生成的,也就是ImageVideoGifDrawableLoadProvider的getSourceEncoder,主要是解析是否是gif圖片,並寫入緩存,之後返回寫入的文件。
這些都執行完成後調用transformEncodeAndTranscode,這裏主要代用我們傳入的Transformation對資源進行轉換,比如裁剪成圓形,或者帶有圓角的等:
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;
}
這先調用transform對資源進程轉換,之後在調用transcode對資源進行轉換,這些都是之前我們設置的參數進行操作的。
整個過程完成後,會調用onLoadComplete,主要是通知callback告訴Resource已經準備好了,最終會回到GenericRequest,該GenericRequest就是我們之前創建的Request,最終會調用GenericRequest的onResourceReady:
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);
}
}
這裏最終會調用我們之前創建的Target,這裏的target就是前面build創建的GlideDrawableImageViewTarget,因此會調用GlideDrawableImageViewTarget的onResourceReady:
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) {
if (!resource.isAnimated()) {
//TODO: Try to generalize this to other sizes/shapes.
// This is a dirty hack that tries to make loading square thumbnails and then square full images less costly
// by forcing both the smaller thumb and the larger version to have exactly the same intrinsic dimensions.
// If a drawable is replaced in an ImageView by another drawable with different intrinsic dimensions,
// the ImageView requests a layout. Scrolling rapidly while replacing thumbs with larger images triggers
// lots of these calls and causes significant amounts of jank.
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();
}
這裏先判斷該圖片是否是動畫類型,也就是gif類型,這個是根據之前decode解碼出來的,那一部分沒有詳細解析,如果是這設置動畫的執行次數。
結束語
到此一次執行的大致流程就結束了,其中還有很多細小的枝節沒有詳細分析,不過可以根據流程看一遍。
諸如其他比如從本地加載,或者從assert加載流程都是一樣的。
目前Glide對本地resource是不能進行transform的,因此如果有需要可以對他進行改造,不過Glide官方也說了會從4.0開始支持。