Android 經常使用開源框架源碼解析 系列 (四)Glide

1、定義 
 
    Glide 一個被google所推薦的圖片加載庫,做者是bumptech。對Android SDk 最低要求是 API 10 
    與之功能相似的是Square公司的picasso 
 
2、基本概念
 
    Model :數據來源 :Uri、本地文件、資源ID
    Data :加工數據
    Resource :對原始數據進行解碼,解碼以後的資源 resource
    Resource decode :資源解碼器
    TransformedResource:轉換資源
    TranscodedResource:轉碼,將靜態、Gif動態圖進行格式轉換以便能加載
    Target :目標圖片
    
3、總體流程
 
A:Model圖片數據源 ———ModelLoader加載—>原始數據Data——Decoder解碼——>
    Resource——Transform裁剪——>TransformResource——Transcode轉碼——>TranscodeResource——封裝——>Target
 
4、源碼
    引入 :compile 'com.github.bumptech.glide:glide:3.7.0'
    
    4.一、使用流程三步曲: Glide
                        .with(「上下文context」)
                        .load(「url」)
                        .into(「顯示的控件資源");
 
    4.二、經常使用加載圖片的配置參數:
public void LoadImage(View view) {
            //with 建立一個加載圖片的Glide實例,流式接口
    Glide.with(getApplicationContext()) //指定的Context,傳入的參數會決定整個Glide的生命週期
        ps:圖片的加載會和傳入的Acitivty或是Fragment保持一致,因此建議使用Activity 或是Fragment做爲參數而不是單純的使用this 或是 context
    
            .load("url")//指定的圖片的URL
 
            加載佔位圖:-int or Drawable 
           .placeholder(R.mipmap.ic_launcher) //指定圖片未成功加載前現實的圖片佔位符
            // ,直到加載的網絡圖片顯示就會被替換,僅支持本地圖片資源
             錯誤佔位圖:-int or Drawable 
           .error(R.mipmap.ic_launcher)  //指定圖片加載失敗顯示的圖片佔位圖
            
            加載本地縮略圖:
           .thumbnail( float ) //0.2f Glide會顯示原始圖片的20%大小,注意ImageView的ScaleType值的設置
            加載網絡縮略圖:
           DrawableRequestBuilder<String> thumbnailRequest = Glide.with(context).load(url);            
            Glide.with( context) .load(url) 
                .thumbnail (thumbnailRequest ).into (imageView);
 
            加載圖片動畫效果:
                .crossFade() or crossfade(int duration)  //強制開啓GLide默認的圖片淡出淡入效果,默認持續時間300ms,設置dontAnimate()設置無任何淡出淡入效果
                
            顯示Gif 和Video 功能:
        String gifUrl = 「…xxxxoo.git」;
            .load (gifUrl) 
            .asGif() 
            .error(xxxx)
            // 若是圖片類型不是Gif的話 就會看成load 失敗來處理
            
            顯示靜態的Gif圖片 //僅僅顯示Gif的第一楨圖像
        String gifUrl = 「…xxxxoo.git」;
            .load (gifUrl) 
            .asBitmap() 
            .error(xxxx)
        
           顯示手機本地視頻文件:
            String filePath = 「/storage/emulated/0/xxxx.mp4"
            .load(Uri.fromFile ( new File (filePath) ))
            
           .override(300, 300)  //不自動適配圖片尺寸的時候進行手動進行圖片尺寸設置 ,glide能夠自動限制圖片的尺寸來優化圖片的加載
            //ps:Glide不會完整的加載原始圖片資源到內存當中,會自動判斷imageView大小再進行最優尺寸選擇加載
 
           .fitCenter()  //指定圖片縮放類型1,顯示的圖像寬和高都小於等於ImageView的邊界範圍
            //ps:缺陷 有可能不被填滿所須要的ImageView, FIT_CENTER
 
           .centerCrop()  //指定圖片縮放類型2。顯示的圖像填充整個ImageView,而後裁剪額外超出的部分
            //ps:缺陷,有可能完整填充ImageView可是圖片不能完整顯示,CENTER_CROP
 
           .skipMemoryCache(true)  //不將該圖片放在內存緩存中,可是仍然會利用該部分磁盤緩存,依然會在磁盤緩存中創建區域,Glide默認將圖片放入內存緩存中
            //ps: Glide默認會將圖片緩存到內存緩存中因此無需傳入false值,若同一Url地址在首次沒有調用該方法則第二次直接從內存中緩存緩存圖片,若想調整行爲須要保證每次調用行爲一致
 
            //硬盤緩存策略-枚舉值:默認開啓
           .diskCacheStrategy(DiskCacheStrategy.NONE) //禁用硬盤的緩存 讓其處於null狀態,跳過磁盤緩存
            .diskCacheStrategy(DiskCacheStrategy.SOURCE) //僅僅只緩存原來的全分辨率尺寸圖像
            .diskCacheStrategy(DiskCacheStrategy.RESULT) //僅僅只緩存資源最終的加載圖像(下降壓縮分辨率後的圖片)
            .diskCacheStrategy(DiskCacheStrategy.ALL)   //緩存全部版本的圖片
 
           .priority(Priority.HIGH)//優先級  先處理優先級高的圖片信息 ,但不保證全部圖片按照優先級順序進行加載
 
            .into(imageview); //顯示到指定的ImageView
}
 
   4.三、with()解析: 基礎目的:獲取RequestManager對象(管理Request請求)
 
    根據當前的上下文和組建選擇不一樣的with構造方法,不一樣的參數對於圖片加載的生命週期產生不一樣的影響,將圖片加載的生命週期和組件相掛鉤。
以context 爲例:
public static RequestManager with(Context context) {
    //用於產生requestManager 圖片請求管理 
   RequestManagerRetriever 生產requestManager
    RequestManagerRetrieverretriever = RequestManagerRetriever.get();
    return retriever.get(context);
}
//經過RequestManagerRetriever 這個生產Request類來處理,同時Glide綁定了組件的生命週期
 
RequestManager的建立流程:
public RequestManager get(Context context) {
    if (context == null) {
        throw new IllegalArgumentException("You cannot start a load on a null Context");
    } 
    //當前Context 是不是在主線程,context是不是application的實例
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());
        }
    }
    //返回一個單例模式的ApplicationManager
    return getApplicationManager(context);
}
    //雙重鎖檢查機制的單例模型
