Glide原始碼分析(二)---執行
吐槽
為啥csdn的排名一直沒有什麼變化啊emmmmm,這幾天5公里夜跑跑的越來越快,越來越接近25分鐘了,趕緊學習啊啊啊啊,自己真的還差好遠啊啊啊
看原始碼的感想
看到郭神部落格上面的寫的:
那麼,雖然原始碼的複雜程度是外在的不可變條件,但我們卻可以通過一些技巧來提升自己閱讀原始碼的能力。這裡我和大家分享一下我平時閱讀原始碼時所使用的技巧,簡單概括就是八個字:抽絲剝繭、點到即止。應該認準一個功能點,然後去分析這個功能點是如何實現的。但只要去追尋主體的實現邏輯即可,千萬不要試圖去搞懂每一行程式碼都是什麼意思,那樣很容易會陷入到思維黑洞當中,而且越陷越深。因為這些龐大的系統都不是由一個人寫出來的,每一行程式碼都想搞明白,就會感覺自己是在盲人摸象,永遠也研究不透。如果只是去分析主體的實現邏輯,那麼就有比較明確的目的性,這樣閱讀原始碼會更加輕鬆,也更加有成效。
之前自己看Okhttp的原始碼的時候,就是看著這些類頭都大了,感覺自己智商可能不夠的感覺,覺得寫出這些的東西的人都好厲害啊,但是,突然一想自己看原始碼是為了什麼 往小裡面說,是更好理解這個框架,讓自己得心應手,面試時候可以講 往大里面講,就是知道具體的框架主體邏輯是什麼,主體功能的實現的流程
所以,我好像並不需要現在就把所有的類啊引數啊都搞清楚,我只需要知道大概的流程是什麼樣的,這才是我看原始碼的目標,因為這些東西都是一個團隊寫的,自己一個人怎麼可能很快把這個框架完全搞懂啊。
閱讀的邏輯
這個框架的幹嘛的?-----》主要的功能是什麼?------》關鍵的跳轉方法是什麼? Glide是一個載入圖片的框架 最最主要的程式碼就是
Glide.with(上下文).load(圖片資源).into(控制元件);
然後我們先把這三個方法搞清楚邏輯
with()方法
我們首先看下這個方法,當時在用with方法的時候,我們發現這個傳入的引數可以是Activity,也可以是fragment,Context這些,所以我們可以判定這個方法肯定有很多個過載方法 進入原始碼我們看下這個方法,就發現了4個過載方法
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(android.app.Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
publc static RequestManager with(Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
雖然有四個過載方法,但是我們發現這幾個實現的邏輯都是如出一轍 都是呼叫RequestManagerRetriever.get()方法返回一個RequestManagerRetriever物件 然後再呼叫RequestManagerRetriever物件的get()方法獲取RequestManager物件
所以,這個邏輯還是蠻清楚的
接著,我們繼續看下這個RequestManagerRetriever的例項get()方法
public static RequestManagerRetriever get() {
return INSTANCE;
}
// Visible for testing.
RequestManagerRetriever() {
handler = new Handler(Looper.getMainLooper(), this /* Callback */);
}
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;
}
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);
}
public RequestManager get(FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm);
}
}
public RequestManager get(Fragment fragment) {
if (fragment.getActivity() == null) {
throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached");
}
if (Util.isOnBackgroundThread()) {
return get(fragment.getActivity().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getActivity(), fm);
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public RequestManager get(Activity activity) {
if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm);
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private static void assertNotDestroyed(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed()) {
throw new IllegalArgumentException("You cannot start a load for a destroyed activity");
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public RequestManager get(android.app.Fragment fragment) {
if (fragment.getActivity() == null) {
throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached");
}
if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
return get(fragment.getActivity().getApplicationContext());
} else {
android.app.FragmentManager fm = fragment.getChildFragmentManager();
return fragmentGet(fragment.getActivity(), fm);
}
}
然後我們看了下get()方法的幾個過載就分為兩種情況
- 傳入Application型別的引數
- 傳入非Application型別的引數//activity啊,Fragment什麼的
先看下第一種情況下的:Application引數
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);
}
如果在Glide.with()方法中傳入的是一個Application物件,那麼在這裡就會呼叫這個方法 然後在方法的最後呼叫這個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.事件,我們必須強制管理器使用ApplicationLifecycle開始恢復
applicationManager = new RequestManager(context.getApplicationContext(),
new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
}
}
}
return applicationManager;
}
就是獲取一個RequestManager物件,把註釋的幾句話谷歌翻譯下,我們就發現這塊就是它自動就是和應用程式的生命週期是同步的,如果應用程式關閉的話,Glide的載入也會同時終止
然後我們再看下第二種情況:非非Application引數 看下他們傳入的引數
- get(FragmentActivity activity)
- get(Fragment fragment)
- get(Activity activity)
- get(android.app.Fragment fragment)
然後我們發現這些方法都會呼叫這兩個方法:
supportFragmentGet(fragment.getActivity(), fm);
fragmentGet(activity, fm);
然後我們進這個兩個方法裡面看下
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
RequestManagerFragment current = getRequestManagerFragment(fm);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}
RequestManager supportFragmentGet(Context context, FragmentManager fm) {
SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}
然後我們總結髮現:
- 所有的流程都是一樣的,都是向當前的Activity裡面新增一個一個隱藏的Fragment,呼叫的方法就是supportFragmentGet(fragment.getActivity(), fm);和fragmentGet(activity, fm);這兩個
- supportFragmentGet(fragment.getActivity(), fm);是V4包下的,fragmentGet(activity, fm)是app包下的
- 之所以要新增一個Fragment的原因是,Glide需要知道這個活動的生命週期,不能你這個活動就要被關閉了,但現在用Glide才載入圖片進行了一半唉,所以肯定要通知Glide這個活動要掛了,當然不應該。可是Glide並沒有辦法知道Activity的生命週期,於是Glide就使用了新增隱藏Fragment的這種小技巧,因為Fragment的生命週期和Activity是同步的,如果Activity被銷燬了,Fragment是可以監聽到的,這樣Glide就可以捕獲這個事件並停止圖片載入了
- 如果我們是在非主執行緒當中使用的Glide,那麼不管你是傳入的Activity還是Fragment,都會被強制當成Application來處理
主要流程圖//畫的醜emmmmmmm
小結一下: 第一個with()方法的原始碼,其實就是為了得到一個RequestManager物件而已,然後Glide會根據我們傳入with()方法的引數來確定圖片載入的生命週期
load()方法
這個方法就是選擇載入資源的,肯定也是很多過載方法 看郭神上面講了URL這種的,我們也就看下這種的,因為這種的我們用的多
首先
我們在上面with()方法返回的是個RequestManager物件,所以很清楚的知道了load方法就是在這個RequestManager類裡面 然後我們去看下這個原始碼:
public class RequestManager implements LifecycleListener {
private final Context context;
private final Lifecycle lifecycle;
private final RequestManagerTreeNode treeNode;
private final RequestTracker requestTracker;
private final Glide glide;
private final OptionsApplier optionsApplier;
private DefaultOptions options;
public RequestManager(Context context, Lifecycle lifecycle, RequestManagerTreeNode treeNode) {
this(context, lifecycle, treeNode, new RequestTracker(), new ConnectivityMonitorFactory());
}
RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode,
RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
this.context = context.getApplicationContext();
this.lifecycle = lifecycle;
this.treeNode = treeNode;
this.requestTracker = requestTracker;
this.glide = Glide.get(context);
this.optionsApplier = new OptionsApplier();
ConnectivityMonitor connectivityMonitor = factory.build(context,
new RequestManagerConnectivityListener(requestTracker));
/sts();
for (RequestManager requestManager : treeNode.getDescendants()) {
requestManager.pauseRequests();
}
}
/**
* Restarts any loads that have not yet completed.
*
* @see #isPaused()
* @see #pauseRequests()
*/
modelLoader) {
return new ImageModelRequest<T>(modelLoader);
}
/**
* Returns a request builder that uses the given
* {@link com.bumptech.glide.load.model.stream.StreamByteArrayLoader} to fetch an {@link java.io.InputStream} for
* loading Bitmaps.
*
* @param modelLoader The byte array loader.
*/
public ImageModelRequest<byte[]> using(StreamByteArrayLoader modelLoader) {
return new ImageModelRequest<byte[]>(modelLoader);
}
/**
* Returns a new request builder that uses the given {@link ModelLoader} to fetch a
* {@link ParcelFileDescriptor} for loading video thumbnails.
*
* @param modelLoader The model loader to use.
* @param <T> The type of the model.
*/
public <T> VideoModelRequest<T> using(final FileDescriptorModelLoader<T> modelLoader) {
return new VideoModelRequest<T>(modelLoader);
}
/**
* Returns a request builder to load the given {@link java.lang.String}.
* signature.
*
* @see #fromString()
* @see #load(Object)
*
* @param string A file path, or a uri or url handled by {@link com.bumptech.glide.load.model.UriLoader}.
*/
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}
public DrawableTypeRequest<String> fromString() {
return loadGeneric(String.class);
}
public DrawableTypeRequest<String> fromString() {
return loadGeneric(String.class);
}
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));
}
這塊的原始碼真的是太長了,,,但是load核心的就這幾個函式 就把這個load(String string)這個方法找到,然後把URL傳進去,然後就是呼叫fromString()方法,這個方法就繼續呼叫loadGeneric()方法,並指定引數為String.class
然後我們看下這塊的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));
}
然後我們發現
-
Glide.buildStreamModelLoader()方法Glide.buildFileDescriptorModelLoader()方法獲取一個ModelLoader物件
-
ModelLoader物件是用於載入圖片的,而我們給load()方法傳入不同型別的引數,這裡也會得到不同的ModelLoader物件
-
才傳入的引數是String.class,因此最終得到的是StreamStringLoader物件,它是實現了ModelLoader介面的
-
loadGeneric()方法是要返回一個DrawableTypeRequest物件的,因此在loadGeneric()方法的最後又去new了一個DrawableTypeRequest物件,然後把剛才獲得的ModelLoader物件,還有一大堆雜七雜八的東西都傳了進去
然後我們去看下這塊的DrawableTypeRequest的作用
DrawableTypeRequest(Class<ModelType> modelClass
相關推薦
Glide原始碼分析(二)---執行
吐槽
為啥csdn的排名一直沒有什麼變化啊emmmmm,這幾天5公里夜跑跑的越來越快,越來越接近25分鐘了,趕緊學習啊啊啊啊,自己真的還差好遠啊啊啊
看原始碼的感想
看到郭神部落格上面的寫的:
那麼,雖然原始碼的複雜程度是外在的不可變條件,但我們卻可以通過一些
Glide原始碼分析(二)——從用法來看之load&into方法
上一篇,我們分析了with方法,文章連結: https://blog.csdn.net/qq_36391075/article/details/82833260
在with方法中,進行了Glide的初始化,建立了RequesManger,並且綁定了生命週期,最終返回了一個Reques
精盡MyBatis原始碼分析 - SQL執行過程(二)之 StatementHandler
> 該系列文件是本人在學習 Mybatis 的原始碼過程中總結下來的,可能對讀者不太友好,請結合我的原始碼註釋([Mybatis原始碼分析 GitHub 地址](https://github.com/liu844869663/mybatis-3)、[Mybatis-Spring 原始碼分析 GitHub 地址
NSQ原始碼分析(二)—— Topic
Topic是NSQ非常重要的概念,本次主要講述Topic的獲取、新建、Topic中訊息的輪詢、Topic中訊息的來源、Topic的刪除和退出以及Topic的暫停和取消暫停
topic的相關操作主要在nsq/nsqd/topic.go中
首先看下Topic結構體
type Topic st
Glide原始碼分析(一)從用法來看之with方法
繼續啃原始碼,用過Glide的人,肯定都覺得它好好用,我們一般只需要幾行程式碼,就可以達到我們想要的效果,可以在這個背後是什麼呢?就需要我們來看了。
我一般看原始碼,我喜歡先從用法來看,然後一步一步的再細扣,所以就先從用法來看Glide的整體流程。
用過Glide的人,用下面這段
Django rest framework 許可權操作(原始碼分析二)
知識回顧
這一篇是基於上一篇寫的,上一篇謝了認證的具體流程,看懂了上一篇這一篇才能看懂,
當用戶訪問是 首先執行dispatch函式,當執行當第二部時:
#2.處理版本資訊 處理認證資訊 處理許可權資訊 對使用者的訪問頻率進行限制
self
Glide 原始碼分析(一):圖片壓縮
關於圖片的那點事兒
Q: 一張大小為 55KB, 解析度為 1080 * 480 的 PNG 圖片,它載入近記憶體時所佔的大小是多少呢?
圖片記憶體大小
圖片佔用記憶體大小 = 解析度 * 畫素點大小
其中資料格式不同畫素點大小也不同:
ALPHA_8: 1B
RGB_565: 2B
spring原始碼分析(二):外部配置檔案的載入
@PropertySource 和@Profile
簡介: @PropertySource:用在類上,載入一個(*.properties)的配置檔案 @Profile:用在類或者方法上,指定元件在哪個環境的情況下才能被註冊到容器中,不指定,任何環境下都能註冊這個元件
使用案例1-
spring原始碼分析(二):bean元件賦值
文章目錄
常用註解
@Value和@PropertySource
介紹
使用案例
@AutoWired、@Qulifer 、@Primary
介紹
使用總結
RxJava2原始碼分析二之just、fromArray、fromIterable
Observable.just:接收1個以上,10個以下的引數,然後逐個發射。
Observable.fromArray:接收一個數組,從陣列中一個一個取出來發射。  
最新版ffmpeg原始碼分析二:transcode()函式
還是先看一下主函式吧:(省略了很多無關大雅的程式碼)
int main(int argc, char **argv)
{
OptionsContext o = { 0 };
int64_t ti;
//與命令列分析有關
Glide原始碼分析(一)---基本
吐槽
中秋節啊,自己身為一個單身程式猿又不回家,還是在實驗室學個新的框架吧,畢竟這個框架很實用,之前自己都還沒用過,就很尷尬。
那中秋快樂啊
本文思維導圖
Glide是什麼
答曰:一種安卓平臺上的圖形載入框架,一種很好很好用的框架
然後網上說了下picasso
從原始碼分析AsyncTask執行流程和原理
本篇文章將從大體上分析AsyncTask的原理,不會涉及過多的細節。AsyncTask現在已經不再流行了,但作為學習還是要了解下。
使用方法
下面是AsyncTask一般的使用場景,相信使用過AsyncTask的人都不會覺得陌生
class MyAsyncTask ex
Glide 原始碼分析(一)
Glide原始碼分析
寫在前面
在專案開發中一直使用Glide進行圖片的載入顯示,知道如何使用,但不清楚Glide內部是如何實現的。所以一直想看看Glide的原始碼實現是如何做的,終於有時間和機會了,寫下來做個筆記,也給大家學習下。
Glide載入基本步驟
一般
ArrayList的原始碼分析(二)
上篇文章給大家介紹了arraylist集合原始碼的一些屬性和擴容方式add方法,接下來再和大家來聊聊這個集合的一些原始碼
首先看看remove的方法,這個方法有兩個,一個是根據下標刪除物件,一個是根據物件刪除
rangeCheck(index);是一個判斷index引數的規範的,如果太長或者
深入原始碼分析Java執行緒池的實現原理
程式的執行,其本質上,是對系統資源(CPU、記憶體、磁碟、網路等等)的使用。如何高效的使用這些資源是我們程式設計優化演進的一個方向。今天說的執行緒池就是一種對CPU利用的優化手段。
網上有不少介紹如何使用執行緒池的文章,那我想說點什麼呢?我希望通過學習執行緒池原理,明白所有
java集合原始碼分析(二)---ListIterator與Iterator
吐槽
早上起來刷牙洗臉洗頭髮後,正準備去上課,然後發現今天早上好像不上課emmmmmm,然後就早上繼續看下java集合方面的原始碼了吼吼吼
Iterator
上次好像說過一點這個,然後繼續複習下這個
還是先看下官方文件
然後我們發現這個東西就是個功能很少但很
React原始碼分析(二)-元件的初始渲染
上一篇文章講到了React 呼叫ReactDOM.render首次渲染元件的前幾個過程的原始碼, 包括建立元素、根據元素例項化對應元件, 利用事務來進行批量更新. 我們還穿插介紹了React 事務的實現以及如何利用事務進行批量更新的實現. 這篇文章我們接著分析後面的過程,
原始碼分析篇之String原始碼分析二
前面已經分析過String原始碼為什麼是不可變的,同時通過我們常用的String的相關的類StringBuffer和StringBuilder,我們可以發現String類中欄位名被定義為了final型別,這樣的話將只能被賦值一次。接下來,繼續看String原始碼實現的
JDK1.8集合框架原始碼分析二-------------LinkedList
0.LinkedList特點
0.1) 刪除或新增效率高:不會移動資料元素,只會維護內部節點的關係
0.2) 查詢慢: 在其內部沒有下標,也即沒有索引,需要使用for迴圈遍歷,LInkedList為了提高效率,