展開說說Android之Glide詳解_源碼解析

基于上一篇介紹了Glide的使用篇本文分析一下Glide的源碼實現,看看我們簡單幾步就實現的圖片展示功能在源碼中是怎樣完成的。

一、Glide中的核心文件

先逐個介紹一下個人以為的幾個核心類:?

1、Glide

Glide是必經的入口,通過Glide.get(context)主要是做初始化,包括、Registry、RequestManagerRetriever等核心組件。

2、RequestManagerRetriever

獲取RequestManager,創建無UI的RequestManagerFragment綁定生命周期監聽,通過Lifecycle監聽宿主生命周期。

3、GlideBuilder?

GlideBuilder?它的build方法會配置線程池、緩存策略等初始化Engine以及初始化Glide對象。

4、RequestBuilder

構建請求參數,asDrawableasGifasBitmap加載不同類型,默認asDrawable最終由Engine調度三級緩存(活動資源/LRU內存/磁盤)

5、?RequestManager?

管理請求隊列,協調生命周期。

6、RequestManagerFragment

?綁定生命周期?非Application上下文:創建無UI的RequestManagerFragment,通過Lifecycle監聽宿主生命周期。主線程調用時通過FragmentManager管理Fragment,子線程使用Application級生命周期。

7、LifecycleListener

定義生命周期的接口,里面onStartonStoponDestroy三個方法與宿主Fragment或Activity的同名生命周期綁定。

8、Engine

合理調度三級緩存(調取活動資源/內存/磁盤)

9、Target?

Target是一個接口,繼承自LifecycleListener并且額外增加了一些加載過程中的方法,比如onLoadStartedonLoadFailedonLoadCleared等等。他還有一個抽象的實現類BaseTarget

后面子子孫孫還有很多抽象類繼承BaseTarget,比如下一代ViewTarget、下下代ImageViewTarget

、下下下代是BitmapImageViewTargetDrawableImageViewTarget以及ThumbnailImageViewTarget,他們才是into方法傳入的實際展示圖片的控件。

是借助ImageViewTargetFactory來創建對應Target(如BitmapImageViewTarget)。

10、DecodeJob?

看得出來他是做解碼工作的,緩存未命中時創建DecodeJob,提交到EngineJob線程池。實現了Runnable接口因此再run方法中借助DataFetcher進行解碼。

11、DataFetcher

上面提到了借助DataFetcher進行解碼,但他是個接口,所以真正工作是它的實現類們,比如FileFetcherAssetPathFetcherHttpUrlFetcher等都不同地址來源的圖片進行解碼。

12、Registry

組件注冊中心,支持擴展ModelLoader等模塊根據模型類型(如String/File)匹配對應的ModelLoader

二、分階段梳理工作流程

1、注

Glide.with(mActivity).load(R.drawable.czzs_step_station).into(image);

以在Activity中加載為例,這也是使用最多的場景。其他場景請按此思路自行梳理。

2、初始化階段?:

Glide.with()通過RequestManagerRetriever獲取RequestManager,綁定生命周期監聽

2.1 獲取RequestManager

public static RequestManager with(Activity activity) {return getRetriever(activity).get(activity);
}

2.2 獲取RequestManagerRetriever

private static RequestManagerRetriever getRetriever(@Nullable Context context) {// Context could be null for other reasons (ie the user passes in null), but in practice it will// only occur due to errors with the Fragment lifecycle.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中的initializeGlide方法,此處代碼角度,只貼出實例化GlideBuilder以及通過它的build方法創建Glide對象,并將Glide對象聲明為成員變量。

GlideBuilder builder = new GlideBuilder().setRequestManagerFactory(factory);
for (GlideModule module : manifestModules) {module.applyOptions(applicationContext, builder);
}
if (annotationGeneratedModule != null) {annotationGeneratedModule.applyOptions(applicationContext, builder);
}
Glide glide = builder.build(applicationContext);
for (GlideModule module : manifestModules) {module.registerComponents(applicationContext, glide, glide.registry);
}
if (annotationGeneratedModule != null) {annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
}
Glide.glide = glide;

然后上面的builder.build(applicationContext);中初始化了EngineRequestManagerRetriever

以及Glid,當然也創建了線程池,只關鍵部分代碼:

 if (engine == null) {engine = new Engine(memoryCache, diskCacheFactory, diskCacheExecutor, sourceExecutor,GlideExecutor.newUnlimitedSourceExecutor());}RequestManagerRetriever requestManagerRetriever = new RequestManagerRetriever(requestManagerFactory);return new Glide(context,engine,memoryCache,bitmapPool,arrayPool,requestManagerRetriever,connectivityMonitorFactory,logLevel,defaultRequestOptions.lock());
}

