Glide原始碼解析(一)
簡介
由於在Android專案開發中我們經常會用到圖片載入,你會選擇什麼第三庫來載入圖片,今天讓我們來學習一下Glide圖片載入庫的原始碼吧,之前文章有講解到Glide的簡單使用。
簡單使用
這裡就不說新增依賴那些了,大家可以看ofollow,noindex">官方文件
,或者我之前的文章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 進行瀏覽相關文章,大家一起相互探討技術。