Android: Glide筆記

//該死的拖延癥瞒御,總是要學習做筆記想虎,紙上得來終覺淺。

一购城、簡介应结、使用刨疼。

1.1簡介

略,見 主頁

1.2基本使用

Glide.with(imageView.getContext())
            .load(url)
            .placeholder(defaultImage)
            .error(failImage) .diskCacheStrategy(DiskCacheStrategy.ALL)
            .into(imageView);

其中 load()重載方法摊趾。load也支持多參數(shù)币狠,這里我們可以傳入的有Uri游两、File砾层、byte[]、Object贱案、Bitmap肛炮、String、Drawable宝踪、Integer侨糟、URl,

DiskCacheStrategy是緩存策略瘩燥,有以下幾個參數(shù)秕重,并詳細說明該策略的保存信息。

   1)  ALL 網絡資源執(zhí)行DATA厉膀、RESOURCE溶耘,本地資源執(zhí)行RESOURCE
    2) NONE  不緩存任何內容
    3) DATA  緩存原始圖片
    4) RESOURCE  緩存解碼后的圖片
    5) AUTOMATIC  根據(jù)圖片資源智能地選擇使用哪一種緩存策略(默認選項)

1.3 其他用法

1.3.1
listener 這里我們可以對加載圖片進行處理二拐,加載成功和加載失敗兩個方法
1.3.2
addListener 和listener不同的是,listener是清空了監(jiān)聽凳兵,把當前監(jiān)聽加入進去百新,addListener是不清空監(jiān)聽,既不影響其他地方調用的監(jiān)聽庐扫,僅僅是把當前的監(jiān)聽加入進去饭望,建議使用addListener,其他和listener相同
1.3.3
.centerInside() .fitCenter() .centerCrop() 這三種圖片處理方式形庭,centerInside等比例縮小顯示铅辞,在橫向或豎向上相等,不會進行放大碘勉。fitCenter等比例放大或縮小巷挥。centerCrop等比例放大。
1.3.4
override(int w,int h)這個方法會將圖片以制定的高寬顯示在ImageView上
1.3.5
apply(RequestOptions.bitmapTransform(new RoundedCorners(dp2px(60)))) 這個方法可以設置為圓形圖了验靡。
.optionalFitCenter() 同樣可以圓形

二倍宾、原理分析

2.1原理

緩存:
先去LruCache中尋找圖片,如果LruCache中有胜嗓,則直接取出來使用高职,如果LruCache中沒有,則去WeakReference中尋找辞州,如果WeakReference中有怔锌,則從WeakReference中取出圖片使用,同時將圖片重新放回到LruCache中变过,如果WeakReference中也沒有圖片埃元,則去文件系統(tǒng)中尋找,如果有則取出來使用媚狰,同時將圖片添加到LruCache中岛杀,如果沒有,則連接網絡從網上下載圖片崭孤。圖片下載完成后类嗤,將圖片保存到文件系統(tǒng)中,然后放到LruCache中辨宠。
Glide的緩存只有兩個模塊遗锣,一個是內存緩存,一個是磁盤緩存嗤形。其中內存緩存又分為Lru算法的緩存和弱引用緩存精偿。

LruCache算法,Least Recently Used,又稱為近期最少使用算法笔咽。主要算法原理就是把最近所使用的對象的強引用存儲在LinkedHashMap上墓阀,并且,把最近最少使用的對象在緩存池達到預設值之前從內存中移除拓轻。
讀取的順序是:Lru算法緩存斯撮、弱引用緩存、磁盤緩存

寫入的順序是:弱引用緩存扶叉、Lru算法緩存勿锅、磁盤緩存(不準確)

2.2 代碼順序

2.2.1 with()

with()方法重載的代碼都非常簡單,都是先調用RequestManagerRetriever的靜態(tài)get()方法得到一個RequestManagerRetriever對象枣氧,這個靜態(tài)get()方法就是一個單例實現(xiàn)溢十。然后再調用RequestManagerRetriever的實例get()方法,去獲取RequestManager對象达吞。

RequestManagerRetriever類中看似有很多個get()方法的重載张弛,實際上只有兩種情況而已,即傳入Application類型的參數(shù)酪劫,和傳入非Application類型的參數(shù)吞鸭。
a.傳入Application參數(shù)
如果在Glide.with()方法中傳入的是一個Application對象,那么這里就會調用帶有Context參數(shù)的get()方法重載覆糟,調用getApplicationManager()方法來獲取一個RequestManager對象刻剥。其實這是最簡單的一種情況,因為Application對象的生命周期即應用程序的生命周期滩字,因此它自動就是和應用程序的生命周期是同步的造虏,如果應用程序關閉的話,Glide的加載也會同時終止麦箍。