2.3 綁定生命周期

現在看2.1中后半部分get(activity)這里是調用RequestManagerRetriever的get方法:

public RequestManager get(Activity activity) {if (Util.isOnBackgroundThread()) {return get(activity.getApplicationContext());} else {assertNotDestroyed(activity);android.app.FragmentManager fm = activity.getFragmentManager();return fragmentGet(activity, fm, null /*parentHint*/);}
}

這里出現了FragmentManager它就是要創建無UI的RequestManagerFragment,通過Lifecycle監聽宿主生命周期?創建fragment的代碼:

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm, android.app.Fragment parentHint) {RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);if (current == null) {current = pendingRequestManagerFragments.get(fm);if (current == null) {current = new RequestManagerFragment();current.setParentFragmentHint(parentHint);pendingRequestManagerFragments.put(fm, current);fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();}}return current;
}

現在準備工作基本完成了。

2、?加載階段?:

load(R.drawable.czzs_step_station).into(image);

傳入圖片路徑和ImageView組件。

public RequestBuilder<Drawable> load(@Nullable Object model) {return asDrawable().load(model);
}
RequestBuilder構建請求參數,最終由Engine調度三級緩存(活動資源/LRU內存/磁盤)。
public Target<TranscodeType> into(ImageView view) {省略很多行。。。return into(context.buildImageViewTarget(view, transcodeClass));
}

buildImageViewTarget方法創建一個Target實例,其實就是上面提到的BitmapImageViewTargetDrawableImageViewTarget

@SuppressWarnings("unchecked")
public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {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)");}
}

然后進入RequestManager的track方法:

void track(Target<?> target, Request request) {targetTracker.track(target);requestTracker.runRequest(request);
}

下一步

public void runRequest(Request request) {requests.add(request);if (!isPaused) {request.begin();} else {pendingRequests.add(request);}
}

進入Request的實現類SingleRequest中的begin方法開始處理加載。

3、?解碼階段?:

解碼是用DataFetcher和它的子實現類們:

這是加載結果的兩個回調:

這里說一下Glide是怎樣加載網絡圖片的,沒錯,和其他網絡請求一樣她也是用的HttpURLConnection

在它的實現類HttpUrlFetcherloadData方法:

@Override
public void loadData(Priority priority, DataCallback<? super InputStream> callback) {long startTime = LogTime.getLogTime();final InputStream result;try {result = loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/,glideUrl.getHeaders());} catch (IOException e) {if (Log.isLoggable(TAG, Log.DEBUG)) {Log.d(TAG, "Failed to load data for url", e);}callback.onLoadFailed(e);return;}if (Log.isLoggable(TAG, Log.VERBOSE)) {Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime)+ " ms and loaded " + result);}callback.onDataReady(result);
}

這里會將結果callback.onDataReady(result);毀掉了上面截圖中提到的onDataReady方法。

調用loadDataWithRedirects方法里:

這里如果失敗還會重新請求連接,最多重連五次:

private static final int MAXIMUM_REDIRECTS = 5;

最后各種通過FetcherReadyCallback以及其他各種回調,最終在BitmapImageViewTarget展示圖片內容:

/*** Sets the {@link android.graphics.Bitmap} on the view using {@link* android.widget.ImageView#setImageBitmap(android.graphics.Bitmap)}.** @param resource The bitmap to display.*/
@Override
protected void setResource(Bitmap resource) {view.setImageBitmap(resource);
}

三、其實還有一些其他很重要的公共類:

有一個線程池:

還一個上下文:

一個異常類:

Glide是一個非常優秀的框架,源碼架構設計的太好了。另外想了解Glide使用的朋友可以參考上一篇《展開說說Android之Glide詳解_使用篇》。

個人總結記錄,才疏學淺,如有錯誤,歡迎指正,多謝。?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/87185.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/87185.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/87185.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

商品中心—6.商品考核系統的技術文檔二