獲取惟一個RequestManager,進行圖片請求的處理
private RequestManager getApplicationManager(Context context) {
    if (applicationManager == null) {
        synchronized (this) {
            if (applicationManager == null) {
                applicationManager = new RequestManager(context.getApplicationContext(),
                        new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
            }
        }
    }
    return applicationManager;
}
   RequestManagerRetriever的 get()解析:
a、傳入application 類型的Context
當Glide傳入的是整個程序的生命週期的時候,就不須要進行過多處理
 
b、傳入非application 類型的Context (activity、fragment)
public RequestManager get(FragmentActivity activity) {
   //是不是在後臺線程,只有在非UI線程才進else
    if (Util.isOnBackgroundThread()) {
        return get(activity.getApplicationContext());
    } else {
        assertNotDestroyed(activity);
        FragmentManager fm = activity.getSupportFragmentManager();
        return supportFragmentGet(activity, fm);
    }
}
supportFragmentGet():
RequestManager supportFragmentGet(Context context, FragmentManager fm) {
   //Fragment 添加到Activity有兩種方式,A有Ui的Fragment B沒有Ui界面的Fragment
    //這裏使用第二種方式,經過RequestManagerFragment來監聽Activity的生命週期來完成綁定生命週期,圖片加載選擇的過程
        ps:Glide沒法直接監聽Activity的生命週期
 SupportRequestManagerFragment current 
                                            =     getSupportRequestManagerFragment(fm);
    //建立RequestManager實例,完成Glide對象的構造,經過RequestManager就能夠控制整個界面的生命週期的監聽,經過監聽進行圖片的相應操做
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
        requestManager = new RequestManager(context, 
                    current.getLifecycle(), current.getRequestManagerTreeNode());
        current.setRequestManager(requestManager);
    }
    return requestManager;
}
 
setRequestManager():將RequestManagerFragment這個空界面的Fragment和RequestManager進行綁定;
    目的:監聽Activity生命週期,管理圖片加載的整個流程 綁定到Activity一塊兒操做
public void setRequestManager(RequestManager requestManager) {
    this.requestManager = requestManager;
}
ps:一個RequestManager 對應 一個RequestManagerFragment,一一對應關係
 
    思考:前文提到的 SupportRequestManagerFragment 空的Fragment 是如何和RequestManager創建生命週期關係的呢?
 
    答:在RequestManagerFragment中含有ActivityFragmentLifecycle函數,用於管理組建生命週期的。RequestManager實際上在RequestManagerFragment中註冊了一些回調接口,經過接口監聽Activity 或是Fragment的生命週期
例如:
@Override
public void onStart() {
    super.onStart();
    lifecycle.onStart();
//說明RequestManagerFragment 這個無Ui的Fragment是經過LifeCycle進行生命週期的管理
}
根據不一樣的生命週期進行相應的處理
 
  4.四、load()解析: 初始化操做 獲取DrawableTypeRequest對象
 
    思考:爲何Glide會有一大堆重載方法?
    答:由於Glide支持多種格式的圖片來源,依託於此Glide加載也就須要不一樣的類型
 
public DrawableTypeRequest<String> load(String string) {
    return (DrawableTypeRequest<String>)
                    fromString().load(string);
}
DrawableTypeRequest() :表示Glide中全部加載圖片的Request請求
 
 
class DrawableTypeRequest<ModelType> extends     DrawableRequestBuilder<ModelType> implements DownloadOptions
   能想象到DrawableTypeRequest 應該就是經過Builder()內部類的構建者模式進行構建初始化的。
DrawableRequestBuilder:對Glide參數初始化的配置工做
而DrawableRequestBuilder又extends繼承自 GenericRequestBuilder
 
GenericRequestBuilder:Glide全部配置參數基本的最終類:
public class DrawableRequestBuilder<ModelType>
        extends GenericRequestBuilder<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable>
        implements BitmapOptions, DrawableOptions 
ps:DrawableRequestBuilder能夠經過鏈式調用配置參數
 
在GenericRequestBuilder中有一個很重要的成員變量:
protected final RequestTracker requestTracker; //負責跟蹤整個圖片請求的週期
 
fromString():傳入String的Class對象做爲參數
public DrawableTypeRequest<String> fromString() {
    return loadGeneric(String.class);
}
loadGeneric():
private <T> DrawableTypeRequest<T> 
                        loadGeneric(Class<T> modelClass) {
   //建立兩個ModelLoader對象,經過數據來源,ML未來源加載成原始數據
    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(...);
    }
    //建立DrawableTypeRequest 對象
    return optionsApplier.apply(
            new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
                    glide, requestTracker, lifecycle, optionsApplier));
}
 
DrawableTypeRequest():相對經常使用的方法,經過返回不一樣的TypeRequest來選擇須要的方法
//強制指定加載靜態圖片
public BitmapTypeRequest<ModelType> asBitmap() {
    return optionsApplier.apply(new BitmapTypeRequest<ModelType>
                            (this, streamModelLoader,
                    fileDescriptorModelLoader, optionsApplier));
}
//強制指定加載動態圖片
public GifTypeRequest<ModelType> asGif() {
    return optionsApplier.apply(new GifTypeRequest<ModelType>        (this, streamModelLoader, optionsApplier));
}
 
   4.五、into()解析:——在主線程中調用的更新UI操做
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.
        }
    }
    //三、建立一個Target對象,圖片所要顯示的控件
    return into(glide.buildImageViewTarget(view, transcodeClass));
}
 
