Android源碼進(jìn)階之Glide加載流程和源碼詳解
本文轉(zhuǎn)載自微信公眾號(hào)「Android開(kāi)發(fā)編程」,作者Android開(kāi)發(fā)編程。轉(zhuǎn)載本文請(qǐng)聯(lián)系A(chǔ)ndroid開(kāi)發(fā)編程公眾號(hào)。
前言
Glide是純Java寫的Android端開(kāi)源圖片加載庫(kù),能夠幫助我們下載、緩存、展示多種格式圖片,也包括GIF格式;
昨天我們從源碼里分析了,glide的緩存策略機(jī)制;
那今天我們就趁熱打鐵來(lái)分析一波加載流程;
一、glide常用的加載方法
1、加載圖片到imageView
- Glide.with(Context context).load(Strint url).into(ImageView imageView);
2、各種形式的圖片加載到ImageView
- // 加載本地圖片
- File file = new File(getExternalCacheDir() + "/image.jpg");
- Glide.with(this).load(file).into(imageView);
- // 加載應(yīng)用資源
- int resource = R.drawable.image;
- Glide.with(this).load(resource).into(imageView);
- // 加載二進(jìn)制流
- byte[] image = getImageBytes();
- Glide.with(this).load(image).into(imageView);
- // 加載Uri對(duì)象
- Uri imageUri = getImageUri();
- Glide.with(this).load(imageUri).into(imageView);
3、加載帶有占位圖
- Glide.with(this).load(url).placeholder(R.drawable.loading).into(imageView);
占位圖目的為在目的圖片還未加載出來(lái)的時(shí)候,提前展示給用戶的一張圖片;
4、加載失敗 放置占位符
- Glide.with(this).load(url).placeholder(R.drawable.loading).error(R.drawable.error)
- .diskCacheStrategy(DiskCacheStrategy.NONE)//關(guān)閉Glide的硬盤緩存機(jī)制
- .into(imageView);
- //DiskCacheStrategy.NONE:表示不緩存任何內(nèi)容。
- //DiskCacheStrategy.SOURCE:表示只緩存原始圖片。
- //DiskCacheStrategy.RESULT:表示只緩存轉(zhuǎn)換過(guò)后的圖片(默認(rèn)選項(xiàng))。
- //DiskCacheStrategy.ALL :表示既緩存原始圖片,也緩存轉(zhuǎn)換過(guò)后的圖片。
5、加載指定格式的圖片--指定為靜止圖片
- Glide.with(this)
- .load(url)
- .asBitmap()//只加載靜態(tài)圖片,如果是git圖片則只加載第一幀。
- .placeholder(R.drawable.loading)
- .error(R.drawable.error)
- .diskCacheStrategy(DiskCacheStrategy.NONE)
- .into(imageView);
6、加載動(dòng)態(tài)圖片
- Glide.with(this)
- .load(url)
- .asGif()//加載動(dòng)態(tài)圖片,若現(xiàn)有圖片為非gif圖片,則直接加載錯(cuò)誤占位圖。
- .placeholder(R.drawable.loading)
- .error(R.drawable.error)
- .diskCacheStrategy(DiskCacheStrategy.NONE)
- .into(imageView);
7、加載指定大小的圖片
- Glide.with(this)
- .load(url)
- .placeholder(R.drawable.loading)
- .error(R.drawable.error)
- .diskCacheStrategy(DiskCacheStrategy.NONE)
- .override(100, 100)//指定圖片大小
- .into(imageView);
8、關(guān)閉框架的內(nèi)存緩存機(jī)制
- Glide.with(this)
- .load(url)
- .skipMemoryCache(true) //傳入?yún)?shù)為false時(shí),則關(guān)閉內(nèi)存緩存。
- .into(imageView);
9、關(guān)閉硬盤的緩存
- Glide.with(this)
- .load(url)
- .diskCacheStrategy(DiskCacheStrategy.NONE) //關(guān)閉硬盤緩存操作
- .into(imageView);
- //其他參數(shù)表示:
- //DiskCacheStrategy.NONE:表示不緩存任何內(nèi)容。
- //DiskCacheStrategy.SOURCE:表示只緩存原始圖片。
- //DiskCacheStrategy.RESULT:表示只緩存轉(zhuǎn)換過(guò)后的圖片(默認(rèn)選項(xiàng))。
- //DiskCacheStrategy.ALL :表示既緩存原始圖片,也緩存轉(zhuǎn)換過(guò)后的圖片。
10、當(dāng)引用的 url 存在 token 時(shí)解決方法
- public class MyGlideUrl extends GlideUrl {
- private String mUrl;
- public MyGlideUrl(String url) {
- super(url);
- mUrl = url;
- }
- @Override
- public String getCacheKey() {
- return mUrl.replace(findTokenParam(), "");
- }
- private String findTokenParam() {
- String tokenParam = "";
- int tokenKeyIndex = mUrl.indexOf("?token=") >= 0 ? mUrl.indexOf("?token=") : mUrl.indexOf("&token=");
- if (tokenKeyIndex != -1) {
- int nextAndIndex = mUrl.indexOf("&", tokenKeyIndex + 1);
- if (nextAndIndex != -1) {
- tokenParam = mUrl.substring(tokenKeyIndex + 1, nextAndIndex + 1);
- } else {
- tokenParam = mUrl.substring(tokenKeyIndex);
- }
- }
- return tokenParam;
- }
- }
然后加載圖片的方式為:
- Glide.with(this)
- .load(new MyGlideUrl(url))
- .into(imageView);
11、利用Glide將圖片加載到不同控件或加載成不同使用方式
(1)、拿到圖片實(shí)例
- //1、通過(guò)自己構(gòu)造 target 可以獲取到圖片實(shí)例
- SimpleTarget<GlideDrawable> simpleTarget = new SimpleTarget<GlideDrawable>() {
- @Override
- public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) {
- imageView.setImageDrawable(resource);
- }
- };
- //2、將圖片實(shí)例記載到指定的imageview上,也可以做其他的事情
- public void loadImage(View view) {
- String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";
- Glide.with(this)
- .load(url)
- .into(simpleTarget);
- }
(2)、將圖片加載到任何位置
- /*
- *將圖片加載為控件背景
- */
- public class MyLayout extends LinearLayout {
- private ViewTarget<MyLayout, GlideDrawable> viewTarget;
- public MyLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- viewTarget = new ViewTarget<MyLayout, GlideDrawable>(this) {
- @Override
- public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) {
- MyLayout myLayout = getView();
- myLayout.setImageAsBackground(resource);
- }
- };
- }
- public ViewTarget<MyLayout, GlideDrawable> getTarget() {
- return viewTarget;
- }
- public void setImageAsBackground(GlideDrawable resource) {
- setBackground(resource);
- }
- }
- //引用圖片到指定控件作為背景
- public class MainActivity extends AppCompatActivity {
- MyLayout myLayout;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- myLayout = (MyLayout) findViewById(R.id.background);
- }
- public void loadImage(View view) {
- String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";
- Glide.with(this)
- .load(url)
- .into(myLayout.getTarget());
- }
- }
12、Glide 實(shí)現(xiàn)預(yù)加載
- //a、預(yù)加載代碼
- Glide.with(this)
- .load(url)
- .diskCacheStrategy(DiskCacheStrategy.SOURCE)
- .preload();
- //preload() 有兩種重載
- // 1、帶有參數(shù)的重載,參數(shù)作用是設(shè)置預(yù)加載的圖片大?。?nbsp;
- //2、不帶參數(shù)的表示加載的圖片為原始尺寸;
- //b、使用預(yù)加載的圖片
- Glide.with(this)
- .load(url)
- .diskCacheStrategy(DiskCacheStrategy.SOURCE)
- .into(imageView);
二、Glide加載流程詳解
1、with(context)
- // Glide.java
- public static RequestManager with(@NonNull Context context) {
- return getRetriever(context).get(context);
- }
- public static RequestManager with(@NonNull Activity activity) {
- return getRetriever(activity).get(activity);
- }
- public static RequestManager with(@NonNull FragmentActivity activity) {
- return getRetriever(activity).get(activity);
- }
- public static RequestManager with(@NonNull Fragment fragment) {
- return getRetriever(fragment.getContext()).get(fragment);
- }
- public static RequestManager with(@NonNull View view) {
- return getRetriever(view.getContext()).get(view);
- }
該函數(shù)創(chuàng)建了Glide實(shí)例并初始化了一些基本參數(shù),然后創(chuàng)建了一個(gè)RequestManager對(duì)象并返回??偣灿?個(gè)場(chǎng)景,這里就先選取參數(shù)為Context類型情形進(jìn)行分析。
- // Glide.java
- public static RequestManager with(@NonNull Context context) {
- return getRetriever(context).get(context);
- }
可以看到該函數(shù)首先調(diào)用了getRetriever(context)獲取到了RequestManagerRetriever對(duì)象。在創(chuàng)建該對(duì)象之前首先通過(guò)Glide.java中的get方法獲得了Glide實(shí)例(Glide是一個(gè)單例),同時(shí)讀取AppGlideModule和AndroidManifest.xml的配置。
- // Glide.java
- private static RequestManagerRetriever getRetriever(@Nullable Context context) {
- // Glide.get(context)獲取Glide實(shí)例
- return Glide.get(context).getRequestManagerRetriever();
- }
- public static Glide get(@NonNull Context context) {
- if (glide == null) {
- // 加載AppGlideModule
- GeneratedAppGlideModule annotationGeneratedModule =
- getAnnotationGeneratedGlideModules(context.getApplicationContext());
- synchronized (Glide.class) {
- if (glide == null) {
- // 加載Mainfest配置、注冊(cè)模塊回調(diào)
- // 這一步執(zhí)行了 Glide.build()方法構(gòu)造Glide實(shí)例。build方法下面會(huì)講到
- checkAndInitializeGlide(context, annotationGeneratedModule);
- }
- }
- }
- return glide;
- }
獲取到Glide實(shí)例后,緊接著調(diào)用getRequestManagerRetriever方法返回了上一步已經(jīng)初始化好的RequestManagerRetriever對(duì)象。
- // Glide.java
- public RequestManagerRetriever getRequestManagerRetriever() {
- return requestManagerRetriever;
- }
接著再看一看RequestManagerRetriever是如何被初始化的,以及初始化過(guò)程中都干了哪些事。首先貼源碼看看Glide.build方法內(nèi)部具體實(shí)現(xiàn)(該方法在上述checkAndInitializeGlide()函數(shù)中被調(diào)用):
- // GlideBuilder.java
- Glide build(@NonNull Context context) {
- // 分配線程池、配置緩存策略
- sourceExecutor = GlideExecutor.newSourceExecutor();
- diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
- animationExecutor = GlideExecutor.newAnimationExecutor();
- memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
- // 監(jiān)聽(tīng)網(wǎng)絡(luò)變化
- connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
- int size = memorySizeCalculator.getBitmapPoolSize();
- if (size > 0) {
- bitmapPool = new LruBitmapPool(size);
- } else {
- bitmapPool = new BitmapPoolAdapter();
- }
- arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
- memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
- diskCacheFactory = new InternalCacheDiskCacheFactory(context);
- // engine是負(fù)責(zé)執(zhí)行加載任務(wù)的
- if (engine == null) {
- engine =
- new Engine(
- memoryCache,
- diskCacheFactory,
- diskCacheExecutor,
- sourceExecutor,
- GlideExecutor.newUnlimitedSourceExecutor(),
- animationExecutor,
- isActiveResourceRetentionAllowed);
- }
- if (defaultRequestListeners == null) {
- defaultRequestListeners = Collections.emptyList();
- } else {
- defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners);
- }
- RequestManagerRetriever requestManagerRetriever =
- new RequestManagerRetriever(requestManagerFactory);
- return new Glide(
- context,
- engine,
- memoryCache,
- bitmapPool,
- arrayPool,
- requestManagerRetriever,
- connectivityMonitorFactory,
- logLevel,
- defaultRequestOptionsFactory,
- defaultTransitionOptions,
- defaultRequestListeners,
- isLoggingRequestOriginsEnabled,
- isImageDecoderEnabledForBitmaps);
- }
- 執(zhí)行Glide.get()方法時(shí)就已經(jīng)分配好了資源加載、緩存線程池、配置好了緩存策略,這里的engine專門負(fù)責(zé)加載、解碼資源,ConnectivityMonitor注冊(cè)了網(wǎng)絡(luò)狀態(tài)監(jiān)聽(tīng)器,當(dāng)網(wǎng)絡(luò)斷開(kāi)時(shí)暫停請(qǐng)求網(wǎng)絡(luò)資源,重連后繼續(xù)請(qǐng)求資源;
- RequestManagerRetriever是原來(lái)是通過(guò)RequestManagerFactory工廠類構(gòu)造的。進(jìn)入到RequestManagerFactory.java類中,可以看到get方法獲取到了相應(yīng)的RequestManager對(duì)象;
- 從這里我們可以發(fā)現(xiàn),無(wú)論哪種情況,當(dāng)App進(jìn)入后臺(tái)后會(huì)導(dǎo)致頁(yè)面不可見(jiàn),此時(shí)RequestManager綁定到了ApplicationContext,與App的生命周期一致,因此在RequestManager.java類中也實(shí)現(xiàn)了生命周期相關(guān)的回調(diào)函數(shù);
- // RequestManagerRetriever.java
- // get有好幾個(gè)重載方法,這里僅選取context參數(shù)進(jìn)行分析
- public RequestManager get(@NonNull Context context) {
- 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
- && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
- return get(((ContextWrapper) context).getBaseContext());
- }
- }
- return getApplicationManager(context);
- }
執(zhí)行完Glide.with(context)后我們拿到了一個(gè)對(duì)應(yīng)的RequestManager對(duì)象,接下來(lái)就執(zhí)行下一個(gè)任務(wù)load(url);
2、load(url)
拿到了RequestManager,緊接著調(diào)用load方法開(kāi)始執(zhí)行下一步操作,同樣先看看load方法的實(shí)現(xiàn)
- // RequestManager.java
- public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
- return asDrawable().load(bitmap);
- }
- public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {
- return asDrawable().load(drawable);
- }
- public RequestBuilder<Drawable> load(@Nullable String string) {
- return asDrawable().load(string);
- }
- public RequestBuilder<Drawable> load(@Nullable Uri uri) {
- return asDrawable().load(uri);
- }
- public RequestBuilder<Drawable> load(@Nullable File file) {
- return asDrawable().load(file);
- }
- public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
- return asDrawable().load(resourceId);
- }
- public RequestBuilder<Drawable> load(@Nullable URL url) {
- return asDrawable().load(url);
- }
- public RequestBuilder<Drawable> load(@Nullable byte[] model) {
- return asDrawable().load(model);
- }
- public RequestBuilder<Drawable> load(@Nullable Object model) {
- return asDrawable().load(model);
- }
- load()同樣有多個(gè)重載函數(shù),傳入的參數(shù)可以是圖片對(duì)象Bitmap、Drawable、本地資源Uri、在線資源路徑Url、文件對(duì)象File、assets資源的id,這里我們只看參數(shù)為Url的情形;
- asDrawable().load(url)返回了一個(gè)RequestBuilder對(duì)象,首先看看asDrawable方法干了什么;
- // RequestManager.java
- public RequestBuilder<Drawable> asDrawable() {
- return as(Drawable.class);
- }
- public <ResourceType> RequestBuilder<ResourceType> as(@NonNull Class<ResourceType> resourceClass) {
- return new RequestBuilder<>(glide, this, resourceClass, context);
- }
asDrawable方法創(chuàng)建了RequestBuilder對(duì)象,然后調(diào)用RequestBuilder.java中的load方法;
- // RequestBuilder.java
- // 傳入的String類型的url將會(huì)被作為緩存的key
- public RequestBuilder<TranscodeType> load(@Nullable String string) {
- return loadGeneric(string);
- }
- // 這里返回了自身
- private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
- this.model = model;
- isModelSet = true;
- return this;
- }
load函數(shù)主要工作就是根據(jù)傳入的資源類型,構(gòu)造了一個(gè)相應(yīng)的RequestBuilder對(duì)象。至此一切準(zhǔn)備工作準(zhǔn)備就緒,接下來(lái)就是最為重要的一步了-加載、展示文件,讓我們來(lái)著看into(view)方法如何完成這些任務(wù);
3、into(view)
拿到的是對(duì)應(yīng)類型RequestBuilder實(shí)例,那么就看看該類里into方法的具體實(shí)現(xiàn)。同樣into方法有into(@NonNull Y target)和into(@NonNull ImageView )兩個(gè)重載函數(shù)(這兩個(gè)函數(shù)最終都會(huì)走到同一個(gè)函數(shù)中),由于調(diào)用into方法時(shí)我們傳入的參數(shù)是ImageView類型的;
- // RequestBuilder.java
- public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
- Util.assertMainThread();
- BaseRequestOptions<?> requestOptions = this;
- // View's scale type.
- // 處理圖片縮放,根據(jù)縮放類型來(lái)初始化對(duì)應(yīng)的requestOptions對(duì)象
- ......
- return into(
- glideContext.buildImageViewTarget(view, transcodeClass),
- /*targetListener=*/ null,
- requestOptions,
- Executors.mainThreadExecutor() // 運(yùn)行在主線程的handler
- );
- }
- 上面代碼段首先處理圖片縮放類型(裁剪、對(duì)齊方式等),并將生成的相關(guān)參數(shù)放入了requestOptions對(duì)象中,然后再將其作為參數(shù)傳給了RequestBuilder.java類私有方法into。該方法定義的四個(gè)參數(shù)分別為:viewTarget、target回調(diào)監(jiān)聽(tīng)器、請(qǐng)求參數(shù)、主線程的回調(diào)函數(shù);
- 顯然外部傳入ImageView對(duì)象最終被轉(zhuǎn)換成了ViewTarget對(duì)象,轉(zhuǎn)換函數(shù)便是glideContext.buildImageViewTarget(view, transcodeClass);
- // GlideContext.java
- public <X> ViewTarget<ImageView, X> buildImageViewTarget(@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
- return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
- }
ViewTarget又是由ImageViewTargetFactory工廠方法生成,接著再看buildTarget方法是如何生成ViewTarget對(duì)象。
- // imageViewTargetFactory.java
- 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);
- } 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)");
- }
- }
- }
可以看到無(wú)論傳入?yún)?shù)是何種類型,最終都會(huì)轉(zhuǎn)換成兩種類型的ViewTarget :BitmapImageViewTarget;DrawableImageViewTarget;這里如何選擇取決于asBitmap()、asGif()和asDrawable()函數(shù)是否被調(diào)用,默認(rèn)是Bitmap類型,所以這里默認(rèn)返回的是BitmapImageViewTarget;
- // BitmapImageViewTarget.java
- public class BitmapImageViewTarget extends ImageViewTarget<Bitmap> {
- public BitmapImageViewTarget(ImageView view) {
- super(view);
- }
- @Override
- protected void setResource(Bitmap resource) {
- view.setImageBitmap(resource); // 顯示圖片
- }
- }
至此ViewTarget創(chuàng)建完畢,我們?cè)倩氐絉equestBuilder.java私有into方法
- // RequestBuilder.java`
- private <Y extends Target<TranscodeType>> Y into(
- @NonNull Y target,
- @Nullable RequestListener<TranscodeType> targetListener,
- BaseRequestOptions<?> options,
- Executor callbackExecutor) {
- // 注釋1:創(chuàng)建request
- Request request = buildRequest(target, targetListener, options, callbackExecutor);
- // 獲取前一個(gè)reqeust請(qǐng)求對(duì)象
- Request previous = target.getRequest();
- // 與上一個(gè)請(qǐng)求相同 并且 上一個(gè)請(qǐng)求已完成
- if (request.isEquivalentTo(previous)&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
- // 上一個(gè)請(qǐng)求已完成,那么重新啟動(dòng)它
- if (!Preconditions.checkNotNull(previous).isRunning()) {
- previous.begin();
- }
- return target;
- }
- // 與上一個(gè)請(qǐng)求不同,則清除掉上一個(gè),再將加入新請(qǐng)求
- requestManager.clear(target);
- target.setRequest(request);
- requestManager.track(target, request);
- return target;
- }
順著代碼次序,來(lái)看看這個(gè)方法每一步都干了什么:
- 首先執(zhí)行buildRequest方法創(chuàng)建一個(gè)新的Request請(qǐng)求req1;
- 獲取當(dāng)前ViewTarget上正在進(jìn)行中的Request請(qǐng)求req2;
- 判斷新建的請(qǐng)求req1與已有的請(qǐng)求req2是否相同,如果相同則判斷是否跳過(guò)req2請(qǐng)求的緩存,兩個(gè)條件都滿足則開(kāi)始執(zhí)行begin()方法開(kāi)始請(qǐng)求資源并停止往下執(zhí)行,條件都不滿足則繼續(xù)執(zhí)行第四步;
- 給ViewTarget設(shè)置最新的請(qǐng)求req1,然后執(zhí)行track方法追蹤req1。
- 執(zhí)行into(view)方法首先獲取到了Request請(qǐng)求,然后開(kāi)始執(zhí)行Request。如果是復(fù)用的Request則直接執(zhí)行begin(),否則執(zhí)行track(target, request),但最終仍然會(huì)執(zhí)行begin();
- // ReqeustManager.java
- synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
- // 與lifecycle綁定
- targetTracker.track(target);
- // 啟動(dòng)reqeust
- requestTracker.runRequest(request);
- }
- // RequestTracker.java
- public void runRequest(@NonNull Request request) {
- requests.add(request);
- if (!isPaused) {
- request.begin(); // 立即開(kāi)始加載
- } else {
- //防止從以前的請(qǐng)求中加載任何位圖,釋放該請(qǐng)求所擁有的任何資源,顯示當(dāng)前占位符(如果提供了該占位符),并將該請(qǐng)求標(biāo)記為已取消。
- // request.java( Interface )
- request.clear();
- pendingRequests.add(request); // 加入隊(duì)列等待執(zhí)行
- }
- }
- track方法的源碼,先是執(zhí)行targetTracker.track(target)監(jiān)聽(tīng)ViewTarget的請(qǐng)求,然后runRequest開(kāi)始執(zhí)行。由于最終都是通過(guò)begin()方法開(kāi)始請(qǐng)求,所以我們先來(lái)看看begin()方法的具體實(shí)現(xiàn);
- Request類是interface類型,begin()它的抽象方法,所以我們要想弄清楚begin()的具體實(shí)現(xiàn),那就要先找到Request的實(shí)現(xiàn)類,從buildRequest(xx)方法入手,同樣先貼出源碼:
- // RequestBuilder.java
- private Request buildRequest(
- Target<TranscodeType> target,
- @Nullable RequestListener<TranscodeType> targetListener,
- BaseRequestOptions<?> requestOptions,
- Executor callbackExecutor) {
- return buildRequestRecursive(
- /*requestLock=*/ new Object(),
- target,
- targetListener,
- /*parentCoordinator=*/ null,
- transitionOptions,
- requestOptions.getPriority(),
- requestOptions.getOverrideWidth(),
- requestOptions.getOverrideHeight(),
- requestOptions,
- callbackExecutor);
- }
- private Request buildRequestRecursive(
- Object requestLock,
- Target<TranscodeType> target,
- @Nullable RequestListener<TranscodeType> targetListener,
- @Nullable RequestCoordinator parentCoordinator,
- TransitionOptions<?, ? super TranscodeType> transitionOptions,
- Priority priority,
- int overrideWidth,
- int overrideHeight,
- BaseRequestOptions<?> requestOptions,
- Executor callbackExecutor) {
- // Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
- ErrorRequestCoordinator errorRequestCoordinator = null;
- // 請(qǐng)求出錯(cuò)了
- if (errorBuilder != null) {
- errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, parentCoordinator);
- parentCoordinator = errorRequestCoordinator;
- }
- // 無(wú)法確認(rèn)完成請(qǐng)求和縮略圖請(qǐng)求哪個(gè)先完成,所以當(dāng)縮略圖比完成請(qǐng)求后完成時(shí)就不再顯示縮略圖
- Request mainRequest =
- buildThumbnailRequestRecursive(
- requestLock,
- target,
- targetListener,
- parentCoordinator,
- transitionOptions,
- priority,
- overrideWidth,
- overrideHeight,
- requestOptions,
- callbackExecutor);
- // 請(qǐng)求成功了,直接返回縮略圖Request
- if (errorRequestCoordinator == null) {
- return mainRequest;
- }
- // ...
- Request errorRequest =
- errorBuilder.buildRequestRecursive(
- requestLock,
- target,
- targetListener,
- errorRequestCoordinator,
- errorBuilder.transitionOptions,
- errorBuilder.getPriority(),
- errorOverrideWidth,
- errorOverrideHeight,
- errorBuilder,
- callbackExecutor);
- // 同時(shí)返回縮略圖請(qǐng)求和錯(cuò)誤請(qǐng)求
- errorRequestCoordinator.setRequests(mainRequest, errorRequest);
- return errorRequestCoordinator;
- }
顯然代碼里的mainRequest就是我們要找的Request了,它是由buildThumbnailRequestRecursive方法返回的,深入其內(nèi)部我們發(fā)現(xiàn)Request最終其實(shí)是由SingleRequest.obtain方法產(chǎn)生,也就是說(shuō)我們最終拿到的Request其實(shí)就是SingleReqeust類的一個(gè)實(shí)例。這里過(guò)程比較簡(jiǎn)單,代碼就不貼出來(lái)了。我們直接去SingleReqeust類里面 看看begin方法如何實(shí)現(xiàn)的;
- // SingleReqeust.java
- public void begin() {
- if (status == Status.COMPLETE) {
- // 資源已下載,直接回調(diào)
- // 執(zhí)行動(dòng)畫
- onResourceReady(resource, DataSource.MEMORY_CACHE);
- return;
- }
- // 計(jì)算尺寸
- if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
- onSizeReady(overrideWidth, overrideHeight);
- } else {
- target.getSize(this);
- }
- if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
- && canNotifyStatusChanged()) {
- // 開(kāi)始加載
- // 設(shè)置占位度
- target.onLoadStarted(getPlaceholderDrawable());
- }
- }
進(jìn)入begin方法后首先判斷如果資源已經(jīng)過(guò)加載好了則直接回調(diào)onResourceReady顯示圖片并緩存,否則測(cè)量出圖片尺寸后再開(kāi)始加載圖片(onSizeReady()中執(zhí)行加載任務(wù))并同時(shí)顯示占位圖:
①overrideWith、overrideHeight通過(guò)override(width, height)設(shè)置:
Glide.with(mContext).load(url).override(75, 75).into(imageView);
②占位圖是用戶調(diào)用placeholder(resId)設(shè)置:
Glide.with(mContext).load(url).placeholder(resId).into(imageView);
接著再看onSizeReady()測(cè)量完圖片尺寸后如何加載圖片的:
- // SingleRequest.java
- @Override
- public void onSizeReady(int width, int height) {
- if (status != Status.WAITING_FOR_SIZE) {
- return;
- }
- status = Status.RUNNING;
- // 獲取圖片尺寸
- float sizeMultiplier = requestOptions.getSizeMultiplier();
- this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
- this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
- // 開(kāi)始加載任務(wù)
- loadStatus =
- engine.load(
- glideContext,
- model,
- requestOptions.getSignature(),
- this.width,
- this.height,
- requestOptions.getResourceClass(),
- transcodeClass,
- priority,
- requestOptions.getDiskCacheStrategy(),
- requestOptions.getTransformations(),
- requestOptions.isTransformationRequired(),
- requestOptions.isScaleOnlyOrNoTransform(),
- requestOptions.getOptions(),
- requestOptions.isMemoryCacheable(),
- requestOptions.getUseUnlimitedSourceGeneratorsPool(),
- requestOptions.getUseAnimationPool(),
- requestOptions.getOnlyRetrieveFromCache(),
- this,
- callbackExecutor);
- }
- 可以看到真正的下載任務(wù)是在Engine類的load方法中實(shí)現(xiàn)的,其中也涉及到了圖片緩存邏輯;
- 最終通過(guò)Handler機(jī)制,Glide從工作線程切換到主線程,并最終將Drawable對(duì)象顯示到ImageView上;
總結(jié)
Glide初始化、顯示占位圖、圖片封面的整個(gè)業(yè)務(wù)流程都走完了;
可以從中學(xué)習(xí)glide中的設(shè)計(jì)模式:?jiǎn)卫J?、工廠方法、策略模式等等,發(fā)現(xiàn)自己的不足之處;
Glide的圖片緩存可以看上一篇文章