Glide源碼分析其一:基本流程

使用最為基本的用法:


Glide.with(this).load(imageUr).into(imageView);

Glide.with(this)

分析

0凑懂、先來看看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;
/**默認(rèn)的配置**/
private DefaultOptions options;
}

1或详、RequestManagerRetriever.get()獲取到RequestManagerRetriever的單例乒躺;
2、retriever.get(activity)獲取到RequestManager的實(shí)例,作用是:
這里會根據(jù)當(dāng)前代碼的所處線程和activity的類別(Activity,F(xiàn)ragmentActivity,F(xiàn)rament和context(contextWrapper))善绎,getApplicationManager到不同的RequestManager
-如果是后臺線程诫尽,會再次詢問是否仍在后臺線程中運(yùn)行禀酱,如果是的話就創(chuàng)建一個(gè)RequestManager的對象

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;
}

從注釋中可以得知:處于后臺運(yùn)行中的或者使用Application創(chuàng)建的RequestManager,是不能監(jiān)聽到Application已經(jīng)啟動的回調(diào)的,于是就force the manager to start resumed using ApplicationLifecycle牧嫉。
-如果在非后臺線程:
-FragmentActivity:

public RequestManager get(FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
/**每次都會新建一個(gè)FragmentManager的對象**/
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm);
}
}

.load(imageUrl)

/**
* 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 load(String string) {
return (DrawableTypeRequest) fromString().load(string);
}

fromString()

private DrawableTypeRequest loadGeneric(Class modelClass) {
ModelLoader streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
ModelLoader 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(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
glide, requestTracker, lifecycle, optionsApplier));
}

這樣剂跟,就得到了一個(gè)DrawableTypeRequest(繼承與GenericRequestBuilder),以下是繼承關(guān)系圖(as中按F4)


genericRequestBuilder
genericRequestBuilder

load()

這個(gè)倒是沒有什么酣藻,就是說明接下來使用的DrawableTypeRequest中的ModelType是何種類型的曹洽。而這個(gè)ModelType解釋是:

The type of model representing the resource.

into()

return into(glide.buildImageViewTarget(view, transcodeClass));

會先生成一個(gè)ImageViewTarget

public class ImageViewTargetFactory {
@SuppressWarnings("unchecked")
public Target buildTarget(ImageView view, Class clazz) {
if (GlideDrawable.class.isAssignableFrom(clazz)) {
return (Target) new GlideDrawableImageViewTarget(view);
} else if (Bitmap.class.equals(clazz)) {
return (Target) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (Target) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException("Unhandled class: " + clazz
+ ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
}

再來執(zhí)行最終的生成request對象和request的過程

/**
* Set the target the resource will be loaded into.
*
* @see Glide#clear(com.bumptech.glide.request.target.Target)
*
* @param target The target to load the resource into.
* @return The given target.
*/
public > Y into(Y target) {
Util.assertMainThread();
if (target == null) {
throw new IllegalArgumentException("You must pass in a non null Target");
}
if (!isModelSet) {
throw new IllegalArgumentException("You must first set a model (try #load())");
}
/**其實(shí)就是獲取view中的tag(Request對象)**/
Request previous = target.getRequest();
/**如果該view有已經(jīng)有Request請求了,那么就先取消**/
if (previous != null) {
previous.clear();
requestTracker.removeRequest(previous);
previous.recycle();
}
Request request = buildRequest(target);
/**設(shè)置view的tag **/
target.setRequest(request);
/**添加請求過程中的生命周期**/
lifecycle.addListener(target);
requestTracker.runRequest(request);
return target;
}

然后就runRequest了

/**
* Starts tracking the given request.
*/
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}

接下來就是request開始執(zhí)行:
先來看看Engine都有什么成員變量

public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
private static final String TAG = "Engine";
/**存儲EngineJob的集合**/
private final Map jobs;
private final EngineKeyFactory keyFactory;
private final MemoryCache cache;
private final EngineJobFactory engineJobFactory;
private final Map>> activeResources;
private final ResourceRecycler resourceRecycler;
private final LazyDiskCacheProvider diskCacheProvider;
// Lazily instantiate to avoid exceptions if Glide is initialized on a background thread. See #295.
private ReferenceQueue> resourceReferenceQueue;
/**
* Allows a request to indicate it no longer is interested in a given load.
*/
public static class LoadStatus {
private final EngineJob engineJob;
private final ResourceCallback cb;
public LoadStatus(ResourceCallback cb, EngineJob engineJob) {
this.cb = cb;
this.engineJob = engineJob;
}
public void cancel() {
engineJob.removeCallback(cb);
}
}