applyCenterCrop():
————>centerCrop()————>centerCrop():
public DrawableRequestBuilder<ModelType> centerCrop() {
    return transform(glide.getDrawableCenterCrop());
}
transform()://圖片轉換,對transformation進行賦值操做
public GenericRequestBuilder<ModelType, DataType,
                    ResourceType, TranscodeType> transform(
        Transformation<ResourceType>... transformations) {
    isTransformationSet = true;
    if (transformations.length == 1) {
        transformation = transformations[0];
    } else {
        transformation = new MultiTransformation<ResourceType>(transformations);
    }
    return this;
}
glide.buildImageViewTarget() 中Target接口 :
 
//Glide能綁定生命週期的緣由:
interface Target<R> extends LifecycleListener 
    接口內含有onStart()、onStop()、onDestory()函數
 
在進行.with()操做的時候,會傳入context or Activity or Fragment進行相應生命週期的綁定操做 ,這時候就是經過LifecycleListener進行組件的監聽
 
<R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) {
    //經過imageViewTargetFactory工廠構建Target
    return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
}
//經過傳入class對象判斷Glide須要加載哪類圖片,buildTarget()就建立哪類Target
public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
    if (GlideDrawable.class.isAssignableFrom(clazz)) {
            //未調用asBitmap()方法就會默認採起GlideDrawableImageViewTarget()方法
        return (Target<Z>) new GlideDrawableImageViewTarget(view);
    } else if (Bitmap.class.equals(clazz)) {
            //調用了asBitmap()
        return (Target<Z>) new BitmapImageViewTarget(view);
    } else if (Drawable.class.isAssignableFrom(clazz)) {
            //使用較少
        return (Target<Z>) new DrawableImageViewTarget(view);
    } else {
        throw new IllegalArgumentException("Unhandled class: " + clazz
                + ", try .as*(Class).transcode(ResourceTranscoder)");
    }
}
 
into():
 
三、新建一個Request綁定到Target上
四、發送Request 交給RequestTracker進行相應的處理
public <Y extends Target<TranscodeType>> Y into(Y target) {
    //是否在主線程 (更新UI必須在主線程)
    Util.assertMainThread();
                …
        一、對舊的綁定的Target對象進行清除
    //獲取固然Target對象所綁定的舊的Request對象
    Request previous = target.getRequest();
    if (previous != null) {
        //清理操做
        previous.clear();
        //取消當前Request請求避免圖片錯位
        requestTracker.removeRequest(previous);
        //在該實現類中將成員變量賦值爲null 
            ps:並調用REQUEST_POOL.offer(this);當前一個Request不用的時候會被放入請求池以備複用
        previous.recycle();
    }
    二、建立一個加載圖片的Request
    ps:list圖片錯位的問題?
    解決:給View綁定setTag(),將view和圖片進行綁定
 
    Request request = buildRequest(target);
    //將圖片Request請求和ImageView綁定
    target.setRequest(request);
    lifecycle.addListener(target);
    //三、將Request發送給RequestTracker執行request請求
    requestTracker.runRequest(request);
    return target;
}
 
如何buildRequest?
buildRequest():————>buildRequestRecursive()
    ...
obtainRequest()————> GenericRequest.obtain():實際建立Request()
 
GenericRequest.obtain():
   //從線程池中獲取一個請求,若是有能夠複用的就複用一個請求,若是沒有就建立一個新的
    ...
GenericRequest<A, T, Z, R> request =
     (GenericRequest<A, T, Z, R>) REQUEST_POOL.poll();
    if (request == null) {
        request = new GenericRequest<A, T, Z, R>();
    }
        request.init();//初始化操做
        return request;
 
requestTracker.runRequest:
public void runRequest(Request request) {
    //將當前request加入到set<Request> 集合當中
    requests.add(request);
    //對當前狀態進行判斷,當前是有Request請求進入else ,將當前Request加入pending懸掛中的集合中
    if (!isPaused) {
        request.begin();
    } else {
        pendingRequests.add(request);
    }
}
 
request.begin():{
    …
    //判斷前面是否調用了overrideWidth 限定寬高的方法,直接執行onSizeReady()
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
    onSizeReady(overrideWidth, overrideHeight);
} else { 
   //沒有限定款高經過getSize()計算寬高
    target.getSize(this);
}
    //當前狀態是否已經完成,圖片是否不是失敗狀態進行判斷
    if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
    //設定圖片(獲取佔位圖)
    target.onLoadStarted(getPlaceholderDrawable());
    ...
}
 
