Android圖片加載框架Gilde源碼層深入分析
1.使用Gilde顯示一張圖片
Glide.with(this). load("https://cn.bing.com/sa/simg/hpb/xxx.jpg") .into(imageView);
上邊是Glide最簡單的來顯示一張圖片,雖然隻有三步操作:with、load、into,但是gilde卻通過大量的代碼在維護著。
with:返回一個RequestManager
load:返回一個RequestBuilder
下面通過源碼分析這三塊,重點是Glide生明周期的管理和緩存的使用,以及整個加載的流程的梳理。
2.Glide with操作源碼解析
Glide的with函數由於參數類型原因有多個重載,下面選擇傳入Activity當做參數的
public static RequestManager with(@NonNull Activity activity) { return getRetriever(activity).get(activity); }
這個方法就是得到一個RequestManager
分析getRetriever
private static RequestManagerRetriever getRetriever(@Nullable Context context) { 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(); }
得到一個RequestManagerRetriever,這個管理RequestManager
RequestManagerRetriever的get方法
public RequestManager get(@NonNull 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 // Only unwrap a ContextWrapper if the baseContext has a non-null application context. // Context#createPackageContext may return a Context without an Application instance, // in which case a ContextWrapper may be used to attach one. && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) { return get(((ContextWrapper) context).getBaseContext()); } } return getApplicationManager(context); }
根據不同的參數類型,執行不同的代碼邏輯。
參數是:FragmentActivity的源碼
public RequestManager get(@NonNull FragmentActivity activity) { if (Util.isOnBackgroundThread()) { return get(activity.getApplicationContext()); } else { assertNotDestroyed(activity); frameWaiter.registerSelf(activity); FragmentManager fm = activity.getSupportFragmentManager(); return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity)); } }
註意這裡判斷瞭線程,如果是子線程會直接走ApplicationContext類型的函數重載。
supportFragmentGet方法:
private RequestManager supportFragmentGet( @NonNull Context context, @NonNull FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) { SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint); RequestManager requestManager = current.getRequestManager(); if (requestManager == null) { // TODO(b/27524013): Factor out this Glide.get() call. Glide glide = Glide.get(context); requestManager = factory.build( glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context); // This is a bit of hack, we're going to start the RequestManager, but not the // corresponding Lifecycle. It's safe to start the RequestManager, but starting the // Lifecycle might trigger memory leaks. See b/154405040 if (isParentVisible) { requestManager.onStart(); } current.setRequestManager(requestManager); } return requestManager; }
這個方法會創建一個空白的SupportRequestManagerFragment,這個Fragment的作用很重要,也是Glide的核心,聲明周期的管理就是靠這個來監控的。
監控瞭:onStart、onStop、onDestroy,所以Glide在使用的時候,我們不用關心資源的釋放,因為Glide會自動釋放資源,釋放資源的依據就是這個聲明周期的管理。
get函數參數是:ApplicationContext 的源碼
private RequestManager getApplicationManager(@NonNull Context context) { // Either an application context or we're on a background thread. if (applicationManager == null) { synchronized (this) { if (applicationManager == null) { Glide glide = Glide.get(context.getApplicationContext()); applicationManager = factory.build( glide, new ApplicationLifecycle(), new EmptyRequestManagerTreeNode(), context.getApplicationContext()); } } } return applicationManager; }
可見當參數類型是:ApplicationContext或者子線程調用with方法不會創建空白的Fragmnet來進行生命周期的監控,也不會進行資源釋放,所以在調用with方法的時候不能傳ApplicationContext。
下面分析Glide分別在onStart、onStop、onDestroy執行瞭那些邏輯
RequestManager的onStart方法
public synchronized void onStart() { resumeRequests(); targetTracker.onStart(); }
執行瞭刷新請求和Tracker的onStart,resumeRequests這個跟請求的緩存的使用
RequestTracker 的resumeRequests方法
public void resumeRequests() { isPaused = false; for (Request request : Util.getSnapshot(requests)) { if (!request.isComplete() && !request.isRunning()) { request.begin(); } } pendingRequests.clear(); }
將運行隊列requests所有沒有完成又處於停止狀態的任務全部開始執行,requests是一個正在運行的請求緩存集合,這是一個set集合,處於等待狀態的請求隊列pendingRequests進行清理操作。這個就是Glide感應到聲明周期onStart進行的操作。
RequestManager的onStop方法
public synchronized void onStop() { pauseRequests(); targetTracker.onStop(); }
RequestTracker的pauseRequests方法
public void pauseRequests() { isPaused = true; for (Request request : Util.getSnapshot(requests)) { if (request.isRunning()) { request.pause(); pendingRequests.add(request); } } }
request.pause():將所有的運行緩存集合中的請求進行暫停操作,並將這些請求加入到等待緩存請求集合pendingRequests
RequestManager的onDestroy
public synchronized void onDestroy() { targetTracker.onDestroy(); for (Target<?> target : targetTracker.getAll()) { clear(target); } targetTracker.clear(); requestTracker.clearRequests(); lifecycle.removeListener(this); lifecycle.removeListener(connectivityMonitor); Util.removeCallbacksOnUiThread(addSelfToLifecycle); glide.unregisterRequestManager(this); }
可以看到Glide在感應到onDestroy會進行各種資源釋放,監聽的移除操作。
3.Glide 的load操作
RequestManager的load方法
public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) { return asDrawable().load(bitmap); } public RequestBuilder<Drawable> asDrawable() { return as(Drawable.class); } public <ResourceType> RequestBuilder<ResourceType> as( @NonNull Class<ResourceType> resourceClass) { return new RequestBuilder<>(glide, this, resourceClass, context); }
load方法返回值是RequestBuilder,通過new的方式進行創建
RequestBuilder的構造方法
protected RequestBuilder( @NonNull Glide glide, RequestManager requestManager, Class<TranscodeType> transcodeClass, Context context) { this.glide = glide; this.requestManager = requestManager; this.transcodeClass = transcodeClass; this.context = context; this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass); this.glideContext = glide.getGlideContext(); initRequestListeners(requestManager.getDefaultRequestListeners()); apply(requestManager.getDefaultRequestOptions()); }
initRequestListeners:初始化監聽
apply:校驗參數
load的過程比較簡單,重點的into將會非常復雜
4.Glide的into流程解析
public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) { return into(target, /*targetListener=*/ null, Executors.mainThreadExecutor()); } <Y extends Target<TranscodeType>> Y into( @NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener, Executor callbackExecutor) { return into(target, targetListener, /*options=*/ this, callbackExecutor); }
into另外一個重載方法:
private <Y extends Target<TranscodeType>> Y into( @NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener, BaseRequestOptions<?> options, Executor callbackExecutor) { Preconditions.checkNotNull(target); if (!isModelSet) { throw new IllegalArgumentException("You must call #load() before calling #into()"); } Request request = buildRequest(target, targetListener, options, callbackExecutor); Request previous = target.getRequest(); if (request.isEquivalentTo(previous) && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) { if (!Preconditions.checkNotNull(previous).isRunning()) { previous.begin(); } return target; } requestManager.clear(target); target.setRequest(request); requestManager.track(target, request); return target; }
buildRequest 會通過SingleRequest.obtain,創建一個SingleRequest,這個必須要前置瞭解,後邊的流程需要用到。
requestManager.track(target, request):這個是主流程,下面來看RequestManager的track方法
RequestManager的track方法
synchronized void track(@NonNull Target<?> target, @NonNull Request request) { targetTracker.track(target); requestTracker.runRequest(request); }
RequestTracker的runRequest方法
public void runRequest(@NonNull Request request) { requests.add(request); if (!isPaused) { request.begin(); } else { request.clear(); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Paused, delaying request"); } pendingRequests.add(request); } }
這裡的Request是一個接口,調用瞭begin方法,這個Request就是前面創建的SingleRequest
SingleRequest的begin
public void begin() { synchronized (requestLock) { ... if (status == Status.COMPLETE) { onResourceReady( resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false); return; } cookie = GlideTrace.beginSectionAsync(TAG); status = Status.WAITING_FOR_SIZE; if (Util.isValidDimensions(overrideWidth, overrideHeight)) { onSizeReady(overrideWidth, overrideHeight); } else { target.getSize(this); } if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE) && canNotifyStatusChanged()) { target.onLoadStarted(getPlaceholderDrawable()); } if (IS_VERBOSE_LOGGABLE) { logV("finished run method in " + LogTime.getElapsedMillis(startTime)); } } }
如果沒有設置寬高會調用 target.getSize(this)來獲取大小,如果設置瞭會執行onSizeReady方法
SingleRequest的onSizeReady方法
public void onSizeReady(int width, int height) { stateVerifier.throwIfRecycled(); synchronized (requestLock) { if (IS_VERBOSE_LOGGABLE) { logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime)); } if (status != Status.WAITING_FOR_SIZE) { return; } status = Status.RUNNING; float sizeMultiplier = requestOptions.getSizeMultiplier(); this.width = maybeApplySizeMultiplier(width, sizeMultiplier); this.height = maybeApplySizeMultiplier(height, sizeMultiplier); if (IS_VERBOSE_LOGGABLE) { logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime)); } 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); if (status != Status.RUNNING) { loadStatus = null; } if (IS_VERBOSE_LOGGABLE) { logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime)); } } }
接下來調用Engin的load方法
Engin的load的方法
public <R> LoadStatus load( GlideContext glideContext, Object model, Key signature, int width, int height, Class<?> resourceClass, Class<R> transcodeClass, Priority priority, DiskCacheStrategy diskCacheStrategy, Map<Class<?>, Transformation<?>> transformations, boolean isTransformationRequired, boolean isScaleOnlyOrNoTransform, Options options, boolean isMemoryCacheable, boolean useUnlimitedSourceExecutorPool, boolean useAnimationPool, boolean onlyRetrieveFromCache, ResourceCallback cb, Executor callbackExecutor) { long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0; EngineKey key = keyFactory.buildKey( model, signature, width, height, transformations, resourceClass, transcodeClass, options); EngineResource<?> memoryResource; synchronized (this) { memoryResource = loadFromMemory(key, isMemoryCacheable, startTime); if (memoryResource == null) { return waitForExistingOrStartNewJob( glideContext, model, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, isScaleOnlyOrNoTransform, options, isMemoryCacheable, useUnlimitedSourceExecutorPool, useAnimationPool, onlyRetrieveFromCache, cb, callbackExecutor, key, startTime); } } // Avoid calling back while holding the engine lock, doing so makes it easier for callers to // deadlock. cb.onResourceReady( memoryResource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false); return null; }
loadFromMemory這個方法是先從緩存中去查找,如果有直接就可以直接使用瞭
loadFromMemory方法
@Nullable private EngineResource<?> loadFromMemory( EngineKey key, boolean isMemoryCacheable, long startTime) { if (!isMemoryCacheable) { return null; } EngineResource<?> active = loadFromActiveResources(key); if (active != null) { if (VERBOSE_IS_LOGGABLE) { logWithTimeAndKey("Loaded resource from active resources", startTime, key); } return active; } EngineResource<?> cached = loadFromCache(key); if (cached != null) { if (VERBOSE_IS_LOGGABLE) { logWithTimeAndKey("Loaded resource from cache", startTime, key); } return cached; } return null; }
loadFromActiveResources:從活動緩存查找,如果有直接返回使用,這個緩存是一個Map集合
loadFromCache:從內存緩存查找,如果有直接使用,這個是一個LRU集合
上邊兩個是都是內存緩存,隻要顯示在頁面上的,都會緩存在活動緩存。
Engin的waitForExistingOrStartNewJob方法
private <R> LoadStatus waitForExistingOrStartNewJob( GlideContext glideContext, Object model, Key signature, int width, int height, Class<?> resourceClass, Class<R> transcodeClass, Priority priority, DiskCacheStrategy diskCacheStrategy, Map<Class<?>, Transformation<?>> transformations, boolean isTransformationRequired, boolean isScaleOnlyOrNoTransform, Options options, boolean isMemoryCacheable, boolean useUnlimitedSourceExecutorPool, boolean useAnimationPool, boolean onlyRetrieveFromCache, ResourceCallback cb, Executor callbackExecutor, EngineKey key, long startTime) { EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache); if (current != null) { current.addCallback(cb, callbackExecutor); if (VERBOSE_IS_LOGGABLE) { logWithTimeAndKey("Added to existing load", startTime, key); } return new LoadStatus(cb, current); } EngineJob<R> engineJob = //這個是線程池的管理者這個裡邊存著各個管理 engineJobFactory.build( key, isMemoryCacheable, useUnlimitedSourceExecutorPool, useAnimationPool, onlyRetrieveFromCache); DecodeJob<R> decodeJob = //這個是具體的任務 decodeJobFactory.build( glideContext, model, key, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, isScaleOnlyOrNoTransform, onlyRetrieveFromCache, options, engineJob); jobs.put(key, engineJob); engineJob.addCallback(cb, callbackExecutor); engineJob.start(decodeJob); if (VERBOSE_IS_LOGGABLE) { logWithTimeAndKey("Started new load", startTime, key); } return new LoadStatus(cb, engineJob); }
engineJob 管理一個線程池,decodeJob這個是任務的抽象是一個Runable,所以下面就會執行任務的run方法
DecodeJob的run方法
public void run() { ... try { if (isCancelled) { notifyFailed(); return; } runWrapped(); } catch (CallbackException e) { // If a callback not controlled by Glide throws an exception, we should avoid the Glide // specific debug logic below. throw e; } catch (Throwable t) { .. } finally { // Keeping track of the fetcher here and calling cleanup is excessively paranoid, we call // close in all cases anyway. if (localFetcher != null) { localFetcher.cleanup(); } GlideTrace.endSection(); } }
DecodeJob的runWrapped方法
private void runWrapped() { switch (runReason) { case INITIALIZE: stage = getNextStage(Stage.INITIALIZE); currentGenerator = getNextGenerator(); runGenerators(); break; case SWITCH_TO_SOURCE_SERVICE: runGenerators(); break; case DECODE_DATA: decodeFromRetrievedData(); break; default: throw new IllegalStateException("Unrecognized run reason: " + runReason); } }
默認會執行case INITIALIZE的runGenerators(),getNextGenerator得到的是SourceGenerator,這個必須要明確,否則走不下去。
SourceGenerator的startNext方法
public boolean startNext() { ... if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) { return true; } sourceCacheGenerator = null; loadData = null; boolean started = false; while (!started && hasNextModelLoader()) { loadData = helper.getLoadData().get(loadDataListIndex++); if (loadData != null && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource()) || helper.hasLoadPath(loadData.fetcher.getDataClass()))) { started = true; startNextLoad(loadData); } } return started; }
DecodeHelper的getLoadData方法
List<LoadData<?>> getLoadData() { if (!isLoadDataSet) { isLoadDataSet = true; loadData.clear(); List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model); //noinspection ForLoopReplaceableByForEach to improve perf for (int i = 0, size = modelLoaders.size(); i < size; i++) { ModelLoader<Object, ?> modelLoader = modelLoaders.get(i); LoadData<?> current = modelLoader.buildLoadData(model, width, height, options); if (current != null) { loadData.add(current); } } } return loadData; }
HttpGlideUrlLoader的buildLoadData方法
public LoadData<InputStream> buildLoadData( @NonNull GlideUrl model, int width, int height, @NonNull Options options) { // GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time // spent parsing urls. GlideUrl url = model; if (modelCache != null) { url = modelCache.get(model, 0, 0); if (url == null) { modelCache.put(model, 0, 0, model); url = model; } } int timeout = options.get(TIMEOUT); return new LoadData<>(url, new HttpUrlFetcher(url, timeout)); }
可以看到最終返回的是包含HttpUrlFetcher的loadData
SourceGenerator的startNextLoad方法
private void startNextLoad(final LoadData<?> toStart) { loadData.fetcher.loadData( helper.getPriority(), new DataCallback<Object>() { @Override public void onDataReady(@Nullable Object data) { if (isCurrentRequest(toStart)) { onDataReadyInternal(toStart, data); } } @Override public void onLoadFailed(@NonNull Exception e) { if (isCurrentRequest(toStart)) { onLoadFailedInternal(toStart, e); } } }); }
loadData.fetcher的fetcher就是HttpUrlFetcher
HttpUrlFetcher的loadData方法
public void loadData( @NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) { long startTime = LogTime.getLogTime(); try { InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders()); callback.onDataReady(result); } catch (IOException e) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Failed to load data for url", e); } callback.onLoadFailed(e); } finally { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime)); } } }
loadDataWithRedirects方法
private InputStream loadDataWithRedirects( URL url, int redirects, URL lastUrl, Map<String, String> headers) throws HttpException { if (redirects >= MAXIMUM_REDIRECTS) { throw new HttpException( "Too many (> " + MAXIMUM_REDIRECTS + ") redirects!", INVALID_STATUS_CODE); } else { // Comparing the URLs using .equals performs additional network I/O and is generally broken. // See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html. try { if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) { throw new HttpException("In re-direct loop", INVALID_STATUS_CODE); } } catch (URISyntaxException e) { // Do nothing, this is best effort. } } urlConnection = buildAndConfigureConnection(url, headers); try { // Connect explicitly to avoid errors in decoders if connection fails. urlConnection.connect(); // Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352. stream = urlConnection.getInputStream(); } catch (IOException e) { throw new HttpException( "Failed to connect or obtain data", getHttpStatusCodeOrInvalid(urlConnection), e); } ... }
到這裡就看到urlConnection拿到圖片的InputStream,終於從一個網絡鏈接請求到流瞭,然後就是將流轉成bitmap顯示的過程瞭,感興趣的可以繼續查看。
5.Glide
聲明周期和緩存總結 Glide 調用with函數如果傳入的ApplicationContext或者在子線程不會為我們創建空白的Fragment,不會進行生命周期的監控,也就不會進行資源自動釋放,所以不用傳ApplicationContext。其他情況glide會自動創建空白的Fragment來進行生命周期監控,並自動進行資源釋放。
Glide的緩存分為4級緩存:活動緩存、內存緩存、磁盤緩存、網絡緩存
- 活動緩存,是一個Map集合,加載圖片會首先從這個緩存裡邊查找。
- 內存緩存,是一個LRU緩存集合,如果獲取緩存沒有就從內存緩存查找。
- 磁盤緩存,是disklrucache緩存集合,如果從內存緩存沒有找到就從磁盤緩存查找。
- 網絡緩存,如果磁盤緩存沒有就需要從網絡上就行請求加載瞭。
到此這篇關於Android圖片加載框架Gilde源碼層深入分析的文章就介紹到這瞭,更多相關Android Gilde內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Android 常見bug匯總及解決方案
- Android圖片加載庫Glide用法
- Android如何使用Glide加載清晰長圖
- C# 將數據庫SqlServer數據綁定到類中的過程詳解
- Android基礎入門之dataBinding的簡單使用教程