b.非Application參數(shù)的情況漓藕。
最終的流程都是一樣的,那就是會向當前的Activity當中添加一個隱藏的Fragment挟裂。因為Glide需要知道加載的生命周期享钞。Fragment的生命周期和Activity是同步的,如果Activity被銷毀了话瞧,F(xiàn)ragment是可以監(jiān)聽到的嫩与,這樣Glide就可以捕獲這個事件并停止圖片加載了寝姿。

2.2.2 load()方法交排。

根據(jù)傳入的參數(shù)類型作為泛型,創(chuàng)建一個圖片請求對象DrawableTypeRequest.
DrawableTypeRequest的父類是DrawableRequestBuilder饵筑,DrawableRequestBuilder中有很多個方法埃篓,這些方法其實就是Glide絕大多數(shù)的API比如說placeholder()方法、error()方法根资、diskCacheStrategy()方法架专、override()方法等同窘。

      RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode, RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
    this.context = context.getApplicationContext();
    this.lifecycle = lifecycle;//與頁面生命周期綁定的lifecycle對象
    /*treeNode:
     *提供了可以訪問當前上下文中所有的RequestManager,也就是說基于Context層次結構建立了RequestManager的層次結構部脚,
     *而上下文的層次結構是在Activity / Fragment中嵌套的想邦。
     *不過,注意委刘,如果當前上下文是Application Context 丧没,只能訪問當前上下文的RequestManager. 總而言之就是更方便的管理所有的RequestManager
     */
    this.treeNode = treeNode;
    this.requestTracker = requestTracker;//請求管理控制類,start锡移、pause呕童、resume、clear淆珊、restart夺饲、addRequest...
    this.glide = Glide.get(context);//保存有一個單例的glide對象
    this.optionsApplier = new OptionsApplier();//optionsApplier的apply方法返回一個圖片請求對象DrawableTypeRequest 
    /*網絡狀態(tài)的監(jiān)聽接口,實現(xiàn)了LifecycleListener 施符,與Activity/Fragment 生命周期綁定在一起往声。
     *具體實現(xiàn)DefaultConnectivityMonitor 以及NullConnectivityMonitor ,其中前者是一個網絡監(jiān)聽的具體實現(xiàn)戳吝,
     *后者是一個空實現(xiàn)烁挟,類似于Null Object Pattern ,可以避免空指針異常
     */
    ConnectivityMonitor connectivityMonitor = factory.build(context, new RequestManagerConnectivityListener(requestTracker));
  
    if (Util.isOnBackgroundThread()) {
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                lifecycle.addListener(RequestManager.this);
            }
        });
    } else {
        lifecycle.addListener(this);
    }
    lifecycle.addListener(connectivityMonitor);
}

/**
 * 根據(jù)傳入的參數(shù)類型作為泛型骨坑,創(chuàng)建一個圖片請求對象DrawableTypeRequest
 * DrawableTypeRequest中存有 glide撼嗓、requestTracker、lifecycle等的引用
 * 還有ModelLoader欢唾,load方法傳入的數(shù)據(jù)不一定是url且警,傳入的數(shù)據(jù)通過ModelLoader處理后轉為真正的圖片數(shù)據(jù)源
 */
public DrawableTypeRequest<String> load(String string) {
    return (DrawableTypeRequest<String>) fromString().load(string);
}
public DrawableTypeRequest<String> fromString() {
    return loadGeneric(String.class);
}
private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
    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("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<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context, glide, requestTracker, lifecycle, optionsApplier));
}
 //DrawableTypeRequest的load方法只是把傳入的model持有,并未真正加載
public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> load(ModelType model) {
    this.model = model;
    isModelSet = true;
    return this;
 }
2.2.3 into()

into()方法的具體邏輯都是在DrawableRequestBuilder的父類當中了礁遣。

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

}

DrawableRequestBuilder的父類是GenericRequestBuilder斑芜,在這里根據(jù)顯示拉伸類型,調用了glide.buildImageViewTarget()方法祟霍,這個方法會構建出一個Target對象杏头,Target對象則是用來最終展示圖片用的。其實是調用的為ImageViewTargetFactory的buildTarget()方法沸呐。然后傳遞給GenericRequestBuilder另一個接收Target對象的into()方法當中了醇王。

   public class ImageViewTargetFactory {

       @SuppressWarnings("unchecked")
       public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
           if (GlideDrawable.class.isAssignableFrom(clazz)) {
               return (Target<Z>) new GlideDrawableImageViewTarget(view);
           } else if (Bitmap.class.equals(clazz)) {
        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)");
           }
       }
   }