target.getSize——>
public void getSize(SizeReadyCallback cb) {
    sizeDeterminer.getSize(cb);
}
getSize():內部根據ImageView寬高進行再次計算後再執行onSizeReady()
public void getSize(SizeReadyCallback cb) {
    //一、獲取寬高
    int currentWidth = getViewWidthOrParam();
    int currentHeight = getViewHeightOrParam();
    //三、當View繪製完時候就會進入onSizeReady()方法
if (isSizeValid(currentWidth) && isSizeValid(currentHeight)) {
    cb.onSizeReady(currentWidth, currentHeight);
    …
    //二、當前View沒有被測量完,會被添加到viewTreeObserver觀察者中
final ViewTreeObserver observer = view.getViewTreeObserver();
layoutListener = new SizeDeterminerLayoutListener(this);
observer.addOnPreDrawListener(layoutListener);    
    
 
    一、經過LoadProvider()方法獲取ModelLoader和Transcoder
    二、根據ModelLoader獲取DataFetcher
    三、engine.load 進行實際圖片加載
 
ModelLoader:從數據源中獲取原始數據,通常是輸入流inputStream,在Glide中被封裝成Data
DataFetcher:將Data原始數據轉換成能直接用的不一樣形式的圖片數據類型
ResourceTranscoder:將原始數據解碼,將io輸入流解碼成bitmap的解碼工具對象
Resource:解碼後的資源
 
onSizeReady (int width, int height) {
    …
ModelLoader<A, T> modelLoader 
                =     loadProvider.getModelLoader();
//經過loadProvider接口獲取到dataFetcher、loadProvider、transcoder
final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
            
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
        priority, isMemoryCacheable, diskCacheStrategy, this);
}
LoadProvider():GenericRequest的成員變量
public interface LoadProvider<A, T, Z, R> extends DataLoadProvider<T, Z> {
    ModelLoader<A, T> getModelLoader();
    ResourceTranscoder<Z, R> getTranscoder();
 
LoadProvider初始化:在GenericRequest的實現類
DrawableTypeRequest(...) {
    super(context, modelClass,
            buildProvider(glide, streamModelLoader, fileDescriptorModelLoader,)
    ...
}
 
buildProvider的返回值就是LoadProvider,FixedLoadProvider是LoadProvider的實現類:獲取原始圖片,進行編解碼、轉碼等功能
private static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide,
        ModelLoader<A, InputStream> streamModelLoader,
        ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader...) {
   //判斷來自不一樣數據的Loader
    if (streamModelLoader == null && fileDescriptorModelLoader == null) {
        return null;
    }
    //ResourceTranscoder是否爲空,爲null則經過Glide建立新的Transcoder
ps:對原始數據進行解碼
    if (transcoder == null) {
        transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
    }
    //DataLoadProvider對圖片進行編解碼的接口,實現類中LoadProvider
    DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider 
                = glide.buildDataProvider(ImageVideoWrapper.class,resourceClass);
    //封裝了兩個ModelLoader
    ImageVideoModelLoader<A> modelLoader = new 
        ImageVideoModelLoader<A>(streamModelLoader,fileDescriptorModelLoader);
    
        return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>
                    (modelLoader, transcoder, dataLoadProvider);
}
 
DataLoadProvider:負責編解碼
Data:從數據源獲取的數據
Resource:解碼後的資源
解碼:Data————>Resource 資源
編碼:將Data、Resource———>持久化到本地的過程 用於實現Glide的磁盤緩存功能
 
engine.load():
engine:負責圖片加載,管理正在使用和處於緩存的圖片類
 
LoadStatus load(){
    …
//加載圖片的惟一標識id,好比加載網絡圖片的話就是一個網絡圖片的url地址
final String id = fetcher.getId();
//傳入惟一標識id ,其餘不少參數依然能夠決定EngineKey的值,構建Glide緩存中的一個key值
EngineKey key = keyFactory.buildKey(id,…);    
    ...
 //從緩存中獲取圖片
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
    cb.onResourceReady(cached);
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Loaded resource from cache", startTime, key);
    }
    //若緩存圖片是空的則調用loadFromActiveResources
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    …
    //若二者都沒有獲取到就本身開啓一個runnable 從磁盤或是網絡進行圖片的獲取
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
    jobs.put(key, engineJob);
    engineJob.addCallback(cb);
    engineJob.start(runnable);
}
 
GLide中的內存緩存策略
 
GLide 構建內存緩存的組成部分:cache、active
 
A、內存緩存讀取操做原理
   一、 LruCache算法:近期最少使用算法,將最近使用的對象的強引用存儲在LinkHashMap上;而且把最近最少使用的對象,在緩存池達到預設值以前從內存中移除
   二、弱引用緩存機制
    
Glide中的內存緩存對象:Engine類中的 Load()函數
    MemoryCache——loadFromCache(EngineKey,boolean isMemoryCacheable)
        cached :最近使用過的而當前不在使用的EngineResource,LoadFromCache內部使用linkHashMap當內存達到閥值會經過LruCache算法進行清除操做
 
loadFromCache():一、從內存緩存MemoryCache中獲取圖片
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
    //配置Glide 的時候使用的isMemoryCacheable,跳過內存緩存操做skipMemoryCache(true)
    默認狀況下爲true 的時候開啓緩存模式
    if (!isMemoryCacheable) {
        return null;
    }
    //獲取實際緩存對象
    EngineResource<?> cached = getEngineResourceFromCache(key);
       //ps:二、在該方法中會使用緩存的key值 從cache中獲取值並調用remove函數把這個圖片從MemoryCahce緩存中移除掉
            //Resource cached = this.cache.remove(key);
    if (cached != null) {
        cached.acquire();
        //寫入一個弱引用的 緩存
        activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
    }
    return cached;
}
 
ps:使用弱引用的好處,使得該部分緩存不會被Lru算法給回收掉資源
 
loadFromActiveResources():三、調用該方法從正在使用的部分中繼續獲取圖片
    ActiveResource——loadFromActiveResources(EngineKey,boolean isMemoryCacheable)——HashMap<key,WeakReference<EngineResource>>
       active:保存當前正在使用的EngineResource對象
      ps:  會以EngineKey爲鍵,以EngineResource的弱引用爲值 
 
HashMap的Get()、 Put()函數對應着 緩存的讀和取 的操做
 
//四、若前兩步都沒有獲取到圖片資源,就建立一個Engine runnbale對象加載,從磁盤或是網絡獲取圖片
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);
EngineRunnablerunnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
 
B、內存緩存寫入操做原理
   
if (cached != null) {
    cb.onResourceReady(cached);
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Loaded resource from cache", startTime, key);
    }
    return null;
}
cb:callback回調
onResourceReady :ResourceCallback的具體實現
在onResourceReady()中:定義一個handler發送message,將執行邏輯切回到主線程中操做
public void onResourceReady(final Resource<?> resource) {
    this.resource = resource;
    MAIN_THREAD_HANDLER.obtainMessage(
                                                MSG_COMPLETE, this).sendToTarget();
}
其中MAIN_THREAD_HANDLER是MainThreadCallback回調定義:
private static final Handler MAIN_THREAD_HANDLER = 
                    new Handler(Looper.getMainLooper(),
                                    new MainThreadCallback());
MainThreadCallback():接受Message信息並處理的callback回調,經過名字能夠知道該消息是在主線程進行結果回調
private static class MainThreadCallbackimplements Handler.Callback {
 