大綱 1.基于大數據系統的商品考核數據指標 2.基于商品考核數據指標的商品考核流程 3.商品考核失敗后的處理 考核流程的設計 4.商品考核系統數據庫模型設計 5.商品考核系統核心接口 6.商品生命周期系統的定時考核任務 6.商品生命周期系統的定時考核任務 (1)定時任務處理…

鴻蒙組件通用事件開發全攻略:從基礎交互到工程實踐

一、引言&#xff1a;事件系統 —— 構建交互體驗的核心樞紐 在鴻蒙應用開發體系中&#xff0c;組件事件系統是連接用戶操作與應用邏輯的關鍵橋梁。從基礎的點擊交互到復雜的多觸點手勢&#xff0c;通用事件覆蓋了全場景設備的交互需求。本文將系統解構鴻蒙事件體系的核心機制…

老項目重構難題破解:飛算 JavaAI 如何實現技術升級突圍

在企業數字化轉型進程中&#xff0c;大量 Java 老項目因長期迭代積累的技術債務&#xff0c;陷入 "重構必要性與實施難度并存" 的困境。這些遺留系統普遍存在代碼體系老化、架構模式滯后、維護成本高企等問題&#xff0c;成為企業技術升級的絆腳石。 傳統 Java 老項…

idea使用技巧分享

寫在前面 分享一些常用的idea使用技巧&#xff0c;進來看看有沒有你不知道的。 設置項目默認配置 TODO設置 位置 方式一&#xff1a;setting -> editor -> TODO 方式二&#xff1a; 定義Patterns過濾模式 正則中“\b”是元字符代表著單詞的開頭或結尾&#xff0c;也就…

【Dify精講】第8章:Agent能力實現原理【知識卡片】

第8章&#xff1a;Agent能力實現原理http://www.airinto.com/share/e7b7e27f 一、Agent架構設計 二、工具調用機制 三、ReAct框架實現 四、自定義Agent開發 五、性能優化與監控 六、總結與實戰建議

【軟件】安裝Miniconda

安裝 根據搜索結果&#xff0c;以下是使用Homebrew在macOS上安裝Miniconda的詳細步驟&#xff1a; 1.安裝Homebrew&#xff08;如果尚未安裝&#xff09; 打開終端&#xff08;Terminal&#xff09;&#xff0c;運行以下命令安裝Homebrew&#xff1a; /bin/bash -c "$(…

FastAPI:(6)錯誤處理

FastAPI&#xff1a;(6)錯誤處理 由于CSDN無法展示「漸構」的「#d&#xff0c;#e&#xff0c;#t&#xff0c;#c&#xff0c;#v&#xff0c;#a」標簽&#xff0c;推薦訪問我個人網站進行閱讀&#xff1a;Hkini 「漸構展示」如下&#xff1a; #c 概述 文章概念關系 graph TDA…

408第一季 - 數據結構 - 排序

排序的概念 外部排序很難&#xff0c;后面都是內部排序 插入排序 直接插入排序 理解 這個排序第一輪是從第二個元素開始的 然后是從后往前一個一個比的 然后我們看i5的情況&#xff0c;會出現比較次數和移動次數的概念&#xff0c;這里97動了 然后i8時&#xff0c;49最好…

高效賬號信息管理工具,可安全隨機生成密碼

軟件介紹 今天給大家推薦一款安全可靠的密碼管理工具&#xff0c;幫助用戶輕松管理各類賬號密碼。 安全便捷的密碼解決方案 這是一款采用先進加密技術開發的密碼管理器&#xff0c;不僅可以生成高強度隨機密碼&#xff0c;還提供安全的賬號密碼備份存儲功能。 基礎安全設置 …

如何在markdown文件中(博客)添加emoji表情,讓你的博客看起來更加優雅

在Markdown中使用Emoji的完整指南 按分類快速參考的完整Emoji列表一、狀態指示類:bulb:二、提示信息類:bulb:三、內容類型類:bulb:四、操作指令類:bulb:五、進度狀態類:bulb:六、技術相關類:bulb:七、人員角色類:bulb:八、版本控制類:bulb: 你學會了嗎 按分類快速參考的完整Emo…

MAZANOKE:一款隱私優先的瀏覽器圖像優化工具及Docker部署指南