EngineRunnable****(實(shí)際進(jìn)行圖片請求的****task

@Override
public void run() {
......
Exception exception = null;
Resource resource = null;
try {
/** task運(yùn)行的核心代碼**/
resource = decode();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception decoding", e);
}
exception = e;
}
......
}

然后會執(zhí)行decode()方法

private Resource decode() throws Exception {
if (isDecodingFromCache()) {
return decodeFromCache();
} else {
return decodeFromSource();
}
}

decodeResultFromCache()方法

public Resource decodeResultFromCache() throws Exception {
if (!diskCacheStrategy.cacheResult()) {
return null;
}
long startTime = LogTime.getLogTime();
Resource transformed = loadFromCache(resultKey);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded transformed from cache", startTime);
}
startTime = LogTime.getLogTime();
/**這里把從cache中拿到的結(jié)果進(jìn)行轉(zhuǎn)碼,后面做詳細(xì)的分析**/
Resource result = transcode(transformed);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transcoded transformed from cache", startTime);
}
return result;
}
private Resource decodeSource() throws Exception {
Resource decoded = null;
try {
long startTime = LogTime.getLogTime();
/**使用ExecutorService組織請求**/
final A data = fetcher.loadData(priority);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Fetched data", startTime);
}
if (isCancelled) {
return null;
}
decoded = decodeFromSourceData(data);
} finally {
fetcher.cleanup();
}
return decoded;
}

其中fetcher是接口DataFetcher對象辽剧,DataFetcher的實(shí)現(xiàn)類包括:

fetcher
fetcher

HttpUrlFetcher為例分析:

@Override
public InputStream loadData(Priority priority) throws Exception {
return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());
}

-glideUrl.toURL():這里需要安全的裝換到一個(gè)可以訪問的URL,具體可見http://stackoverflow.com/questions/3286067/url-encoding-in-android

final int statusCode = urlConnection.getResponseCode();
if (statusCode / 100 == 2) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (statusCode / 100 == 3) {
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new IOException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else {
if (statusCode == -1) {
throw new IOException("Unable to retrieve response code from HttpUrlConnection.");
}
throw new IOException("Request failed " + statusCode + ": " + urlConnection.getResponseMessage());
}

-如果返回的code是3開頭的重定向請求送淆,那么需要獲取重定向的地址,重新組織訪問怕轿。

以上基本上走了一遍流程偷崩,下面還要具體分析glide中的各種管理細(xì)節(jié)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末撞羽,一起剝皮案震驚了整個(gè)濱河市阐斜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌诀紊,老刑警劉巖谒出,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異邻奠,居然都是意外死亡笤喳,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進(jìn)店門碌宴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來杀狡,“玉大人,你說我怎么就攤上這事唧喉〉仿保” “怎么了忍抽?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵八孝,是天一觀的道長董朝。 經(jīng)常有香客問我,道長干跛,這世上最難降的妖魔是什么子姜? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮楼入,結(jié)果婚禮上哥捕,老公的妹妹穿的比我還像新娘。我一直安慰自己嘉熊,他們只是感情好遥赚,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著阐肤,像睡著了一般凫佛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上孕惜,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天愧薛,我揣著相機(jī)與錄音,去河邊找鬼衫画。 笑死毫炉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的削罩。 我是一名探鬼主播瞄勾,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼弥激!你這毒婦竟也來了丰榴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤秆撮,失蹤者是張志新(化名)和其女友劉穎四濒,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體职辨,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡盗蟆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了舒裤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喳资。...
    茶點(diǎn)故事閱讀 39,764評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖腾供,靈堂內(nèi)的尸體忽然破棺而出仆邓,到底是詐尸還是另有隱情鲜滩,我是刑警寧澤,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布节值,位于F島的核電站徙硅,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏搞疗。R本人自食惡果不足惜嗓蘑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望匿乃。 院中可真熱鬧桩皿,春花似錦、人聲如沸幢炸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宛徊。三九已至佛嬉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間岩调,已是汗流浹背巷燥。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留号枕,地道東北人缰揪。 一個(gè)月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像葱淳,于是被迫代替她去往敵國和親钝腺。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評論 2 354

推薦閱讀更多精彩內(nèi)容