    @Override
    public boolean handleMessage(Message message) {
        if (MSG_COMPLETE == message.what || MSG_EXCEPTION == message.what) {
            EngineJob job = (EngineJob) message.obj;
            if (MSG_COMPLETE == message.what) {
                job.handleResultOnMainThread();
            } else {
                job.handleExceptionOnMainThread();
            }
              ...
}
job.handleResultOnMainThread():
private void handleResultOnMainThread() {
    //一、若是任務取消則回收資源 recycle
    if (isCancelled) {
        resource.recycle();
        return;
    //二、若callback集合爲null 空 則拋出異常
    } else if (cbs.isEmpty()) {
        throw new IllegalStateException("Received a resource without any callbacks to notify");
    }
    //三、經過工廠累建立包含圖片資源的engineResource對象
    engineResource = engineResourceFactory.build(resource, isCacheable);
     ...
   //三、將對象回調到onEngineJobComplete()當中
engineResource.acquire();
    //四、acquire():記錄圖片被引用的次數,同時在該函數結尾經過 ++this.acquire 來進行圖片次數的+1 操做;當acquired >0 表示有圖片正在使用中,也就是應該把圖片放入到activeResource 這個弱引用緩存中
    listener.onEngineJobComplete(key, engineResource);
for (ResourceCallback cb : cbs) {
    if (!isInIgnoredCallbacks(cb)) {
        engineResource.acquire();
        cb.onResourceReady(engineResource);
    }
}
    //五、release() 當圖片等於0的時候表示圖片沒有在使用了就調用onResourceReleased釋放資源操做,該實如今Engine()函數中
    engineResource.release();
}
 
onEngineJobComplete():
public void onEngineJobComplete(Key key, EngineResource<?> resource) {
    Util.assertMainThread();
    if (resource != null) {
        resource.setResourceListener(key, this);
        if (resource.isCacheable()) {
           //最終調用activeResources.put()這個函數進行了緩存的寫入操做
    ps:這裏寫入的是弱引用的緩存
            activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue()));
        }
    }
    // TODO: should this check that the engine job is still current?
    jobs.remove(key);
}
 
onResourceReleased():
public void onResourceReleased(Key cacheKey, EngineResource resource) {
    Util.assertMainThread();
    //一、把整個緩存圖片從activeResources從緩存圖片中刪除
    activeResources.remove(cacheKey);
    if (resource.isCacheable()) {
    //二、經過put()加入到LruCache算法中,
    ps:實現正在使用的圖片經過弱引用進行緩存,而不在使用的利用LruCache進行緩存
       cache.put(cacheKey, resource);
    } else {
        resourceRecycler.recycle(resource);
    }
}
 
磁盤緩存讀寫 待補充
 
5、Glide經常使用
     
  5.1 、Target函數的使用- Glide獲取 Bitmap資源自己
        Target其實就是整個圖片的加載的生命週期,經過它在圖片加載完成以後獲取Bitmap
    以 SimpleTarget 爲例:
       private SimpleTarget<Bitmap> mSimpleTarget= new SimleTarget<Bitmap> (可添加寬*高 尺寸限定){
            @Override
            public void onResourceReady( Bitmap resource, 
                                                        GlideAnimation< ? super Bitmap>animation) {
                ImageView.setImageBitmap(resource);
            }
    };    
    private void loadImageSimpleTarget(){
        Glide .with (1 ). load(url ) . asBitmap() . into ( mSimpleTarget );
    }
 
在這裏若是SimpletTarget 使用匿名內部類的方式建立 SimpleTarget 的對象,這樣會增大該對象在 Glide 完成圖片請求以前就被回收的可能性。
(1):這裏若是傳入的是Target ,可能獨立於with的生命週期以外,因此最好給with裏傳入參數context .getApplicationContext(),讓Glide持有整個app的生命週期。
 
    5.二、ViewTarget函數的使用
        在自定義View的時候,Glide有時候並不支持加載圖片到自定義View中的時候,這時候就須要ViewTarget
public void loadImageTarget(Context context){ 
        CustomView mCustomView = (CustomView) findViewById(R.id.custom_view);             ViewTarget viewTarget = 
        new ViewTarget<CustomView,GlideDrawable>( mCustomView) { 
                @Override 
                public void onResourceReady(GlideDrawable resource,
                                         GlideAnimation<? super GlideDrawable> glideAnimation) {                             this.view.setImage(resource); 
    } 
}; 
    Glide.with(context) .load(mUrl) .into(viewTarget); 
}
onResourceReady回調方法中使用了自定義view本身的方法設置圖片,能夠看到在建立ViewTarget的時候傳入了CustomView 對象
 
    5.三、Transformations函數的使用
         若是須要對圖片進行圖片切圓角、灰階處理 等功能 就須要 經過 Transformations來操做bitmap 。經過修改尺寸、範圍、顏色、像素、位置等達到需求。
    ps:如果對圖片進行常規的bitmap轉換的話,可使用抽象類 BitmapTransformation 進行
   對圖片切圓角操做 :getId()是圖片的惟一標識符必須惟一
    public class RoundTransformation extends BitmapTransformation {
         private float radius = 0f; 
        
    public RoundTransformation(Context context) { 
            this(context, 4); 
        } 
    
    public RoundTransformation(Context context, int px) {
         super(context); 
        this.radius = px; 
    } 
 
       @Override 
      protected Bitmap transform(BitmapPool pool, Bitmap toTransform,
                                                                                 int outWidth, int outHeight) { 
            return roundCrop(pool, toTransform); 
    } 
    
    private Bitmap roundCrop(BitmapPool pool, Bitmap source) { 
        if (source == null) 
            return null; 
            Bitmap result = pool.get(source.getWidth(),
                                 source.getHeight(), Bitmap.Config.ARGB_8888); 
 
        if (result == null) { 
            result = Bitmap.createBitmap(source.getWidth(), 
                                  source.getHeight(), Bitmap.Config.ARGB_8888); 
        } 
 
            Canvas canvas = new Canvas(result);    
            Paint paint = new Paint(); 
            paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP,
                                                            BitmapShader.TileMode.CLAMP));                
             paint.setAntiAlias(true); 
             RectF rectF = new RectF(0f, 0f, source.getWidth(), 
                                                                            source.getHeight());                                                     canvas.drawRoundRect(rectF, radius,radius, paint); 
            return result;
     } 
        @Override 
        public String getId() { 
            return getClass().getName() + Math.round(radius); 
        } 
    }