在buildTarget()方法中會根據(jù)傳入的class參數(shù)來構建不同的Target對象,這個class參數(shù)其實基本上只有兩種情況崭添,如果你在使用Glide加載圖片的時候調用了asBitmap()方法寓娩,那么這里就會構建出BitmapImageViewTarget對象,否則的話構建的都是GlideDrawableImageViewTarget對象。
接上文第一次into(imageview)通過imageViewTargetFactory.buildTarget(imageView, transcodedClass)生成GlideDrawableImageViewTarget對象有傳遞給第二個into(),第二個into對象

 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);
    lifecycle.addListener(target);
     requestTracker.runRequest(request);
    return target;
}
2.2.4 request

可以看出來調用buildRequest()方法構建出了一個Request對象棘伴,然后執(zhí)行這個Request寞埠。Request是用來發(fā)出加載圖片請求的,buildRequestRecursive實則調用了buildRequestRecursive函數(shù)焊夸。
順序為如果配置了thumbnail(縮略圖)請求仁连,則構建一個ThumbnailRequestCoordinator(包含了FullRequest和ThumbnailRequest)請求,否則簡單的構建一個Request阱穗,調用了obtainRequest函數(shù)怖糊。至此請求對象創(chuàng)建成功。
然后執(zhí)行runrequest

 public void runRequest(Request request) {
      this.requests.add(request);
//添加request對象到集合中颇象,requests是一個set伍伤,Set是一個不包含重復元素的 collection,在這里是無序的遣钳。
    if(!this.isPaused) {
        request.begin();//如果當前狀態(tài)是非暫停的扰魂,調用begin方法發(fā)送請求
    } else {
        this.pendingRequests.add(request);
  //將請求加入到掛起的請求集合,RequestTracker在RequestManager類中初始化蕴茴,并且跟蹤著生命周期
    }

}

begin函數(shù)劝评,首先做了幾個判斷,驗證寬高是否合法倦淀,加載占位圖等蒋畜,然后執(zhí)行onsizeReady()方法中l(wèi)oad() ,進入Engine邏輯

 this.engine.load(this.signature, width, height, dataFetcher, this.loadProvider, this.transformation, transcoder, this.priority, this.isMemoryCacheable, this.diskCacheStrategy, this);

load的策略,據(jù)調用loadFromCache從內存加載撞叽,若返回值為空再次從活動的資源中加載姻成,若再次為空查看jobs是否提交過任務,若沒有提交則創(chuàng)建EngineRunnable愿棋,并將任務提交到engineJob中科展。
Transformation類負責處理資源,這里面出現(xiàn)BitmapPool類糠雨,達到Bitmap復用才睹。
先從cache中尋找資源,如果找到則將其從cache中移除并放入activeResources中甘邀,否則從activeResources中尋找琅攘。cache是LruResourceCache對象,作為資源的LRU緩存松邪;activeResources是以弱引用為值的Map坞琴,用于緩存使用中的資源。比一般內存緩存額外多一級緩存的意義在于测摔,當內存不足時清理cache中的資源時置济,不會對使用中的Bitmap造成影響。

3.1

with(Context context) - 需要上下文锋八,這里還可以使用 Activity浙于、FragmentActivity、android.support.v4.app.Fragment挟纱、android.app.Fragment 的對象羞酗。將 Activity/Fragment 對象作為參數(shù)的好處是,圖片的加載會和 Activity/Fragment 的生命周期保持一致紊服,例如:onPaused 時暫停加載檀轨,onResume 時又會自動重新加載。所以在傳參的時候建議使用 Activity/Fragment 對象欺嗤,而不是 Context参萄。

3.2Module

Glide 的 Module 是一個可以全局改變 Glide 的行為的東西,為了定制 Glide 的行為我們要去實現(xiàn) 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
    }
}

可以看到 GlideModule 為我們提供了兩個方法讹挎,這里我們主要使用的是 applyOptions(Context context, GlideBuilder builder) , 我們自己的需要重新定義的代碼寫在該方法里就可以了吆玖。然后我們還需要去 AndroidManifest.xml 中使用 meta 聲明我們上面實現(xiàn)的 Module

   <application>

    <meta-data
        android:name="com.xxx.ExampleModule"
        android:value="GlideModule" />

    ...

</application>

applyOptions(Context context, GlideBuilder builder) 中有兩個參數(shù)筒溃, 我們通過使用 GlideBuilder 來實現(xiàn)我們的需求。先看看 GlideBuilder 中可用的方法

