Glide源码分析以及三级缓存原理
?? Glide是Android端開源圖片加載庫,能夠幫助我們下載、緩存、展示多種格式圖片。也是現(xiàn)在主流圖片加載框架之一。源碼內部究竟是如何實現(xiàn)的呢?講解主流程,簡略分析。
用法如下:
Glide.with(context).load(url).into(imageView);我這里拆分為三步分析:
一、with(context)
點擊源碼查看到是多個重載方法activity、fragment、view等等,下面用其中一個方法來展示
@NonNullpublic static RequestManager with(@NonNull Activity activity) {return getRetriever(activity).get(activity);} @NonNullprivate 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();}調用getRetriever方法獲取RequestManagerRetriever對象。在創(chuàng)建該對象之前首先通過Glide.java中的get方法獲得了Glide單例對象以及AppClideModule等配置。
@NonNullpublic static Glide get(@NonNull Context context) {if (glide == null) {GeneratedAppGlideModule annotationGeneratedModule =getAnnotationGeneratedGlideModules(context.getApplicationContext());synchronized (Glide.class) {if (glide == null) {checkAndInitializeGlide(context, annotationGeneratedModule);}}}return glide;}?下面的get方法可知道,在子線程不會添加生命周期;主線程添加一個空白的fragment來處理生命周期。最后返回RequestManager對象
@NonNullpublic 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);}//調用get判斷線程@NonNullpublic RequestManager get(@NonNull FragmentActivity activity) {if (Util.isOnBackgroundThread()) {//子線程return get(activity.getApplicationContext());} else {//主線程添加生命周期assertNotDestroyed(activity);FragmentManager fm = activity.getSupportFragmentManager();return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));}}二、load(url)
?上面執(zhí)行完成到這里已經(jīng)拿到RequestManager對象,然后調用load(url)。看源碼可知是多個重載方法,傳不同類型的資源。最終拿到RequestBuilder對象
// RequestManager.java 的代碼如下public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {return asDrawable().load(bitmap);}public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {return asDrawable().load(drawable);}public RequestBuilder<Drawable> load(@Nullable String string) {return asDrawable().load(string);}public RequestBuilder<Drawable> load(@Nullable Uri uri) {return asDrawable().load(uri);}public RequestBuilder<Drawable> load(@Nullable File file) {return asDrawable().load(file);}public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) {return asDrawable().load(resourceId);}public RequestBuilder<Drawable> load(@Nullable URL url) {return asDrawable().load(url);}public RequestBuilder<Drawable> load(@Nullable byte[] model) {return asDrawable().load(model);}public RequestBuilder<Drawable> load(@Nullable Object model) {return asDrawable().load(model);}三、into(imageView)
? 上一步拿到了RequestBuilder對象,調用into可知有2個重載方法。into的參數(shù)就是最終顯示的控件。
? into方法內部代碼分支很多,代碼龐大,所以只需走主流程如何顯示ImageView的實現(xiàn)即可。當into內部代碼執(zhí)行完成后回到 buildImageViewTarget方法,這個方法是顯示使用的,通過Executors.mainThreadExecutor())來切主線程,最終顯示控件。
return into(glideContext.buildImageViewTarget(view, transcodeClass),/*targetListener=*/ null,requestOptions,Executors.mainThreadExecutor());點擊到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 the request is completed, beginning again will ensure the result is re-delivered,// triggering RequestListeners and Targets. If the request is failed, beginning again will// restart the request, giving it another chance to complete. If the request is already// running, we can let it continue running without interruption.if (!Preconditions.checkNotNull(previous).isRunning()) {// Use the previous request rather than the new one to allow for optimizations like skipping// setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions// that are done in the individual Request.previous.begin();}return target;}這里處理請求
?Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
將請求對象裝到集合中,并且有加鎖處理,運用于多線程的并發(fā)請求。
url請求走如下:
網(wǎng)絡請求完成callback.onDataReady(result),開始一步一步往回傳數(shù)據(jù)。在這一系列過程中,進行了數(shù)據(jù)處理,比如:圖片壓縮等。 省略N步驟
// HttpUrlFetcher.java 代碼如下@Overridepublic 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));}}}最后回到了ImageViewTarget類,顯示控件。這就是整體簡略主流程。
@Overridepublic void setDrawable(Drawable drawable) {view.setImageDrawable(drawable);}四、緩存原理分析
?當加載圖片會走2種方式:
1、是Http/IO? ;
2、三級緩存策略
一級緩存:活動緩存 ,當前Activity退出緩存銷毀。
二級緩存:LRU內存緩存 ,APP應用退出緩存銷毀。
三級緩存:LRU磁盤緩存?,一直存在。
? 一、緩存機制加載流程:
? ? 獲取順序是,先從活動緩存取,如果沒有就再去內存緩存取,如果還沒是沒有就再去磁盤緩存取,都沒有就再去網(wǎng)絡下載。
? 二、緩存介紹:
? ? ? ?(1)? 活動緩存:Glide自己實現(xiàn)的一種緩存策略,將使用的對象存放在HashMap,里面使用的弱引用,不需要時立即移除及時釋放資源。
? ? (2)內存緩存:使用的LRU算法進行處理,核心是使用 LinkedHashMap 實現(xiàn),保存到內存中。
? ? ?(3)磁盤緩存:使用的LRU算法進行處理,核心是使用 LinkedHashMap 實現(xiàn),保存到磁盤中。(Glide使用DiskLruCache實現(xiàn),將圖片進行的加密、壓縮處理,所以文件讀寫比普通IO處理效率高)
? ? ? ??
? ?LRU的原理:假設 maxSize =3,當?shù)?個數(shù)據(jù)進入時,移除最先未使用的。畫圖理解一哈:
?LruCache類實際上是對LinkedHashMap進行的封裝。上代碼證明:
?
?值得注意的是,第三個參數(shù)true代表訪問排序
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);? 三、活動緩存的意義
? ?示例場景:加入maxSize=3時,有新元素添加,此刻正回收1元素,剛好頁面又使用1元素。這時候如果1元素被回收,就會找不到1元素從而崩潰。所以設計了活動緩存
?增加的活動緩存區(qū)解決上面的問題,畫圖方便理解:
? ?
?總結:1、當元素在使用時,將從內存緩存(二級緩存)移動到活動緩存(一級緩存);
? ? ? ? ? ? ?2、當元素未使用時,將從活動緩存釋放資源,然后把該元素從活動緩存移動到內存緩存;
三級緩存策略的使用總結:
1、優(yōu)先從活動緩存讀取
2、活動緩存沒有,再內存緩存中讀取
3、內存緩存沒有,再去磁盤緩存讀取
4、磁盤緩存沒有,再去網(wǎng)絡獲取本地文件讀取
總結
以上是生活随笔為你收集整理的Glide源码分析以及三级缓存原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大数据公司挖掘数据价值的49个典型案例
- 下一篇: 从开发转到安全渗透工程师,是我做的最对的