下面是transform的調用:
    Glide.with(context) 
            .load(mUrl) 
                .transform(new RoundTransformation(context , 20)) 
                    //.bitmapTransform( new RoundTransformation(context , 20) )         .into(mImageView);
 
ps:若須要同時執行多個transformation的話 ,不能再像這樣經過鏈式調用的形式屢次調用.transform() 或是.bitmapTransform() 方法。上面的transform會被最後一個覆蓋掉。
這時候能夠這樣作:
    Glide.with(context) 
        .load(mUrl) 
            .transform(new RoundTransformation(context , 20) 
                , new RotateTransformation(context , 90f)) 
        .into(mImageView);
 
  對圖片旋轉操做 
public class RotateTransformationextends BitmapTransformation { 
        private float rotateRotationAngle = 0f; 
        public RotateTransformation(Context context, float rotateRotationAngle) {
             super( context );
         this.rotateRotationAngle = rotateRotationAngle;
     } 
        @Override 
        protected Bitmap transform(BitmapPool pool, 
                Bitmap toTransform, int outWidth, int outHeight) {
             Matrix matrix = new Matrix(); 
             matrix.postRotate(rotateRotationAngle); 
                return Bitmap.createBitmap(toTransform, 0, 0, 
                            toTransform.getWidth(), toTransform.getHeight(), matrix, true); 
    } 
        @Override 
       public String getId() {
             return getClass().getName() + Math.round(rotateRotationAngle); 
        }
 }
ps:這裏須要注意一點 .centerCrop() 和 .fitCenter() 也都是 Transformation 因此也是遵循同時使用多個 Transformation 的規則的,即:當你使用了自定義轉換後你就不能使用 .centerCrop() 或 .fitCenter() 了。
這裏有一個 GLide Transformations 的庫,它提供了不少 Transformation 的實現,很是值得去看,沒必要重複造輪子對吧!
 
5.四、Animate動畫函數的使用
     圖片間切換的平滑過渡 是很是重要的!!因此Glide又一個標準動畫去柔化Ui中的改變,可是若是是設置本身的動畫的話:
一個小例子:
<set 
    xmlns:android=" http://schemas.android.com/apk/res/android"         android:fillAfter="true」> 
    <scale 
        android:duration="@android:integer/
                                                config_longAnimTime」     
        android:fromXScale="0.1」 
        android:fromYScale="0.1」 
        android:pivotX="50%」
        android:pivotY="50%」
        android:toXScale="1」 
        android:toYScale="1"/> 
    </set>
    XML 動畫縮放動畫,圖片剛開始小的,而後逐漸增大到原尺寸。咱們如今要應用到 Glide 加載圖片中去,調用 .animate() 方法傳入 XML 動畫的 id 便可。
    Glide.with(context)     
        .load(mUrl)
         .transform(new RoundTransformation(this , 20))
         .animate( R.anim.zoom_in )
         .into(mImageView);
 
animate在Target中的使用:
    ViewPropertyAnimation.Animatoranimator 
            = new ViewPropertyAnimation.Animator() { 
        @Override 
        public void animate(View view) { 
            view.setAlpha( 0f ); 
            ObjectAnimator fadeAnim 
                    = ObjectAnimator.ofFloat( view, "alpha", 0f, 1f );
                 fadeAnim.setDuration( 2500 ); 
                fadeAnim.start();
         } 
    };
Glide.with(context)
    .load(mUrl)
    .animate( animator )
    .into(viewTarget);
 
  5.五、Modules函數的使用
    Glide 的Module 是一個能夠全局改變Glide 的行爲的東西。爲了實現需求須要去實現 interface GlideModule 來實現功能
   public class ExampleModule implements GlideModule{
         @Override 
        public void applyOptions(Context context, GlideBuilder builder) { 
        // todo
     } 
        @Override 
        public void registerComponents(Context context, Glide glide) { 
        // todo
     } }
主要使用的是 applyOptions(Context context, GlideBuilder builder) , 咱們本身的須要從新定義的代碼寫在該方法裏就能夠了。而後咱們還須要去 AndroidManifest.xml 中使用 meta 聲明咱們上面實現的 Module:
        <application>
             <meta-data android:name
                                ="com.mrtrying.demoglide.module.ExampleModule"                         android:value="GlideModule" /> 
                ... </application>
到這裏咱們就完成了 ExampleModule 的聲明,Glide 將會在工做是使用咱們所定義的 Module
 
TIPS
  • 咱們須要將 android:name 屬性改爲 包名+類名 的形式,這樣的引用纔是正確的。若是你想刪掉 Glide Module,只須要刪除在 AndroidManifest.xml 中的聲明就能夠了。Java 類能夠保存,說不定之後會用呢。若是它沒有在 AndroidManifest.xml 中被引用,那它不會被加載或被使用。
  • 定製 module 的話 Glide 會有這樣一個優勢:你能夠同時聲明多個 Glide module。Glide 將會(沒有特定順序)獲得全部的聲明 module。由於你當前不能定義順序,請確保定製不會引發衝突!
applyOptions(Context context, GlideBuilder builder) 中有兩個參數, 咱們經過使用 GlideBuilder 來實現咱們的需求。先看看 GlideBuilder 中可用的方法
  • .setMemoryCache(MemoryCache memoryCache)
  • .setBitmapPool(BitmapPool bitmapPool)
  • .setDiskCache(DiskCache.Factory diskCacheFactory)
  • .setDiskCacheService(ExecutorService service)
  • .setResizeService(ExecutorService service)
  • .setDecodeFormat(DecodeFormat decodeFormat)
