簡言:
之前寫過關於Glide的文章,都是一些如何使用的案例,比較注重使用了,沒有考慮它的源碼是如何實現的,今天爲大家講解一下源碼,從源碼的角度讓你瞭解Glide這個神奇的圖片框架。
1.Gilde 簡介
在泰國舉行的谷歌發佈者論壇上,谷歌爲我們介紹了一個叫Gilde的圖片加載庫,作者是bumptech。
這個庫被廣泛的運用在goole的開源項目中,包括2014年goole I/O大會上發佈的官方app
2.使用gilde的優點:
1)使用簡單
2)可支配度高,自適應程度高
3)支持常見圖片格式 jpg png gif webp
4)支持多種數據源,網絡,本地資源,assets等
5)高效緩存策略,支持Memory 和Disk圖片緩存,默認bitmap格式採用RGB_565內存至少減少一半
6)生命週期集成,根據activity/Fragment生命週期自動管理請求
7)高效處理bitmap 使用bitmap pool使bitmap複用,主動調用recycle回收需要回收的bitmap,減少系統回收壓力。
3.Glide的基本概念
Model它代表圖片的數據源,例如URL,本地文件,然後通過ModelLoader從數據源中獲取原始數據Data,在對原始數據Data進行Decoder解碼,解碼之後的資源Resource,並通過Transform進行圖片的裁剪轉換,轉換之後的資源我們稱之爲TransformResource,轉換之後我們還需要對圖像進行Transcode轉碼操作,轉碼之後的資源我們就稱之爲TranscodedResource,最後將我們所顯示的資源封裝成Target,並進行顯示。這就是Glide圖片加載的整體流程,
4.通過源碼進行分析
1)我們針對Glide3.7這個版本進行源碼分析簡介,這個版本相對於還是比較穩定的。
compile 'com.github.bumptech.glide:glide:3.7.0'
2)簡單實用的代碼:
Glide
.with(getApplicationContext())
.load(url)
.into(imageView);
我們幾乎都會這麼簡單的使用了,這裏沒什麼好講的了,接下來我們看一下他複雜的使用,我們並對每一個屬性進行講解,我們看代碼:
Glide.with(getApplicationContext()) // 指定Context
.load(url)// 指定圖片的URL
.placeholder(R.mipmap.ic_launcher)// 指定圖片未成功加載前顯示的圖片
.error(R.mipmap.ic_launcher)// 指定圖片加載失敗顯示的圖片
.override(300, 300)//指定圖片的尺寸
.fitCenter()//指定圖片縮放類型爲
.centerCrop()// 指定圖片縮放類型爲
.skipMemoryCache(true)// 跳過內存緩存
.diskCacheStrategy(DiskCacheStrategy.NONE)//跳過磁盤緩存
.diskCacheStrategy(DiskCacheStrategy.SOURCE)//僅僅只緩存原來的全分辨率的圖像
.diskCacheStrategy(DiskCacheStrategy.RESULT)//僅僅緩存最終的圖像
.diskCacheStrategy(DiskCacheStrategy.ALL)//緩存所有版本的圖像
.priority(Priority.HIGH)//指定優先級.Glide將會用他們作爲一個準則,並儘可能的處理這些請求,但是它不能保證所有的圖片都會按照所要求的順序加載。優先級排序:IMMEDIATE > HIGH > NORMAL > LOW
.into(imageView);//指定顯示圖片的Imageview
這裏說個題外話,注意到我們with方法傳遞的上下文了嗎?如果傳入的是當前activity的上下文,或者傳遞的是getApplicationContext,他們分別代表什麼?
當傳入的是activity被銷燬時,這個圖片加載的生命週期也會被銷燬,反之,如果山下文傳遞的是getApplicationContext時,只有程序被銷燬時,圖片加載的生命週期纔會被銷燬。
其它屬性的配置在代碼中已經做了標註,就不做過多的解釋了,我們下邊會對每一個參數進行源碼分析,我們針對幾個需要注意的點進行講解一下:
1)placeholder這個方法:不要加載網絡圖片,加載本地圖片,本身該方法主要解決網絡慢時,指定的佔位圖,如果在加載網絡圖片,該方法就沒有意義了,這裏注意一下。
2)override這個方法主要解決的就是內存浪費,防止內存溢出泄露等。
3)fitCenter,centerCrop,這兩個方法主要防止圖片失真,顯示模糊,做的處理。
3.源碼分析
1)首先我們看with方法的源碼:
public static RequestManager with(Context context) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(context);
}
public static RequestManager with(Activity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
public static RequestManager with(FragmentActivity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static RequestManager with(android.app.Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
爲什麼with這麼多重載方法,其實這個設計非常好,因爲傳入不同的參數,所對應的圖片加載的生命週期就會不同,所以才設置這麼多的重載方法,
我們繼續看retriever.get()方法:
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);
}
(Util.isOnMainThread() && !(context instanceof Application),首先判斷是否是主線程,然後判斷傳遞過來的上下文,如果是Application的,它會走return getApplicationManager(context),我們進去看一下這個方法的源碼:
private RequestManager getApplicationManager(Context context) {
// Either an application context or we're on a background thread.
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
// Normally pause/resume is taken care of by the fragment we add to the fragment or activity.
// However, in this case since the manager attached to the application will not receive lifecycle
// events, we must force the manager to start resumed using ApplicationLifecycle.
applicationManager = new RequestManager(context.getApplicationContext(),
new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
}
}
}
return applicationManager;
}
這麼不難看出,這是一個單例模式,爲了保證RequestManager這個唯一的實例,
所以這個with方法,的核心源碼主要就是獲取一個RequestManager這個實例,來負責管理我們的圖片請求。
2)load()方法
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}
這個load方法也有很多重載方法,
DrawableTypeRequest<String>這個方法就是代表我們所有圖片的請求。我們看看源碼:
public class DrawableTypeRequest<ModelType> extends DrawableRequestBuilder<ModelType> implements DownloadOptions {
private final ModelLoader<ModelType, InputStream> streamModelLoader;
private final ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader;
private final RequestManager.OptionsApplier optionsApplier;
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);
}
它是繼承DrawableRequestBuilder<ModelType>這個類:
public class DrawableRequestBuilder<ModelType>
extends GenericRequestBuilder<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable>
implements BitmapOptions, DrawableOptions {
DrawableRequestBuilder(Context context, Class<ModelType> modelClass,
LoadProvider<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable> loadProvider, Glide glide,
RequestTracker requestTracker, Lifecycle lifecycle) {
super(context, modelClass, loadProvider, GlideDrawable.class, glide, requestTracker, lifecycle);
// Default to animating.
crossFade();
}
這個類繼承GenericRequestBuilder<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable>
主要就是一些參數的配置,例如縮略圖,佔位符,error圖等參數的設置。
我們返回DrawableTypeRequest方法,看fromString這個方法的源碼都做了些 什麼?
public DrawableTypeRequest<String> fromString() {
return loadGeneric(String.class);
}
我們看一下loadGeneric這個方法,主要實現都在loadGeneric這個方法裏面:
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));
}
這個方法裏面主要創建兩個ModelLoader,把我們的數據來源加載成原始數據,
3)into()方法(重要)
我們看一下into方法的實現:
@Override
public Target<GlideDrawable> into(ImageView view) {
return super.into(view);
}
我們看一下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是否爲null,這裏就不多說了,
我們看一下applyCenterCrop(),這個方法:
void applyCenterCrop() {
// To be implemented by subclasses when possible.
}
它是一個空實現,我們看一下他的子類:
@Override
void applyCenterCrop() {
centerCrop();
}
我們接着看centerCrop這個方法:
@SuppressWarnings("unchecked")
public DrawableRequestBuilder<ModelType> centerCrop() {
return transform(glide.getDrawableCenterCrop());
}
我們看一下transform怎麼操作的:
/**
* Transform resources with the given {@link Transformation}s. Replaces any existing transformation or
* transformations.
*
* @param transformations the transformations to apply in order.
* @return This request builder.
*/
public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> transform(
Transformation<ResourceType>... transformations) {
isTransformationSet = true;
if (transformations.length == 1) {
transformation = transformations[0];
} else {
transformation = new MultiTransformation<ResourceType>(transformations);
}
return this;
}
這裏做了一些初始化的操作,對transform做了一些賦值。
我們回到into的方法,看一下return into(glide.buildImageViewTarget(view, transcodeClass));這個方法:
這裏是構建一個ImageViewTarget對象,我們看一下是如何構建的:
<R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
}
這個方法其實就是通過ImageViewTargetFactrory工廠來構建Target,我們看一下工廠主要做了什麼?
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)");
}
}
}
這個是不是主要做了Target加載哪一種圖片。
所以我們知道,這個BuildIamgeViewTarget主要做的就是讓我知道返回哪一種Target對象,
我們接着看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;
}
這裏主要做的就是,創建一個加載圖片的Request,並對之前綁定的request進行刪除,並將新建的request綁定target,然後通過requestTracker這個request管理跟蹤器,做相應的request的處理。
到這裏簡單實用的Glide框架的源碼分析基本完事了,如果繼續深入關於內存緩存,或者磁盤緩存的源碼分析,請私信我,我將相關資源分享給您,into這個方法是Glide中重要的方法,如果深入探索還有很多東西,