在日常工作中&#xff0c;大家是否經常遇到這樣的需求&#xff1a;需要壓縮圖片體積、調整圖片尺寸或轉換圖片格式&#xff0c;但又受限于數據安全要求無法將圖片上傳至公網&#xff1f;在我們之前開發的工單配置系統中&#xff0c;這類需求尤為常見。最近在GitHub上發現了一款…

【Vue PDF】Vue PDF 組件初始不加載 pdfUrl 問題分析與修復

Vue PDF 組件初始不加載 pdfUrl 問題分析與修復 問題現象 在開發 PDF 預覽組件時&#xff0c;遇到這樣一個問題&#xff1a; 初始狀態下&#xff0c;PDF 組件不會請求 pdfUrl&#xff08;即不會加載 PDF 文件&#xff09;。只有點擊"全屏"按鈕后&#xff0c;才會請…

《注解的江湖:一場元數據的“宮斗劇”》

一、你真的懂注解嗎 你是否使用過Autowired卻不知道是如何生效的&#xff1f; 這幾個注解你一定很熟悉&#xff1a; OverrideDeprecatedTransactional 那么你有進一步思考過怎么生效的嗎&#xff1f;注解到底是什么&#xff1f;注解&#xff0c;到底是信息&#xff1f;還是指…

智能土木通 - 土木工程專業知識問答系統02-RAG檢索模塊搭建

一、項目目錄 civil_qa_system/ ├── docs/ # 項目文檔 ├── config/ # 配置文件 ├── core/ # 核心功能代碼 ├── knowledge_base/ # 知識庫相關 ├── web/ # Web應用部分 ├…

進程和線程區別、管道和套接字、共享變量、TCP三次握手,是否可以少一次握手、子進程和主進程區別和API——Nodejs

首先講了進程和線程區別 然后講解 管道和套接字&#xff0c;它是進程間通信的方式 接著講解共享變量 &#xff0c;它是線程間通信 最后講解TCP三次握手&#xff0c;因為套接字使用了TCP協議 一、線程和進程的區別 線程&#xff08;Thread&#xff09;和進程&#xff08;Pr…

docker(學習筆記第一課) 使用nginx +https + wordpress

文章目錄 docker(學習筆記第一課) 使用nginx https wordpress學習內容&#xff1a;1. 整體架構1.1 在aws ec2的整體架構1.2 不懂都可以問AI 2. 構建詳細2.1 構建ec22.2 安裝docker2.3 創建一個docker的內部network2.4 創建wordpress使用的mysql數據庫2.5 創建兩個wordpress的d…

Leetcode 刷題記錄 15 —— 二分查找

本系列為筆者的 Leetcode 刷題記錄&#xff0c;順序為 Hot 100 題官方順序&#xff0c;根據標簽命名&#xff0c;記錄筆者總結的做題思路&#xff0c;附部分代碼解釋和疑問解答&#xff0c;01~07為C語言&#xff0c;08及以后為Java語言。 01 搜索插入位置 class Solution {pub…

C++核心編程(動態類型轉換,STL,Lanmda)

一. 類型轉換 二. STL 1. 容器 1.1 Vector&#xff08;常用&#xff09; 1.1.1 概述 特性&#xff1a; 動態數組&#xff1a; 想象成一個會自動變長變短的數組。起始在內存中是連續存儲的。 隨機訪問&#xff1a; 通過[]運算符或at()方法&#xff0c;可以瞬間&#xff08;…

【圖像處理入門】8. 數學基礎與優化:線性代數、概率與算法調優實戰

摘要 圖像處理的核心離不開數學工具的支撐。本文將深入解析線性代數、概率論在圖像領域的應用,包括矩陣變換與圖像幾何操作的關系、噪聲模型的數學描述,以及遺傳算法、粒子群優化等智能算法在參數調優中的實踐。通過理論結合代碼案例,幫助讀者掌握從數學原理到工程優化的完…

操作系統八股文

一.進程和線程的區別 1.本質區別和所屬關系是什么&#xff1f; 進程是資源調度以及分配的基本單位。 線程是CPU調度的基本單位。 一個線程屬于一個進程&#xff0c;一個進程可以擁有多個線程。 2.地址空間和內存 進程擁有獨立的虛擬地址空間。 線程沒有獨立的地址空間&#xf…