能夠看到,這個 GlideBuilder 對象給你訪問了 Glide 重要的核心組件。接下來咱們就要試着去使用這些方法
增長 Glide 的圖片質量
在 Android 中有兩個主要的方法對圖片進行解碼:ARGB_8888 和 RGB_565 。前者爲每一個像素使用4個字節,後者每一個像素僅使用2個字節。ARGB_8888 的有時就是圖像質量更高以及能儲存一個 alpha 通道。 Picasso 使用的就是 ARGB_8888 , Glide 默認使用低質量的 RGB_565 ,可是如今你就可使用 Glide module 來改變圖片解碼規則。就象這樣
public class QualityModule implements GlideModule{
    @Override
    public void applyOptions(Context context , GlideBuilder builder){
        builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
    }
 
    @Override
    public void registerComponents(Context context , Glide glide){
        // nothing to do here
    }
}
這樣咱們就簡單的增長了 Glide 的圖片質量。
每每咱們還會遇到一些狀況,但願 Glide 可使用咱們本身的網絡框架,咱們就須要作一些事情來實現這個需求了。Glide 的開發者不強制設置網絡庫給你,因此Glide能夠說和 HTTPS 無關。理論上,它能夠與任何的網絡庫實現,只要覆蓋了基本的網絡能力就行。一樣是須要實現 Glide 的 ModuleLoader 的接口,爲了讓咱們更加易用,Glide 爲 OkHttp 和 Volley 兩個網絡庫提供了實現。
假設我要集成 OkHttp 做爲 Glide 的網絡庫,我能夠手動實現一個 GlideModule 也能夠在 build.gradle 中添加依賴:
dependencies{
    //...
    
    // Glide
    compile 'com.github.bumptech.glide:glide:3.7.0'
 
    // Glide's OkHttp Integration
    compile 'com.github.bumptech.glide:okhttp-integration:1.4.0@aar'
    compile 'com.squareup.okhttp:okhttp:3.2.0'
}
Gradle 會自動合併必要的 GlideModule 到你的 AndroidManifest.xml , Glide 會承認在 manifest 中存在,而後使用 OkHttp 作到的全部網絡鏈接!!
 
做者:MrTrying
來源:簡書
以上一小節選取了 該文章的內容多謝分享!
 
6、Gilde相關知識補充-bitmap &oom & 優化bitmap
 
    4.6.1 bitmap 致使OOM
a、在使用list類View
    1、加載大量View組件又沒有合理的處理緩存的狀況下,大量加載bitmap很容易形成OOM。
    解決方法:
    (1)三級緩存
    (2)設置listView的監聽事件,當滑動的時候不進行圖片的加載,當中止滑動的時候再加載
 
   2、圖片分辨率愈來愈高,消耗的內存愈來愈大 
    注意:圖片Bitmap所佔用的內存 = 圖片長度 * 圖片寬度 * 一個像素點佔用的字節數
 
    3、VM值上限dalvik.vm.heapgrowthlimit 
    限定了每一個app 可用的最大內存,若是超過這個值就會出現OOM現象
 
    4.6.2 bitmap的4種優化策略
        1、對圖片質量進行壓縮
private Bitmap compressImage(Bitmap image) {
    //一、建立字節輸出流
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    //二、Bitmap.CompressFormat進行壓縮(類型,值越小表明壓縮比例越大,100表示不壓縮,壓縮好的圖片放在字節輸出流)
    image.compress(Bitmap.CompressFormat.JPEG
            , 100, baos);
    int options = 100;
    //三、循環判斷,壓縮好的圖片大小是否大於100kb,大於就進一步壓縮
    while (baos.toByteArray().length / 1024 > 100) {
            //3.一、清空輸出流
        baos.reset();
            //3.二、再次壓縮
        image.compress(Bitmap.CompressFormat.JPEG, options, baos);
            //3.三、每次減小10options 數值
        options -= 10;
    }
        //四、將壓縮好的數據放入到字節輸入流中
    ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
        //五、經過BitmapFactory.decodeStream完成bitmap的重建
    Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);
    return bitmap;
}
 
        2、對圖片按比例進行壓縮
/**
* 圖片比例壓縮
*/
private Bitmap comBitmapInSize(Bitmap image) {
    //一、建立字節數組輸入輸出流
           ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ByteArrayInputStream isBm;
    //二、建立BitmapFactory的Options內部類對象,經過該對象能夠對bimtap進行寬高、內存等的控制
            BitmapFactory.Options newOpts = new BitmapFactory.Options();
    //三、解碼bitmap只返回它的寬高數值,而沒必要爲他申請內存,經過設爲true能夠節省內存空間
            newOpts.inJustDecodeBounds = true;
            Bitmap bitmap;
            int w = newOpts.outWidth;
            int h = newOpts.outHeight;
            float hh = 1290f; //設置高度爲1280f
            float ww = 720f; //設置寬度爲720f
    //四、建立be縮放比例
            int be = 1;
            if (w > h && w > ww) {
        //4.一、若是寬大於高的話,就根據寬的大小進行縮放
                be = (int) (newOpts.outWidth / ww);
             } else if (w < h && h > hh) {
        //4.二、若是寬小於高的話,就根據高的大小進行縮放
                be = (int) (newOpts.outHeight / hh);
            }
                if (be <= 0)
                    be = 1;
    //五、將計算好的比例數字be 賦值給Options中的縮放比例變量inSamplesize
                newOpts.inSampleSize = be;
   //六、將Options的inJustDecodeBounds設置爲false表示須要將圖片加載到內存中
                newOpts.inJustDecodeBounds = false;
                isBm = new ByteArrayInputStream(baos.toByteArray());
    //七、重建bitmap
                bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);
                return bitmap;
}
        3、關於bitmap的recycle方法(含有爭議話題)