.setMemoryCache(MemoryCache memoryCache)
.setBitmapPool(BitmapPool bitmapPool)
.setDiskCache(DiskCache.Factory diskCacheFactory)
.setDiskCacheService(ExecutorService service)
.setResizeService(ExecutorService service)
.setDecodeFormat(DecodeFormat decodeFormat)

3.3Transformations篇

圖片進行處理操作沾乘,比如:圖片切圓角怜奖,灰階處理等等;這些需求我們通過 Transformations 操作 bitmap 來實現(xiàn)翅阵,我們可以修改圖片的任意屬性:尺寸歪玲,范圍,顏色掷匠,像素位置等等读慎。其實我們之前已經提到過兩個 Transformation 了,即 fitCenter 和 centerCrop 槐雾,這兩個是 Glide 已經實現(xiàn)的夭委。

怎么樣來實現(xiàn)自己的 Transformation ,我們需要創(chuàng)建一個類去實現(xiàn) Transformation 接口募强,但是要實現(xiàn)這個方法還是比較復雜的株灸,接口中 transform 方法提供的參數(shù) Resource<T> resource 不是那么好處理的。如果你只是想要對圖片(不是 Gif 和 video)做常規(guī)的 bitmap 轉換擎值,我們推薦你使用抽象類 BitmapTransformation慌烧。它簡化了很多的實現(xiàn),這應該能覆蓋 95% 的應用場景啦鸠儿。

下面的代碼實現(xiàn)了對圖片切圓角的操作屹蚊,其中 getId() 方法描述了這個 Transformation 的唯一標識厕氨,為避免意外我們需要確保它是唯一的。

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() 方法汹粤,將自定義的 Transformation 的對象作為參數(shù)傳遞進去就可以使用你的 Transformation 了命斧,這里也可以使用 .bitmaoTransform() 但是它只能用于 bitmap 的轉換。

Glide.with(context)
.load(mUrl)
.transform(new RoundTransformation(context , 20))
//.bitmapTransform( new RoundTransformation(context , 20) )
.into(mImageView);
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末嘱兼,一起剝皮案震驚了整個濱河市国葬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌芹壕,老刑警劉巖汇四,帶你破解...
    沈念sama閱讀 212,599評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異踢涌,居然都是意外死亡通孽,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評論 3 385
  • 文/潘曉璐 我一進店門睁壁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來利虫,“玉大人,你說我怎么就攤上這事堡僻】繁梗” “怎么了?”我有些...
    開封第一講書人閱讀 158,084評論 0 348
  • 文/不壞的土叔 我叫張陵钉疫,是天一觀的道長硼讽。 經常有香客問我,道長牲阁,這世上最難降的妖魔是什么固阁? 我笑而不...
    開封第一講書人閱讀 56,708評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮城菊,結果婚禮上备燃,老公的妹妹穿的比我還像新娘。我一直安慰自己凌唬,他們只是感情好并齐,可當我...
    茶點故事閱讀 65,813評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著客税,像睡著了一般况褪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上更耻,一...
    開封第一講書人閱讀 50,021評論 1 291
  • 那天测垛,我揣著相機與錄音,去河邊找鬼秧均。 笑死食侮,一個胖子當著我的面吹牛号涯,可吹牛的內容都是我干的。 我是一名探鬼主播锯七,決...
    沈念sama閱讀 39,120評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼链快,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了起胰?” 一聲冷哼從身側響起久又,我...
    開封第一講書人閱讀 37,866評論 0 268
  • 序言:老撾萬榮一對情侶失蹤巫延,失蹤者是張志新(化名)和其女友劉穎效五,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炉峰,經...
    沈念sama閱讀 44,308評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡畏妖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,633評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了疼阔。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片戒劫。...
    茶點故事閱讀 38,768評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖婆廊,靈堂內的尸體忽然破棺而出迅细,到底是詐尸還是另有隱情,我是刑警寧澤淘邻,帶...
    沈念sama閱讀 34,461評論 4 333
  • 正文 年R本政府宣布茵典,位于F島的核電站,受9級特大地震影響宾舅,放射性物質發(fā)生泄漏统阿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,094評論 3 317
  • 文/蒙蒙 一筹我、第九天 我趴在偏房一處隱蔽的房頂上張望扶平。 院中可真熱鬧,春花似錦蔬蕊、人聲如沸结澄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽概而。三九已至,卻和暖如春囱修,著一層夾襖步出監(jiān)牢的瞬間赎瑰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評論 1 267
  • 我被黑心中介騙來泰國打工破镰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留餐曼,地道東北人压储。 一個月前我還...
    沈念sama閱讀 46,571評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像源譬,于是被迫代替她去往敵國和親集惋。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,666評論 2 350

推薦閱讀更多精彩內容