簡介
由於在Android項目開發中我們經常會用到圖片加載,你會選擇什麼第三庫來加載圖片,今天讓我們來學習一下Glide圖片加載庫的源碼吧,之前文章有講解到Glide的簡單使用。
簡單使用
這裏就不說添加依賴那些了,大家可以看官方文檔,或者我之前的文章Glide的簡單使用,但是版本現在最新的版本是4.8.0
。
接下來我們看看Glide是如何加載圖片的,如下代碼
public class MainActivity extends AppCompatActivity {
ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = findViewById(R.id.imageView);
Glide.with(this).load("http://goo.gl/gEgYUd").into(imageView);
}
}
我們通過上述代碼中得到,Glide加載的使用方式是非常簡單的。好了根據這句代碼我們進行Glide的源碼分析吧。
Glide.with(this).load("http://goo.gl/gEgYUd").into(imageView);
通過上述代碼我們知道Glide中的with(this)調用的方法中攜帶了Activity或者Fragment的上下文對象其作用是:用於綁定該上下文對象的生命週期,如Activity或者Fragment的聲明週期。代碼如下:共有6個不同參數構造方法,返回靜態的RequestManager
,用於加載圖片。
//contex對象
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
/**
* Begin a load with Glide that will be tied to the given {@link android.app.Activity}'s lifecycle
* and that uses the given {@link Activity}'s default options.
*
* @param activity The activity to use.
* @return A RequestManager for the given activity that can be used to start a load. 用於啓動加載的給定activity上下文的RequestManager
*/
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
//FragmentActivity 上下文對象
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
// fragment上下文對象
public static RequestManager with(@NonNull Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
//V4的fragment對象
public static RequestManager with(@NonNull android.app.Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
// view對象
public static RequestManager with(@NonNull View view) {
return getRetriever(view.getContext()).get(view);
}
RequestManager
是通過RequestManagerRetriever
的get()
方法獲取,而get() 方法中的參數就是我們上述代碼傳遞過來的上下文對象。
然後getRetriever(Context context)
方法返回的對象是RequestManagerRetriever
,如下代碼所示。
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
// 這裏通過方法checkNotNull() 校驗攜帶的上下文是否爲null,如果是null將拋出空指針異常
Preconditions.checkNotNull(
context,
"You cannot start a load on a not yet attached View or a Fragment where getActivity() "
+ "returns null (which usually occurs when getActivity() is called before the Fragment "
+ "is attached or after the Fragment is destroyed).");
return Glide.get(context).getRequestManagerRetriever();
}
如果校驗上下文對象並無異常,則通過Glide.get(context).getRequestManagerRetriever();
返回 RequestManagerRetriever
對象。
//這裏使用的是雙重校驗單例方式初始化Glide實例
public static Glide get(@NonNull Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
//檢查並初始化Glide
checkAndInitializeGlide(context);
}
}
}
return glide;
}
在checkAndInitializeGlide(context)方法中的initializeGlide(context,builder)
方法進行初始化,實例化唯一的Glide實例(雙重校驗單例方式獲取)。
private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
Context applicationContext = context.getApplicationContext();//獲取長連接上下文對象
GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();//通過註解方式生成GeneratedAppGlideModule
List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
//加載Glide Module
manifestModules = new ManifestParser(applicationContext).parse();
}
if (annotationGeneratedModule != null
&& !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) {
Set<Class<?>> excludedModuleClasses =
annotationGeneratedModule.getExcludedModuleClasses();
Iterator<com.bumptech.glide.module.GlideModule> iterator = manifestModules.iterator();
//清除已經存在的Glide Module
while (iterator.hasNext()) {
com.bumptech.glide.module.GlideModule current = iterator.next();
if (!excludedModuleClasses.contains(current.getClass())) {
continue;
}
...
//清除
iterator.remove();
}
}
...
}
RequestManagerRetriever.RequestManagerFactory factory =
annotationGeneratedModule != null
? annotationGeneratedModule.getRequestManagerFactory() : null;
//設置Glide Builder 用於建造 Glide
builder.setRequestManagerFactory(factory);
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.applyOptions(applicationContext, builder);
}
if (annotationGeneratedModule != null) {
annotationGeneratedModule.applyOptions(applicationContext, builder);
}
//構建Glide 這步非常重要,接下來另一篇文章將重點分析這個模塊!!!
Glide glide = builder.build(applicationContext);
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
//註冊組件 此方法只調用一次
module.registerComponents(applicationContext, glide, glide.registry);
}
if (annotationGeneratedModule != null) {
annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
}
//上下文註冊回調
applicationContext.registerComponentCallbacks(glide);
//設置glide,初始化Glide完成
Glide.glide = glide;
}
有上面我們知道要獲取到RequestManager
是通過Glide.get(context).getRequestManagerRetriever();
調用get()
方法。我們來看看getRequestManagerRetriever()
方法。
//返回RequestManagerRetrieve對象
public RequestManagerRetriever getRequestManagerRetriever() {
return requestManagerRetriever;
}
現在我得到了RequestManagerRetrieve
以後通過RequestManagerRetrieve.get(context)
方法就可以獲取到我們的RequestManager
了。
這裏需要注意的是get()
方法下面有不同之處。FragmentActivity
或者是``
@NonNull
public RequestManager get(@NonNull 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()); //從新調用上邊get()方法
} else {
assertNotDestroyed(activity); //通過斷言判斷是否是已經銷燬的上下文對象
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(
activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
supportFragmentGet()
方法得到RequestManager
對象,當前Activity不能是Finished關閉狀態。
// 獲取RequestManager 對象
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,
boolean isParentVisible) {
//無界面的Fragment,即SupportRequestManagerFragment
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
// 獲取RequestManager
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
//獲取Glide 實例
Glide glide = Glide.get(context);
//通過RequestManagerFactory構建RequestManager。
//current.getGlideLifecycle()獲取生命週期,getRequestManagerTreeNode獲取RequestManagerTreeNode
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
到這裏我們已經得到了一個無界面的Fragment,即SupportRequestManagerFragment ,讓請求和你的activity的生命週期同步。
接下來我們進行到調用load()
方法中,看看發生什麼情況。我們知道調用load()
方法是RequestManager.class
,所以我們進入該類發現該類實現了LifecycleListener
和ModelTypes<T>
接口,而ModelTypes<T>
是我們所需的load()
方法的接口。
//表示回調是Activity生命週期的三個方法
public interface LifecycleListener {
void onStart();
void onStop();
void onDestroy();
}
而ModelTypes<T>
接口剛好是定義了load()
方法,如下
interface ModelTypes<T> {
//bitmap參數類型
T load(@Nullable Bitmap bitmap);
//drawable參數類型
T load(@Nullable Drawable drawable);
//string參數類型
T load(@Nullable String string);
//uri參數類型
T load(@Nullable Uri uri);
//file參數類型
T load(@Nullable File file);
//resourceId參數類型
T load(@RawRes @DrawableRes @Nullable Integer resourceId);
//url參數類型
T load(@Nullable URL url);
//byte[]參數類型
T load(@Nullable byte[] model);
//Object 參數類型
T load(@Nullable Object model);
}
當RequestManager
調用方法load()
代碼如下,返回的是RequestBuilder<Drawable>
實例,該實例可以進行一些常用的操作,如佔位符如:placeholder、error、fallback
等方式接下來我們會進行分析該佔位符。
//返回一個請求的構建器RequestBuilder<Drawable>
@Override
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
我們進入asDrawable()
方法看看返回RequestBuilder<Drawable>
//默認情況下,可以返回android.graphics.drawable.BitmapDrawable或GifDrawable,但如果爲其他Drawable子類註冊了其他解碼器,也可以返回任何子類。
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
//用於加載給定資源類的新請求構建器:RequestBuilder<ResourceType>
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
得到RequestBuilder<ResourceType>
以後調用load()
,而該類實現的接口是ModelTypes<RequestBuilder<TranscodeType>>
,這裏區別於RequestManager
實現的接口參數ModelTypes<RequestBuilder<Drawable>>
,說明在這裏已經進行了轉碼了,從Drawable
發生了轉碼。
@Override
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
@Override
public RequestBuilder<TranscodeType> load(@Nullable Bitmap bitmap) {
return loadGeneric(bitmap)
.apply(diskCacheStrategyOf(DiskCacheStrategy.NONE)); //diskCacheStrategyOf(DiskCacheStrategy.NONE)表示磁盤緩存策略
//apply方法在load(@RawRes @DrawableRes @Nullable Integer resourceId) 、load(@Nullable Drawable drawable)、load(@Nullable Bitmap bitmap)調用
}
//返回當前RequestBuilder<TranscodeType>對象
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
接下來我們進入into()
方法。
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread(); // 檢查是否爲後臺線程也就是UI線程,必須在主線程調用Into()方法
Preconditions.checkNotNull(view); //當前view是否爲空,是空就報空指針異常。
RequestOptions requestOptions = this.requestOptions;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
//在此方法中克隆,以便如果我們使用此RequestBuilder加載到View中,然後加載到不同的目標中,我們不會保留基於先前View的縮放類型應用的轉換。
//獲取view的ScaleType
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
//調用into(),返回ViewTarget<ImageView, TranscodeType> 對象
return into(
glideContext.buildImageViewTarget(view, transcodeClass),//構建ImageViewTarget,這個類擴展自BaseTarget.class
/*targetListener=*/ null,
requestOptions);
}
在buildImageViewTarget(view, transcodeClass)
通過imageViewTargetFactory.buildTarget()
工廠方法來返回ViewTarget<ImageView, X>
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
通過ImageViewTargetFactory
工廠方法得到ViewTarget
實例,這個過程爲後面設置圖片加載view.setImageBitmap(resource);
使用。
public class ImageViewTargetFactory {
public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,
@NonNull Class<Z> clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view); //創建了BitmapImageViewTarget轉換爲ViewTarget
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
} } }
然後進入上述的into()
方法中,該方法構建了Request
對象實例過程並進行加載圖片。如下代碼
//返回ViewTarget<ImageView, TranscodeType>
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
@NonNull RequestOptions options) {
Util.assertMainThread();
Preconditions.checkNotNull(target);
if (!isModelSet) { //是否調用load()方法標誌
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
options = options.autoClone(); //設置自動克隆,加鎖過程
//構造一個請求Request,通過構建請求遞歸方式返回Request,調用方法是buildRequestRecursive(), 構建縮略圖buildThumbnailRequestRecursive()
//這個方法最終會調用到`SingleRequest.obtain()`並且初始化SingleRequest,而我們想要的Request對象就是SingleRequest返回的
Request request = buildRequest(target, targetListener, options);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous) //判斷這個請求和之前的是否是一樣的
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {//如果之前已經保存在內存緩存了則跳過內存緩存
request.recycle(); //回收request
//如果請求完成,則再次開始將確保重新傳遞結果,觸發RequestListeners和Targets。 如果請求失敗,則再次開始將重新啓動請求,
//從而再次完成請求。 如果請求已在運行,我們可以讓它繼續運行而不會中斷。
if (!Preconditions.checkNotNull(previous).isRunning()) {
//使用先前的請求而不是新請求來允許優化,例如跳過設置佔位符,跟蹤和取消跟蹤目標,以及獲取在單個請求中完成的視圖維度。
previous.begin(); //啓動異步加載
}
return target;
}
requestManager.clear(target); //清除target對象
target.setRequest(request); //設置此目標(View)保留的當前請求,不應在Glide外部調用
requestManager.track(target, request); //追蹤請求操作
return target;
}
通過上面代碼註解我們知道Request
初始化過程,並返回了SingleRequest
對象。我們進入 requestManager.track(target, request)
追蹤方法,我要知道target是之前傳遞過來的View的對象轉換而成的,在TargetTracker.class
中實現了生命週期LifecycleListener
接口,通過弱引用集合WeakHashMap
保存了target
對象(記得這個對象是View轉換而成的),這裏確保了在線程同步狀態中執行。然後在requestTracker.runRequest(request);
方法中啓動了給定了請求request.begin();
,在SingleRequest<R>.class
中實現了該接口,如下。
@Override
public void begin() {
assertNotCallingCallbacks(); //是否允許回調,如果是true則拋出異常無法在RequestListener或Target回調中啓動或清除加載...
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
if (model == null) {
if (Util.isValidDimensions(overrideWidth, overrideHeight)) { //是否是有效尺寸
width = overrideWidth;
height = overrideHeight;
}
// 如果用戶設置了回退可繪製,則僅記錄更詳細的日誌級別,因爲回退Drawable表示用戶偶爾需要空模型。
int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
if (status == Status.COMPLETE) { //成功加載媒體
onResourceReady(resource, DataSource.MEMORY_CACHE); // DataSource.MEMORY_CACHE表示從內存緩存中獲取數據,該方法表示發佈資源,併發布完成並設置爲noll :engine.release(resource); Engine.class負責啓動負載並管理活動和緩存資源 這個方法重點
return;
}
status = Status.WAITING_FOR_SIZE; //等待給予目標的回調以確定目標尺寸
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight); //目標圖片大小讀取,這個方法中開始調用了請求方法,追蹤到Engine.load,設置一下緩存策略,並從磁盤,緩存中讀取
} else {
target.getSize(this); //獲取大小
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable()); //加載開始
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}}
從上述代碼中成功加載的狀態跟蹤方法到這裏,我們從開始的加載Resource
的對象是ImageView
通過轉換爲最終的target
到這裏我們將弄明白了這個加載情況,代碼如下。
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
....
isCallingCallbacks = true;
try {
boolean anyListenerHandledUpdatingTarget = false;
if (requestListeners != null) {
for (RequestListener<R> listener : requestListeners) {
anyListenerHandledUpdatingTarget |=
listener.onResourceReady(result, model, target, dataSource, isFirstResource);
}
}
anyListenerHandledUpdatingTarget |=
targetListener != null
&& targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
if (!anyListenerHandledUpdatingTarget) {
Transition<? super R> animation =
animationFactory.build(dataSource, isFirstResource);
target.onResourceReady(result, animation); //target最後調用加載資源文件讀取
}
} finally {
isCallingCallbacks = false;
}
notifyLoadSuccess(); //通知加載成功!
}
我們回再到into()
方法中,由於ImageViewTarget<Z>
擴展自抽象類ViewTarget<ImageView, Z>
而ViewTarget<ImageView, Z>
擴展自BaseTarget<Z>
該父類實現了Target<Z>
接口,所以ImageViewTarget<Z>
重寫了該方法,而DrawableImageViewTarget.class
擴展自ImageViewTarget<Z>
,這樣最後將會調用setResource()
方法
@Override
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
if (transition == null || !transition.transition(resource, this)) {
setResourceInternal(resource);
} else {
maybeUpdateAnimatable(resource);
}}
private void setResourceInternal(@Nullable Z resource) {
setResource(resource);
maybeUpdateAnimatable(resource);
}
public class DrawableImageViewTarget extends ImageViewTarget<Bitmap> {
@Override
protected void setResource(Bitmap resource) {
view.setImageBitmap(resource);
}
}
總結
接下來我們將細分模塊進行講解,本篇只是大致的講解Glide加載的過程,知道加載完成的步驟,還有某些重要的模塊這裏沒有講解到。如佔位符、緩存、變化等等,下篇見分曉。
推薦
- 我的博客
- 大家可以到我的博客https://eirunye.github.io進行瀏覽相關文章,大家一起相互探討技術。