/**
* Free the native object associated with this bitmap, and clear the
* reference to the pixel data. This will not free the pixel data synchronously;
* it simply allows it to be garbage collected if there are no other references.
* The bitmap is marked as "dead", meaning it will throw an exception if
* getPixels() or setPixels() is called, and will draw nothing. This operation
* cannot be reversed, so it should only be called if you are sure there are no
* further uses for the bitmap. This is an advanced call, and normally need
* not be called, since the normal GC process will free up this memory when
* there are no more references to this bitmap.
*/
    /**
        * 當釋放這個bitmap相關聯的native對象的時候,它會清除像素數據。ps:不會同步的釋放像素數據,只是根據在沒有其餘引用的狀況下收集 該GC垃圾,同時將狀態標記爲dead狀態。這時候意味着調用 setPixels() 或是getPixels()時候都會拋出異常。而不回去繪製任務東西。一般狀況下不須要手動的調用,當其沒有引用這個bitmap的時候,正常的垃圾回收進程會釋放掉該部份內存。
        *
        */
    public void recycle() {
        ...
    }
註解: 手動的調用recycle()自己可能並無被釋放資源。因此硬要調用recycle的時候要將bitmap設置爲null,好讓GC和垃圾回收器回收這部分的對象。
 
        4、捕獲異常
針對OOM 的區域進行 異常捕獲 
public void handleBitmapCrash() {
    Bitmap bitmap = null;
    String path = "xx/xx/xx";
    try {
        //實例化bitmap
        bitmap = BitmapFactory.decodeFile(path);
    } catch (OutOfMemoryError e) {
        e.printStackTrace();
    }
}
 
7、Gilde相關知識補充-三級緩存 /LruChache算法
   Android的緩存策略-三級緩存
        內存 - 本地 - 網絡  
意義:
       第一次從網絡獲取 ,以後將獲取的圖片保存到本地和內存各一份,當程序再次用到該圖片的時候首先會從內存中判斷是否有緩存,有的話直接從內存中獲取,沒有的話再從本地SD卡中進行獲取。若是內存和本地均沒有的話,再次從網絡上獲取。
 
思考:內存緩存是如何實現的呢?
    緩存主要是經過添加 、獲取、刪除 進行操做的。
    不論內存緩存仍是硬盤緩存,緩存的大小都是固定的,有個max限制。當該部分緩存用滿以後其餘緩存就沒法再添加,因此須要進行刪除無用緩存的操做。這時候就須要一個刪除舊緩存的算法,也就說常說的LRU(Least Recently Used)近期最少使用緩存算法
 
LruChache算法
LRU核心
    當緩存已滿的時候會優先淘汰掉最近使用最少的緩存對象
   整個Lru算法的核心是LinkedHashMap,其繼承自HashMap,其構造函數中會有一個boolean類型的值,尤爲控制插入的順序,a是Lru順序,b是正常順序
 
public class LruCache<T, Y> {
    private final LinkedHashMap<T, Y> cache= new LinkedHashMap<T, Y>(100, 0.75f(負載因子), true(當爲true時候說明整個順序是經過Lru算法順序刪除元素的));
     
        int size : 當前緩存的大小
        int maxSize : 當前緩存的最大值
        int putCount : put方法調用的次數
        int createCount : create方法調用的次數
        int evictionCount : 當須要淘汰一些緩存變量時候的計數器
        int hitCount : 緩存對象使用到hitCount +1 計數
        int missCount : 緩存未命中 計數 +1
        
Get():
     傳入key值 獲取相應的item,如果無緩存會建立並返回相應的item,相應的item會被移動到隊列的尾部。
    public final V  get(K key){
        if(key == null ) {
            throw new NullPointerException(「key==null"):
        }
    V mapValue;
        //利用LinkedHashMap的get方法獲取相應的value
        synchronized(this){
        mapValue = map.get(key);
        if(mapValue !=null){
            //獲取到緩存 hitCount+1 並返回Value值
            hitCount++;
            return mapValue;
        }
        missCount++ ;
    }
   //未命中,調用create()建立一個對象
    V createdValue = create(key);
     if(createdValue == null) {
        return null;    
    }
    synchronized (this ){
            createCount++;   
            //覆蓋原有Value值並添加到mapValue中
            mapValue = map.put(key , createdValue);
            if(mapValue !=null){
               //若是該值不爲空,將從新建立好的cratedValue又覆蓋成原來的mapValue,逆推上一步操做
                map.put(key,mapValue);  //插入的新的對象會存儲到列表的尾端
            }else{
                //加入新建立的對象須要從新建立緩存size大小
            size += safeSizeOf(key ,createValue);
    }
        if(mapValue != null){
            entryRemoved(false, key,createdValue,mapValue);
            return mapValue;
        }else {
            //新加入對象都會調用trimToSize(),來查看對象是否須要被回收。根據傳入的緩存的最大容量調整緩存的大小。傳入 -1 表示清空全部緩存對象
            trimToSize(maxSize);
            return createdValue;
        }
}
Put():
    public final V put (K key , V value){
        if(key == null || value == null) {
                throw new NullPointerException (「key ==null ||value ==null ")
        }
        V previos ;
        synchronized (this ){
            //調用put方法每次計數+1
            putCount ++;
            // 每次put都會形成整個緩存大小增大,因此須要自增當前緩存
            size += safeSizeOf (key ,value );
            previous= map .put(key , value );
           //若是以前存在key爲鍵值的對象,這時候當前的緩存size 須要減掉這個對象的大小
                if( previous !=null){
                    size -= safeSizeOf (key ,previous);
                    }
            }
            if(previous !=null ){
                entryRemoved (false ,key ,previous ,value);
            }
           //調整內存緩存大小,插入的新對象會存儲在列表的尾端
            trimToSize (maxSize);
            return previous;
    }
  • LruCache中將LinkedHashMap的順序設置爲LRU順序來實現LRU緩存
  • 每次調用get則將該對象移到鏈表的尾端
  • 調用put插入新的對象也是存儲在鏈表尾端
  • 當內存緩存達到設定的最大值時候,將鏈表頭部的對象(近期最少使用到的)移除
相關文章
相關標籤/搜索