Glide源碼有不少值得學習的地方,各類設計模式用的堪比AOSP,不過也實在是有夠複雜,以前擼了簡單的圖片加載框架,是參照Volley設計的,如今該像Glide源碼學習了.java
咱們先看一下Glide的用法,相信你們都會. 精彩之處在於android
Glide.with(TodayFragment.this)
.load(gankDay.results.福利.get(0).getUrl())
.centerCrop()
.crossFade()
.error(R.drawable.jay)
.into(mImageView);複製代碼
with起到綁定生命週期的做用,這裏的with多是多種類型,Glide都幫咱們複寫了.with的效果主要是幫咱們給當前對象綁定上生命週期.git
以Fragment爲調用方舉例.github
public static RequestManager with(Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}複製代碼
咱們去RequestManagerRetriever
看看.設計模式
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);
}
}
RequestManager supportFragmentGet(Context context, final FragmentManager fm) {
SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(TAG);
if (current == null) {
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
current = new SupportRequestManagerFragment();
pendingSupportRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifecycle());
current.setRequestManager(requestManager);
}
return requestManager;
}複製代碼
能夠看到主要流程就是,去 FragmentManager尋找fragment,fm.findFragmentByTag(TAG);
, 若是找不到,就實類化一個current = new SupportRequestManagerFragment();
,並添加進去緩存
那SupportRequestManagerFragment
是什麼呢?咱們進去看網絡
public class SupportRequestManagerFragment extends Fragment {
private RequestManager requestManager;
private final ActivityFragmentLifecycle lifecycle;
public SupportRequestManagerFragment() {
this(new ActivityFragmentLifecycle());
}
...
}複製代碼
SupportRequestManagerFragment是繼承於Fragment
,並實現了各個生命週期的回調,最終回調lifecycle
接口.這裏就實現了生命週期的監聽.app
ActivityFragmentLifecycle
是一個觀察者,裏面有一個Set集合存放了LifecycleListener
, 這是標準的觀察者模式的寫法,每次回調都會遍歷集合分發事件.代碼以下.框架
class ActivityFragmentLifecycle implements Lifecycle {
private final Set<LifecycleListener> lifecycleListeners =
Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<LifecycleListener, Boolean>()));
private boolean isStarted;
private boolean isDestroyed;
@Override
public void addListener(LifecycleListener listener) {
lifecycleListeners.add(listener);
if (isDestroyed) {
listener.onDestroy();
} else if (isStarted) {
listener.onStart();
} else {
listener.onStop();
}
}
void onStart() {
isStarted = true;
for (LifecycleListener lifecycleListener : lifecycleListeners) {
lifecycleListener.onStart();
}
}
void onStop() {
isStarted = false;
for (LifecycleListener lifecycleListener : lifecycleListeners) {
lifecycleListener.onStop();
}
}
void onDestroy() {
isDestroyed = true;
for (LifecycleListener lifecycleListener : lifecycleListeners) {
lifecycleListener.onDestroy();
}
}
}複製代碼
此時咱們再回到RequestManagerRetriever
的裏面,該走下一步了,咱們要返回的是RequestManager
,
同樣的套路,先去拿,拿不到就實類化.ide
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifecycle());
current.setRequestManager(requestManager);
}
return requestManager;複製代碼
經過current.getLifecycle()
將RequestManagerFragment
的生命週期傳給RequestManager,又把這個RequestManager
設置進RequestManagerFragment
,這邊至關因而互相持有了.其實設置生命週期給RequestManager徹底沒有必要這麼寫,由於Current已經持有RequestManager了.
最終咱們返回RequestManager,咱們如今看RequestManager是什麼?
更精彩的來了!!!
看一下RequestManager
代碼
public class RequestManager implements LifecycleListener {
private final Context context;
private final Lifecycle lifecycle;
private final RequestTracker requestTracker;
private final Glide glide;
private final OptionsApplier optionsApplier;
private DefaultOptions options;
...
this.glide = Glide.get(context);
lifecycle.addListener(this);
...
@Override
public void onStart() {
// onStart might not be called because this object may be created after the fragment/activity's onStart method.
resumeRequests();
}
/** * Lifecycle callback that unregisters for connectivity events (if the android.permission.ACCESS_NETWORK_STATE * permission is present) and pauses in progress loads. */
@Override
public void onStop() {
pauseRequests();
}
/** * Lifecycle callback that cancels all in progress requests and clears and recycles resources for all completed * requests. */
@Override
public void onDestroy() {
requestTracker.clearRequests();
}
}複製代碼
咱們獲得幾個有用信息,
implements LifecycleListener
,實現了這個接口,咱們上面還講到觀察者模式,在ActivityFragmentLifecycle
裏面,此時RequestManager
擁有了生命週期的回調,咱們從代碼能夠看到,它在每一個生命週期裏面進行了取消,暫停,恢復請求的操做.Glide
變量,Glide類是單例的.咱們看經過Glide.get(context);
拿到Glide類.咱們知道,不少框架須要在Application裏面初始化,由於確定要持有Context
,又不想綁定某個Activity的Context,所以用Application
的Context
.這就形成了要在Application
裏面初始化的結果.包括個人SherlockImageLoader也是這麼寫的.如今看樣子須要改進了.
咱們看看get方法.
/** * Get the singleton. * * @return the singleton */
public static Glide get(Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
Context applicationContext = context.getApplicationContext();
List<GlideModule> modules = new ManifestParser(applicationContext).parse();
GlideBuilder builder = new GlideBuilder(applicationContext);
for (GlideModule module : modules) {
module.applyOptions(applicationContext, builder);
}
glide = builder.createGlide();
for (GlideModule module : modules) {
module.registerComponents(applicationContext, glide);
}
}
}
}
return glide;
}複製代碼
從上面能夠看到,get時候傳入context,但並非每次傳的context都會被用到的,只有第一次使用的時候這個Context會被用到,也只是用它來獲取Application的Context. 而後利用Application
的Context
來惰性初始化咱們的全局Glide
對象.
好咱們RequestManager先暫停一下,咱們看with完了,咱們拿到RequestManager
後該幹什麼.
Glide.with(TodayFragment.this).load(gankDay.results.福利.get(0).getUrl())...
咱們會調用load()
方法,或者是fromUri()
,loadFromMediaStore()
,load(File file)
..等多種重載.可是咱們會獲得一個新的對象叫DrawableTypeRequest
,很明顯,這是用來幫助咱們初始化請求的類.咱們去看看.
public class DrawableTypeRequest<ModelType> extends DrawableRequestBuilder<ModelType> implements DownloadOptions {
public BitmapTypeRequest<ModelType> asBitmap() {
return optionsApplier.apply(new BitmapTypeRequest<ModelType>(this, streamModelLoader,
fileDescriptorModelLoader, optionsApplier));
}
@Override
public DrawableRequestBuilder<ModelType> animate(ViewPropertyAnimation.Animator animator) {
super.animate(animator);
return this;
}
/** * {@inheritDoc} */
@Override
public DrawableRequestBuilder<ModelType> animate(int animationId) {
super.animate(animationId);
return this;
}
/** * {@inheritDoc} */
@Override
public DrawableRequestBuilder<ModelType> placeholder(int resourceId) {
super.placeholder(resourceId);
return this;
}
}複製代碼
果真是,是一個Builder,咱們能夠用來設置各類模式,各類狀況.這個Builder
很是的大.由於要考慮到全世界的需求啊.裏面的動畫和編解碼等都是不少的,不過這些都是應付各類各樣的業務.
咱們能夠看一下UML圖就知道了.
知道了是Builder後,咱們直接跳去builder的into
方法看.
public Target<TranscodeType> into(ImageView view) {
Util.assertMainThread();
if (view == null) {
throw new IllegalArgumentException("You must pass in a non null View");
}
if (!isTransformationSet && view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
applyCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
applyFitCenter();
break;
//$CASES-OMITTED$
default:
// Do nothing.
}
}
return into(glide.buildImageViewTarget(view, transcodeClass));
}
public <Y extends Target<TranscodeType>> 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())");
}
Request previous = target.getRequest();
if (previous != null) {
previous.clear();
requestTracker.removeRequest(previous);
previous.recycle();
}
//建立請求對象
Request request = buildRequest(target);
target.setRequest(request);
//將target加入lifecycle
lifecycle.addListener(target);
//執行請求
requestTracker.runRequest(request);
return target;
}複製代碼
咱們知道了三點:
咱們看看上面代碼裏面的buildRequest
方法.
private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority, RequestCoordinator requestCoordinator) {
return GenericRequest.obtain(
loadProvider,
model,
signature,
context,
priority,
target,
sizeMultiplier,
placeholderDrawable,
placeholderId,
errorPlaceholder,
errorId,
requestListener,
requestCoordinator,
glide.getEngine(),
transformation,
transcodeClass,
isCacheable,
animationFactory,
overrideWidth,
overrideHeight,
diskCacheStrategy);
}複製代碼
裏面有一個享元模式,有點相似於Message.obtain同樣,都是去生成Request,而且都是複用.
這是一個插曲,回到into()
裏面來,最後生成了Request
後, 調用requestTracker.runRequest(request);
/** * Starts tracking the given request. */
public void runRequest(Request request) {
//添加request對象到集合中
requests.add(request);
if (!isPaused) {
//若是當前狀態是非暫停的,調用begin方法發送請求
request.begin();
} else {
//將請求加入到掛起的請求集合
pendingRequests.add(request);
}
}複製代碼
咱們能夠看到.將Request
添加進一個set後,仍是調用了Request
的begin
方法,這個Request是GenericRequest
咱們進去看GenericRequest
的begin
方法
@Override
public void begin() {
startTime = LogTime.getLogTime();
if (model == null) {
onException(null);
return;
}
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}複製代碼
在這邊回調了target.onLoadStarted(getPlaceholderDrawable());
方法,去設置佔位圖.這裏的Target有不少種
不過都是回調他們的生命週期onLoadStart了.
這裏咱們來注意幾個細節,首先若是model等於null,model也就是咱們在第二步load()方法中傳入的圖片URL地址,這個時候會調用onException()方法。若是你跟到onException()方法裏面去看看,你會發現它最終會調用到一個setErrorPlaceholder()當中.就是加載錯誤圖片
GenericRequest
類中還有個重要方法,剛剛的begin
方法裏面會調用到.
咱們看看代碼.
/** * A callback method that should never be invoked directly. */
@Override
public void onSizeReady(int width, int height) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
width = Math.round(sizeMultiplier * width);
height = Math.round(sizeMultiplier * height);
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
if (dataFetcher == null) {
onException(new Exception("Got null fetcher from model loader"));
return;
}
ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadedFromMemoryCache = true;
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);
loadedFromMemoryCache = resource != null;
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}複製代碼
在上面咱們看到了不少關鍵詞Loader
,ResourceTranscoder
,loadProvider
,loadedFromMemoryCache
,這些不正是咱們設計圖片加載框架最核心的地方麼,加載器,轉碼器,加載器管理器,緩存池都在此處有影子.
加載器,轉碼器等是怎麼根據類型判斷的咱們能夠跳過,加載器和轉碼器有不少種,又是一個大的抽象樹.可是不是重點,咱們看這些只要看其頂級接口就好了.
咱們直接去看engin怎麼load的.
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher, DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder, Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
final String id = fetcher.getId();
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
// 從緩存加載
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
// 獲取數據成功,會回調target的onResourceReady() 結束
cb.onResourceReady(cached);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
// 嘗試從活動Resources 中獲取,它表示的是當前正在使用的Resources,與內存緩存不一樣之處是clear緩存時不會clear它。
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
// 成功 再見
cb.onResourceReady(active);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
//判斷jobs中是否已經存在任務,若是存在說明任務以前已經提交了
EngineJob current = jobs.get(key);
if (current != null) {
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
//緩存沒有獲取到,建立EngineJob 對象
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}複製代碼
上面步驟爲:
如今咱們去看EngineRunnable
到底幹了啥.
@Override
public void run() {
if (isCancelled) {
return;
}
Exception exception = null;
Resource<?> resource = null;
try {
resource = decode();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception decoding", e);
}
exception = e;
}
if (isCancelled) {
if (resource != null) {
resource.recycle();
}
return;
}
if (resource == null) {
onLoadFailed(exception);
} else {
onLoadComplete(resource);
}
}複製代碼
媽的好像沒幹什麼事情,就主要調了decode()
方法啊,以後都回調成功或者失敗的結果了.說明確定就在decode()
方法裏面.
private Resource<?> decode() throws Exception {
if (isDecodingFromCache()) {
return decodeFromCache();
} else {
return decodeFromSource();
}
}
private Resource<?> decodeFromCache() throws Exception {
Resource<?> result = null;
try {
result = decodeJob.decodeResultFromCache();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Exception decoding result from cache: " + e);
}
}
if (result == null) {
result = decodeJob.decodeSourceFromCache();
}
return result;
}
private Resource<?> decodeFromSource() throws Exception {
return decodeJob.decodeFromSource();
}複製代碼
上面一看,decode()
又去調用decodeFromCache()
或decodeFromSource()
了.抓頭,這麼快就decode了?source在哪來的啊,咱們直接點進去看,咱們傳入的是url的時候,目前而言,圖片還沒下載下來呢.
public Resource<Z> decodeFromSource() throws Exception {
Resource<T> decoded = decodeSource();
return transformEncodeAndTranscode(decoded);
}
private Resource<T> decodeSource() throws Exception {
Resource<T> decoded = null;
try {
long startTime = LogTime.getLogTime();
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;
}複製代碼
有一絲但願,咱們看到了原來這裏面不是decode咱們的圖片.並且是decode咱們的url資源. 看到了關鍵詞fetcher.loadData(priority);
由於事先就用uml生成工具看了Glide的uml圖,fetcher但是擔任load任務的人.
咱們看一下fetcher的uml
裏面但是有加載方法的.咱們去看一個Fetcher
,就使用率最高的HttpUrlFetcher
吧.
public InputStream loadData(Priority priority) throws Exception {
return this.loadDataWithRedirects(this.glideUrl.toURL(), 0, (URL)null);
}
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl) throws IOException {
if(redirects >= 5) {
throw new IOException("Too many (> 5) redirects!");
} else {
try {
if(lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
throw new IOException("In re-direct loop");
}
} catch (URISyntaxException var7) {
;
}
this.urlConnection = this.connectionFactory.build(url);
this.urlConnection.setConnectTimeout(2500);
this.urlConnection.setReadTimeout(2500);
this.urlConnection.setUseCaches(false);
this.urlConnection.setDoInput(true);
this.urlConnection.connect();
if(this.isCancelled) {
return null;
} else {
int statusCode = this.urlConnection.getResponseCode();
if(statusCode / 100 == 2) {
this.stream = this.urlConnection.getInputStream();
return this.stream;
} else if(statusCode / 100 == 3) {
String redirectUrlString = this.urlConnection.getHeaderField("Location");
if(TextUtils.isEmpty(redirectUrlString)) {
throw new IOException("Received empty or null redirect url");
} else {
URL redirectUrl = new URL(url, redirectUrlString);
return this.loadDataWithRedirects(redirectUrl, redirects + 1, url);
}
} else if(statusCode == -1) {
throw new IOException("Unable to retrieve response code from HttpUrlConnection.");
} else {
throw new IOException("Request failed " + statusCode + ": " + this.urlConnection.getResponseMessage());
}
}
}
}複製代碼
不出所料,在這裏加載url,從網絡獲取資源. 感恩,咱們終於找到調用urlConnection的源碼了!能夠看到咱們拿到了網絡請求的InputStream
,
對應於HttpUrlFetcher
的是ImageVideoBitmapDecoder
, 它是接收InputStream
,由於這個InputStream
類型對於這兩貨來講都是T類型.相對應的.
代碼以下,能夠看到,咱們的經過decode,成功拿到bitmap.剛剛的InputStream
已經被封裝到ImageVideoWrapper source
裏面了.經過InputStream is = source.getStream();
拿到.
public class ImageVideoBitmapDecoder implements ResourceDecoder<ImageVideoWrapper, Bitmap> {
private static final String TAG = "ImageVideoDecoder";
private final ResourceDecoder<InputStream, Bitmap> streamDecoder;
private final ResourceDecoder<ParcelFileDescriptor, Bitmap> fileDescriptorDecoder;
public ImageVideoBitmapDecoder(ResourceDecoder<InputStream, Bitmap> streamDecoder, ResourceDecoder<ParcelFileDescriptor, Bitmap> fileDescriptorDecoder) {
this.streamDecoder = streamDecoder;
this.fileDescriptorDecoder = fileDescriptorDecoder;
}
@SuppressWarnings("resource")
// @see ResourceDecoder.decode
@Override
public Resource<Bitmap> decode(ImageVideoWrapper source, int width, int height) throws IOException {
Resource<Bitmap> result = null;
InputStream is = source.getStream();
if (is != null) {
try {
result = streamDecoder.decode(is, width, height);
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Failed to load image from stream, trying FileDescriptor", e);
}
}
}
if (result == null) {
ParcelFileDescriptor fileDescriptor = source.getFileDescriptor();
if (fileDescriptor != null) {
result = fileDescriptorDecoder.decode(fileDescriptor, width, height);
}
}
return result;
}
@Override
public String getId() {
return "ImageVideoBitmapDecoder.com.bumptech.glide.load.resource.bitmap";
}
}複製代碼
都拿到了bitmap了,下面水到渠成了
哎呀我去,太長了,寫了三小時,好不容易拿到bitmap了.接下來就是如何顯示如何回調了. 顯示回調部分,之後有機會再分析.
本文做者:Anderson/Jerey_Jobs
博客地址 : jerey.cn/
簡書地址 : Anderson大碼渣
github地址 : github.com/Jerey-Jobs