Glide源碼閱讀理解一小時

前言

這篇圖害晦、文怒允、表惯疙、代碼一起組成的 Glide 源碼分析的文章是在上一篇文章 Android-Universal-Image-Loader源碼分析 中之后的又一篇圖片加載框架源碼解析簇抵,它也具備了 ImageLoader 中講述了Android一個圖片加載庫所需要的一些基礎(chǔ)必備的:MemoryCahceDiskCahce Decoder DownLoaderExecutor 等部分门烂。這篇 Glide 的代碼分析量可以說至少是 ImageLoader 的3倍多宙枷,本來想對 Glide 代碼進行拆分钙畔,細(xì)化每個部分進行講解這個每個部分講的更加清楚一些甚疟。但最終還是打算整體一篇文章講完仗岖,因為我覺得整體性的學(xué)習(xí)能更深的的了解到 Glide 的框架的設(shè)計之美。

本篇文章講述的Glide 相關(guān)知識比較多览妖,閱讀完需要大量的時間箩帚。所以我們按需分配,根據(jù)目錄尋找自己需要的知識點進行查看黄痪。

Glide介紹

Glide的Git地址:https://github.com/bumptech/glide

簡體中文文檔:https://muyangmin.github.io/glide-docs-cn/

Glide's documentation:https://bumptech.github.io/glide/

關(guān)于Glide

Glide是一個快速高效的Android圖片加載庫,注重于平滑的滾動盔然。Glide提供了易用的API桅打,高性能、可擴展的圖片解碼管道(decode pipeline)愈案,以及自動的資源池技術(shù)挺尾。

Glide 支持拉取,解碼和展示視頻快照站绪,圖片遭铺,和GIF動畫。Glide的Api是如此的靈活恢准,開發(fā)者甚至可以插入和替換成自己喜愛的任何網(wǎng)絡(luò)棧魂挂。默認(rèn)情況下,Glide使用的是一個定制化的基于HttpUrlConnection的棧馁筐,但同時也提供了與Google VolleySquare OkHttp快速集成的工具庫涂召。

雖然Glide 的主要目標(biāo)是讓任何形式的圖片列表的滾動盡可能地變得更快、更平滑敏沉,但實際上果正,Glide幾乎能滿足你對遠(yuǎn)程圖片的拉取/縮放/顯示的一切需求炎码。

Glide性能

Glide 充分考慮了Android圖片加載性能的兩個關(guān)鍵方面:

  • 圖片解碼速度
  • 解碼圖片帶來的資源壓力

為了讓用戶擁有良好的App使用體驗,圖片不僅要快速加載秋泳,而且還不能因為過多的主線程I/O或頻繁的垃圾回收導(dǎo)致頁面的閃爍和抖動現(xiàn)象潦闲。

Glide使用了多個步驟來確保在Android上加載圖片盡可能的快速和平滑:

  • 自動、智能地下采樣(downsampling)和緩存(caching)迫皱,以最小化存儲開銷和解碼次數(shù)歉闰;
  • 積極的資源重用,例如字節(jié)數(shù)組和Bitmap舍杜,以最小化昂貴的垃圾回收和堆碎片影響新娜;
  • 深度的生命周期集成,以確保僅優(yōu)先處理活躍的FragmentActivity的請求既绩,并有利于應(yīng)用在必要時釋放資源以避免在后臺時被殺掉概龄。

GlideAPI

Glide 使用簡明的流式語法API,這是一個非常棒的設(shè)計饲握,因為它允許你在大部分情況下一行代碼搞定需求:

Glide.with(fragment)
    .load(url)
    .into(imageView);

上述是FragmengGlide將一張網(wǎng)絡(luò)圖片顯示到ImageView的代碼私杜,下面源碼分析的時候我們也會用這段代碼進行分析,看看這么簡單的API到底是怎么實現(xiàn)的救欧。

Glide源碼分析

我們學(xué)習(xí)和了解一些框架主要不是看它某個功能的具體實現(xiàn)衰粹,主要是學(xué)習(xí)框架結(jié)構(gòu)搭建和框架中模塊的設(shè)計與實現(xiàn)。當(dāng)然每個人的對每個框架的理解都各不相同笆怠,不過沒關(guān)系我們可以多學(xué)習(xí)多總結(jié)铝耻,慢慢培養(yǎng)我們自己的框架結(jié)構(gòu)意識。這個在我們平時開發(fā)過程中對我們幫助非常大蹬刷。

在這里插入圖片描述

上圖是對我Glide的一個總結(jié)^_^瓢捉。

Glide接入

Glide的用法網(wǎng)上有很多文章講述的都非常好,這里不再進行講述办成。這塊主要想通過Glide的配置來分析Glide的運行機制泡态。

我們在使用Glide的時候都會使用注解@GlideModule 實現(xiàn)AppGlideModule 或者 GeneratedAppGlideModule ,生成一個類名為GeneratedAppGlideModuleImpl 它是Glide 模塊的代理迂卢。 在GeneratedAppGlideModuleImpl 會包含我們自定義的GlideModel某弦。

下面為我們接入項目的Glide配置:

實現(xiàn)Glide對緩存的配置

@GlideModule
public final class GlideModuleConfig extends AppGlideModule {
    @Override
    public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
        super.applyOptions(context, builder);
        long memoryCacheSizeBytes = 1024 * 1024 * 20;
        builder.setMemoryCache(new LruResourceCache(memoryCacheSizeBytes));
        long diskCacheSizeBytes = 1024 * 1024 * 100;
        builder.setDiskCache(new InternalCacheDiskCacheFactory(context, diskCacheSizeBytes));
    }
}

實現(xiàn)Okhttp的接入

@GlideModule
public final class OkHttpLibraryGlideModule extends LibraryGlideModule {
    @Override
    public void registerComponents(
            @NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
        //將GlideUrl數(shù)據(jù)轉(zhuǎn)為InputStream的ModelLoader替換為Okhttp
        registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
    }
}

我們編譯之后生成的GeneratedAppGlideModuleImpl

final class GeneratedAppGlideModuleImpl extends GeneratedAppGlideModule {
    private final GlideModuleConfig appGlideModule;
    public GeneratedAppGlideModuleImpl(Context context) {
      appGlideModule = new GlideModuleConfig();
    }
    //GlideBuilder的配置項進行應(yīng)用
    @Override
    public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
      appGlideModule.applyOptions(context, builder);
    }
    //注冊自定義GlideModule
    @Override
    public void registerComponents(@NonNull Context context, @NonNull Glide glide,
        @NonNull Registry registry) {
      new OkHttpLibraryGlideModule().registerComponents(context, glide, registry);
      appGlideModule.registerComponents(context, glide, registry);
    }
    /***部分代碼省略***/
}

下面將GeneratedAppGlideModuleImpl的使用。

Glide初始化

public class Glide implements ComponentCallbacks2 {
    /**
    * Get the singleton.
    * @return the singleton
    */
    @NonNull
    public 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;
    }
    //通過Java反射機制獲取通過注解生成的GeneratedAppGlideModuleImpl
    private static GeneratedAppGlideModule getAnnotationGeneratedGlideModules(Context context) {
        GeneratedAppGlideModule result = null;
        try {
        Class<GeneratedAppGlideModule> clazz =
            (Class<GeneratedAppGlideModule>)
                Class.forName("com.bumptech.glide.GeneratedAppGlideModuleImpl");
        result =
            clazz.getDeclaredConstructor(Context.class).newInstance(context.getApplicationContext());
        } catch (ClassNotFoundException e) {
        /***部分代碼省略**/
        }
        return result;
    }
}

Glide是個單例而克,初始化的時候需要注解生成的GeneratedAppGlideModuleImpl 靶壮。

public class Glide implements ComponentCallbacks2 {
    @GuardedBy("Glide.class")
    private static void initializeGlide(
        @NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
        initializeGlide(context, new GlideBuilder(), generatedAppGlideModule);
    }
    @GuardedBy("Glide.class")
    @SuppressWarnings("deprecation")
    private static void initializeGlide(
        @NonNull Context context,
        @NonNull GlideBuilder builder,
        @Nullable GeneratedAppGlideModule annotationGeneratedModule) {
        Context applicationContext = context.getApplicationContext();
        List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
        //如果Manifest文件中配置了GlideModule,并且annotationGeneratedModule允許Manifest文件中配置生效员萍,那么需要解析出來
        if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
            manifestModules = new ManifestParser(applicationContext).parse();
        }
        //如果annotationGeneratedModule配置了擴展模塊需要解析出來亮钦,并且不能與果Manifest中的重復(fù)
        if (annotationGeneratedModule != null
            && !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) {
            Set<Class<?>> excludedModuleClasses = annotationGeneratedModule.getExcludedModuleClasses();
            Iterator<com.bumptech.glide.module.GlideModule> iterator = manifestModules.iterator();
            while (iterator.hasNext()) {
                com.bumptech.glide.module.GlideModule current = iterator.next();
                if (!excludedModuleClasses.contains(current.getClass())) {
                    continue;
                }
                /***省略部分日志***/
                iterator.remove();
            }
        }
        /***省略部分日志***/
        //獲取Request的構(gòu)造管理工廠類
        RequestManagerRetriever.RequestManagerFactory factory =
            annotationGeneratedModule != null
                ? annotationGeneratedModule.getRequestManagerFactory()
                : null;
        builder.setRequestManagerFactory(factory);
        //Manifest中的配置項進行應(yīng)用
        for (com.bumptech.glide.module.GlideModule module : manifestModules) {
            module.applyOptions(applicationContext, builder);
        }
        //annotationGeneratedModule進行配置項的應(yīng)用
        if (annotationGeneratedModule != null) {
            annotationGeneratedModule.applyOptions(applicationContext, builder);
        }
        //構(gòu)造Glide
        Glide glide = builder.build(applicationContext);
        //Manifest中的組件進行注冊
        for (com.bumptech.glide.module.GlideModule module : manifestModules) {
            try {
                module.registerComponents(applicationContext, glide, glide.registry);
            } catch (AbstractMethodError e) {
                /***省略部分日志***/
            }
        }
        //annotationGeneratedModule中的組件進行注冊
        if (annotationGeneratedModule != null) {
            annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
        }
        //application注冊Glide組件
        applicationContext.registerComponentCallbacks(glide);
        Glide.glide = glide;
    }
}
  1. 獲取ManifestGlideModule,反射構(gòu)造充活;
  2. 獲取GeneratedAppGlideModule中擴展模塊蜂莉,如果包含Manifest的擴展那么進行刪除蜡娶;
  3. 獲取RequestManagerFactroy;
  4. Manifest.applyoptions;
  5. GeneratedAppGlideModule.applytions;
  6. 構(gòu)建Glide;
  7. Manifest.registerComponents;
  8. GeneratedAppGlideModule.registerComponents;

Glide和Engine構(gòu)造

public class Glide implements ComponentCallbacks2 {
    private final Engine engine;//負(fù)責(zé)啟動負(fù)載以及管理活動和緩存的資源。
    private final BitmapPool bitmapPool;//Bitmap的緩存池(LruBitmapPool)
    private final MemoryCache memoryCache;//Resource緩存池(LruResourceCache)
    private final GlideContext glideContext;
    private final Registry registry;
    private final ArrayPool arrayPool;//存儲大小可變的數(shù)組的緩存池(LruArrayPool)
    //一組靜態(tài)方法用來創(chuàng)建一個新的RequestManager或者從已經(jīng)存在的activity和fragment中獲取
    private final RequestManagerRetriever requestManagerRetriever;
    private final ConnectivityMonitorFactory connectivityMonitorFactory;
    new Glide(
        @NonNull context,//上下文環(huán)境
        @NonNull engine,//任務(wù)執(zhí)行引擎
        @NonNull memoryCache,//內(nèi)存資源緩存
        @NonNull bitmapPool,//內(nèi)存Bitmap緩存
        @NonNull arrayPool,//數(shù)據(jù)緩存池
        @NonNull requestManagerRetriever,//RequestManager管理集合
        @NonNull connectivityMonitorFactory,//網(wǎng)絡(luò)監(jiān)聽器的生產(chǎn)工廠
        int logLevel,//log日志等級映穗,默認(rèn)為Log.info=4
        @NonNull defaultRequestOptionsFactory,//默認(rèn)的RequestOptions生產(chǎn)工廠
        @NonNull defaultTransitionOptions,//默認(rèn)的資源展現(xiàn)過渡配置容器窖张,,默認(rèn)map大小為0
        @NonNull defaultRequestListeners,//在圖像加載時的監(jiān)聽器數(shù)組蚁滋,默認(rèn)數(shù)組大小為0
        boolean isLoggingRequestOriginsEnabled,//是否需要請求日志
        boolean isImageDecoderEnabledForBitmaps,//在安卓P或更高版本進行解碼bitmap
        int hardwareBitmapFdLimit,//700,
        int minHardwareDimension){//128,
        /***部分代碼省略***/
    }
}

Glide構(gòu)造的時候需要構(gòu)造Engine:

public class Engine
    implements EngineJobListener,
        MemoryCache.ResourceRemovedListener,
        EngineResource.ResourceListener {
    /***部分代碼省略***/
    public Engine(
        MemoryCache memoryCache,//內(nèi)存緩存
        DiskCache.Factory diskCacheFactory,//磁盤緩存
        //磁盤緩存執(zhí)行器宿接,該線程池的核心線程數(shù)和最大線程數(shù)為1
        GlideExecutor diskCacheExecutor,
        //檢索資源尚未在緩存中,該線程池的核心線程數(shù)和最大線程數(shù)為cpu內(nèi)核數(shù)量辕录,最大為4
        GlideExecutor sourceExecutor,
        ////newScheduledThreadPool睦霎,核心線程數(shù)為0,用來執(zhí)行網(wǎng)絡(luò)操作
        GlideExecutor sourceUnlimitedExecutor,
        //加載動畫線程池走诞,加載動畫圖像的幀時使用副女,尤其是GitDrawable,該線程池的核心線程數(shù)和最大線程數(shù)為1或2(cpu內(nèi)核數(shù)量>=4)
        GlideExecutor animationExecutor,
        //活動資源是否允許被保留蚣旱,默認(rèn)為false
        boolean isActiveResourceRetentionAllowed) {
            /***部分代碼省略***/
    }
    /***部分代碼省略***/
}

在閱讀源碼的時候Glide.java的構(gòu)造方法碑幅,除過基礎(chǔ)的賦值操作之前就剩下大量的注冊。注冊的所有組件都由Registry進行管理塞绿。

Register

Register :管理組件注冊以擴展或替換Glide的默認(rèn)加載沟涨,解碼和編碼邏輯。

public class Registry {
    public static final String BUCKET_GIF = "Gif";
    public static final String BUCKET_BITMAP = "Bitmap";
    public static final String BUCKET_BITMAP_DRAWABLE = "BitmapDrawable";
    private static final String BUCKET_PREPEND_ALL = "legacy_prepend_all";
    private static final String BUCKET_APPEND_ALL = "legacy_append";
    //維護{@link ModelLoader}的有序放置以及它們處理的模型和數(shù)據(jù)類型,從最高優(yōu)先級到最低優(yōu)先級的順序异吻。
    private final ModelLoaderRegistry modelLoaderRegistry;
    //編碼數(shù)據(jù)容器裹赴,有序列表
    private final EncoderRegistry encoderRegistry;
    //包含能夠解碼任意數(shù)據(jù)類型的{@link ResourceDecoder}的有序列表
    //分為從最高優(yōu)先級解碼器到最低優(yōu)先級解碼器的任意資源類型。
    private final ResourceDecoderRegistry decoderRegistry;
    //包含能夠編碼任意資源*類型的{@link ResourceEncoder}的有序列表诀浪。
    private final ResourceEncoderRegistry resourceEncoderRegistry;
    //DataRewinder的生產(chǎn)工廠
    private final DataRewinderRegistry dataRewinderRegistry;
    //注冊和轉(zhuǎn)化ResourceTranscoder
    private final TranscoderRegistry transcoderRegistry;
    //包含能夠解析圖像標(biāo)題的{@link ImageHeaderParser}的無序列表棋返。
    private final ImageHeaderParserRegistry imageHeaderParserRegistry;
    //維護“模型+資源”類的高速緩存到一組已注冊資源類的集合,這些資源類是可以從模型類中解碼的資源類的子類笋妥。
    private final ModelToResourceClassCache modelToResourceClassCache = new ModelToResourceClassCache();
    //維護數(shù)據(jù),資源和轉(zhuǎn)碼類的緩存
    private final LoadPathCache loadPathCache = new LoadPathCache();
    //異常列表維護容器
    private final Pool<List<Throwable>> throwableListPool = FactoryPools.threadSafeList();
    /***部分代碼省略***/
}

下面我們看看Glide默認(rèn)注冊的各種組件窄潭。

Encoder

Encoder:用于將數(shù)據(jù)編碼成文件春宣。

//用于將數(shù)據(jù)寫入某些持久性數(shù)據(jù)存儲的接口,例如文件
public interface Encoder<T> {
    //將給定數(shù)據(jù)寫入給定輸出流嫉你,如果寫入完成月帝,則返回True
    boolean encode(@NonNull T data, @NonNull File file, @NonNull Options options);
}

EncoderRegistry :包含能夠編碼任意數(shù)據(jù)類型的Encoder的有序列表。

DataClass Encoder
ByteBuffer ByteBufferEncoder
InputStream StreamEncoder
Bitmap BitmapEncoder
BitmapDrawable BitmapDrawableEncoder
GifDrawable GifDrawableEncoder
//用于將數(shù)據(jù)從資源寫入某些持久性數(shù)據(jù)存儲的接口幽污,例如文件
public interface ResourceEncoder<T> extends Encoder<Resource<T>> {
    //獲取對應(yīng)的策略模式
    @NonNull
    EncodeStrategy getEncodeStrategy(@NonNull Options options);
}
//ResourceEncoder進行資源編碼緩存枚舉
public enum EncodeStrategy {
    //將資源的原始未修改數(shù)據(jù)寫入磁盤,不包括采樣和轉(zhuǎn)化
    SOURCE,
    //將資源的解碼嚷辅,下采樣和轉(zhuǎn)換后的數(shù)據(jù)寫入磁盤。
    TRANSFORMED,
    /** Will write no data. */
    NONE,
}

ResourceEncoderRegistry :包含能夠編碼任意資源類型的ResourceEncoder的有序列表距误。

ResourceClass ResourceEncoder
BitmapDrawable BitmapDrawableEncoder
GifDrawable GifDrawableEncoder
Bitmap BitmapEncoder

Decoder

Decoder:解碼給定的資源類型文件簸搞。

/**
 *用于解碼資源的接口扁位。
 *@param <T>將從中解碼資源的類型(文件,InputStream等)趁俊。
 *@param <Z>解碼資源的類型(位圖域仇,可繪制等)。
 */
public interface ResourceDecoder<T, Z> {
    /**
     *如果此解碼器能夠使用給定的源解碼給定的源寺擂,則返回true選項暇务,否則為false。
     *解碼器應(yīng)盡最大努力快速確定是否可能夠解碼數(shù)據(jù)怔软,但不應(yīng)嘗試完全讀取給定的數(shù)據(jù)垦细。
     *典型的實現(xiàn)將檢查文件頭,以確保它們與解碼器期望的內(nèi)容匹配句柄(即GIF解碼器應(yīng)驗證圖像是否包含GIF標(biāo)頭塊)挡逼。
     */
    boolean handles(@NonNull T source, @NonNull Options options) throws IOException;
    //從給定的數(shù)據(jù)返回已解碼的資源括改;如果無法解碼任何資源,則返回null挚瘟。
    //注意width和height參數(shù)僅是提示叹谁,沒有要求解碼后的資源與給定尺寸完全匹配。
    //一個典型的用例將使用目標(biāo)尺寸來確定對位圖進行降采樣的量乘盖,以避免分配過多焰檩。
    @Nullable
    Resource<Z> decode(@NonNull T source, int width, int height, @NonNull Options options)throws IOException;
}

ResourceDecoderRegistry :包含ResourceDecoder的有序列表,這些列表可以將任意數(shù)據(jù)類型解碼為從最高優(yōu)先級解碼器到最低優(yōu)先級解碼器的任意資源類型订框。

Bucket DataClass ResourceClass ResourceDecoder
Bitmap ByteBuffer Bitmap ByteBufferBitmapImageDecoderResourceDecoder
Bitmap InputStream Bitmap InputStreamBitmapImageDecoderResourceDecoder
Bitmap ParcelFileDescriptor Bitmap VideoDecoder
Bitmap AssetFileDescriptor Bitmap VideoDecoder
Bitmap Bitmap Bitmap UnitBitmapDecoder
Bitmap GifDecoder Bitmap GifFrameResourceDecoder
Bucket DataClass ResourceClass ResourceDecoder
BitmapDrawables ByteBuffer BitemapDrawable BitmapDrawableDecoder
BitmapDrawables InputStream BitemapDrawable BitmapDrawableDecoder
BitmapDrawables ParcelFileDescriptor BitemapDrawable BitmapDrawableDecoder
Bucket DataClass ResourceClass ResourceDecoder
Gif ByteBuffer GifDrawable ByteBufferGifDecoder
Bucket DataClass ResourceClass ResourceDecoder
All Uri Drawable ResourceDrawableDecoder
All Uri Bitmap ResourceBitmapDecoder
All File File FileDecoder
All Drawable Drawable UnitDrawableDecoder

Transcoder

Transcoder:將一種類型的資源轉(zhuǎn)碼為另一種類型的資源析苫。

/**
 * 將一種類型的資源轉(zhuǎn)碼為另一種類型的資源。
 * @param <Z> 要被轉(zhuǎn)碼的資源類型
 * @param <R> 需要轉(zhuǎn)成的資源類型
 */
public interface ResourceTranscoder<Z, R> {
    //將給定資源轉(zhuǎn)碼為新資源類型并返回新資源穿扳。
    @Nullable
    Resource<R> transcode(@NonNull Resource<Z> toTranscode, @NonNull Options options);
}

TranscoderRegistry :該類允許ResourceTranscoder在它們之間進行轉(zhuǎn)換的類中進行注冊和檢索衩侥。

ResourceClass TranscoeClass ResourceTranscoder
Bitmap BitmapDrawable BitmapDrawableTranscoder
Bitmap byte[] BitmapBytesTranscoder
Drawable byte[] DrawableBytesTranscoder
GifDrawable byte[] GifDrawableBytesTranscoder

ModelLoader

ModelLoaderGlide 比較核心的類,主要是用來加載數(shù)據(jù)源Model 中的數(shù)據(jù)矛物。

一般加載資源類型有Bitmap 茫死、String(網(wǎng)絡(luò)圖片、本地圖片履羞、資源圖片) 峦萎、Uri(網(wǎng)絡(luò)圖片、本地圖片忆首、資源圖片) 爱榔、URL(網(wǎng)絡(luò)圖片)Integer(資源圖片)File(本地文件)等糙及。

Glide 為每中資源類型設(shè)計了對應(yīng)的ModelLoaderFactory 详幽,每種ModelLoaderFactory對應(yīng)一種ModleLoader

資源類型可以相互轉(zhuǎn)化,比如String轉(zhuǎn)URL唇聘。所以ModelLoader 內(nèi)部也是可以相互進行代理版姑。

在這里插入圖片描述
//com.bumptech.glide.load.model.MultiModelLoaderFactory
//通過僅在以下位置創(chuàng)建加載器來避免堆棧溢出遞歸地創(chuàng)建模型加載器
//遞歸請求(如果尚未在鏈中的早期創(chuàng)建)。例如:Uri加載程序可以轉(zhuǎn)換為另一個模型雳灾,而后者又可以轉(zhuǎn)換回Uri漠酿。
//盡管原始Uri加載程序不會提供給中間模型加載程序,其他的Uri裝載程序也會谎亩。
synchronized <Model> List<ModelLoader<Model, ?>> build(@NonNull Class<Model> modelClass) {
    try {
        List<ModelLoader<Model, ?>> loaders = new ArrayList<>();
        for (Entry<?, ?> entry : entries) {
            if (alreadyUsedEntries.contains(entry)) {//去重
                continue;
            }
            if (entry.handles(modelClass)) {
                alreadyUsedEntries.add(entry);
                //遞歸調(diào)用MultiModelLoaderFactory.build
                loaders.add(this.<Model, Object>build(entry));
                alreadyUsedEntries.remove(entry);
            }
        }
        return loaders;
    } catch (Throwable t) {
        alreadyUsedEntries.clear();
        throw t;
    }
}
//調(diào)用ModelLoaderFactory.build
private <Model, Data> ModelLoader<Model, Data> build(@NonNull Entry<?, ?> entry) {
    return (ModelLoader<Model, Data>) Preconditions.checkNotNull(entry.factory.build(this));
}

比如ModelClassString 類型炒嘲,我們遍歷找到 StreamFactory

public static class StreamFactory implements ModelLoaderFactory<String, InputStream> {
    @NonNull
    @Override
    public ModelLoader<String, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {
        return new StringLoader<>(multiFactory.build(Uri.class, InputStream.class));
    }
    @Override
    public void teardown() {
      // Do nothing.
    }
}

這個時候有會進行一次遞歸:

//com.bumptech.glide.load.model.MultiModelLoaderFactory
public synchronized <Model, Data> ModelLoader<Model, Data> build(
      @NonNull Class<Model> modelClass, @NonNull Class<Data> dataClass) {
    try {
        List<ModelLoader<Model, Data>> loaders = new ArrayList<>();
        boolean ignoredAnyEntries = false;
        for (Entry<?, ?> entry : entries) {
            if (alreadyUsedEntries.contains(entry)) {//去重
                ignoredAnyEntries = true;
                continue;
            }
            if (entry.handles(modelClass, dataClass)) {
                alreadyUsedEntries.add(entry);
                //遞歸調(diào)用MultiModelLoaderFactory.build
                loaders.add(this.<Model, Data>build(entry));
                alreadyUsedEntries.remove(entry);
            }
        }
        if (loaders.size() > 1) {//數(shù)量大于1的時候構(gòu)建MultiModelLoader存儲loaders
            return factory.build(loaders, throwableListPool);
        } else if (loaders.size() == 1) {
            return loaders.get(0);
        } else {
            //如果遞歸導(dǎo)致沒有可用的加載程序,請避免崩潰匈庭。該斷言應(yīng)該捕獲完全未處理的類型夫凸,遞歸可能意味著未在某處處理子類型進入堆棧,這通常是可以的阱持。 
            if (ignoredAnyEntries) {
                return emptyModelLoader();
            } else {
                throw new NoModelLoaderAvailableException(modelClass, dataClass);
            }
        }
    } catch (Throwable t) {
        alreadyUsedEntries.clear();
        throw t;
    }
}

所以我們根據(jù)Model的類型從注冊到GlideModelLoaderFactory中尋找匹配項夭拌,這個查找是按照添加的順序進行遍歷。找到對應(yīng)的ModelLoaderFactory 在生成 ModelLoader的時候可能會繼續(xù)尋找它的代理的ModelLoader衷咽,直到不需要代理為止鸽扁,我們會對這個結(jié)果去重然后如果結(jié)果數(shù)量大于1那么會生成MultiModelLoader 存儲這一組 ModelLoader 。如果這組中的 ModelLoader 中還包括 MultiModelLoader 那么這個 MultiModelLoader 內(nèi)還會有一組 ModelLoader 镶骗。

下面為ModelLoader涉及到的類:

DataSource :表示某些檢索到的數(shù)據(jù)的來源桶现。

public enum DataSource {
    //表示數(shù)據(jù)可能是從設(shè)備本地檢索的,盡管可能已經(jīng)是通過可能已從遠(yuǎn)程源獲取數(shù)據(jù)的內(nèi)容提供者獲得的鼎姊。
    LOCAL,
    //表示從設(shè)備以外的遠(yuǎn)程源檢索到數(shù)據(jù)骡和。
    REMOTE,
    //表示從設(shè)備緩存中檢索的數(shù)據(jù)未經(jīng)修改。
    DATA_DISK_CACHE,
    //表示數(shù)據(jù)是從設(shè)備上緩存中的已修改內(nèi)容中檢索到的相寇。
    RESOURCE_DISK_CACHE,
    //表示已從內(nèi)存緩存中檢索數(shù)據(jù)慰于。
    MEMORY_CACHE,
}

DataFetcher :加載資源的數(shù)據(jù)。

/**
 * 懶惰地檢索可用于加載資源的數(shù)據(jù)唤衫。
 * @param <T> 要加載的數(shù)據(jù)類型  (InputStream, byte[], File etc).
 */
public interface DataFetcher<T> {
    //獲取可以從中解碼資源的數(shù)據(jù)婆赠。
    //沒有數(shù)據(jù)的時候調(diào)用,異步線程執(zhí)行佳励,io不會阻塞
    void loadData(@NonNull Priority priority, @NonNull DataCallback<? super T> callback);
    //清理或回收此數(shù)據(jù)獲取器使用的任何資源休里。
    void cleanup();
    //取消任務(wù)
    void cancel();
    //返回此訪存器將嘗試獲取的數(shù)據(jù)的類。
    @NonNull
    Class<T> getDataClass();
    //返回要處理的資源數(shù)據(jù)類型
    @NonNull
    DataSource getDataSource();
}

ModelLoaderFactory :用于為給定模型類型創(chuàng)建ModelLoader的接口植兰。使用設(shè)計模式之工廠模式份帐,讓每一個ModelLoader 對應(yīng)一個工廠璃吧。

public interface ModelLoaderFactory<T, Y> {
    //為此模型類型構(gòu)建一個具體的ModelLoader楣导。
    @NonNull
    ModelLoader<T, Y> build(@NonNull MultiModelLoaderFactory multiFactory);
    //一種生命周期方法,該工廠即將被替換時將被調(diào)用畜挨。
    void teardown();
}

ModelLoader :用于將任意復(fù)雜的數(shù)據(jù)模型轉(zhuǎn)換為具體的數(shù)據(jù)類型筒繁。

/**
 *工廠接口噩凹,用于將任意復(fù)雜的數(shù)據(jù)模型轉(zhuǎn)換為具體的數(shù)據(jù)類型,DataFetcher可以使用來獲取由模型。
 *此接口有兩個目標(biāo):
 *1.將特定模型轉(zhuǎn)換為可以被解碼為資源毡咏。
 *2.允許將模型與視圖的尺寸組合以獲取模型的資源具體尺寸驮宴。
 *這不僅避免了必須在xml和代碼中重復(fù)尺寸,以便確定具有不同密度的設(shè)備上視圖的大小呕缭,
 *但也允許您使用布局權(quán)重或通過編程方式放置視圖的尺寸而不會強迫您獲取通用資源大小堵泽。
 *您獲取的資源越小,使用的帶寬和電池壽命越少恢总,并且越低每個資源的內(nèi)存占用量迎罗。
 *
 *@param <Model> 模型的類型。
 *@param <Data> 可以使用的數(shù)據(jù)類型ResourceDecoder來解碼資源片仿。
 */
public interface ModelLoader<Model, Data> {
    //返回可以解碼model的LoadData來進行資源解碼
    //注意-如果無法返回有效的數(shù)據(jù)提取程序(例如纹安,如果模型的URL為空),然后可以從此方法返回空數(shù)據(jù)獲取程序砂豌。
    @Nullable
    LoadData<Data> buildLoadData(@NonNull Model model, int width, int height, @NonNull Options options);
    //當(dāng)前的數(shù)據(jù)模型是否能被處理
    boolean handles(@NonNull Model model);
}

LoadData :加載的資源的Key和對應(yīng)加載該資源的DataFetcher 的包裝對象厢岂。

//包含一組標(biāo)識負(fù)載源的keys,指向等效數(shù)據(jù)的備用緩存鍵以及DataFetcher阳距,可用于獲取在緩存中找不到的數(shù)據(jù)塔粒。
class LoadData<Data> {
  public final Key sourceKey;//數(shù)據(jù)源標(biāo)識
  public final List<Key> alternateKeys;//備用的標(biāo)識
  public final DataFetcher<Data> fetcher;//可用于加載資源的數(shù)據(jù)
  public LoadData(@NonNull Key sourceKey, @NonNull DataFetcher<Data> fetcher) {
    this(sourceKey, Collections.<Key>emptyList(), fetcher);
  }
  public LoadData(
    @NonNull Key sourceKey,
    @NonNull List<Key> alternateKeys,
    @NonNull DataFetcher<Data> fetcher) {
    this.sourceKey = Preconditions.checkNotNull(sourceKey);
    this.alternateKeys = Preconditions.checkNotNull(alternateKeys);
    this.fetcher = Preconditions.checkNotNull(fetcher);
  }
}

ModelLoaderRegistry :維護ModelLoader的有序放置以及它們處理的模型和數(shù)據(jù)類型,從最高優(yōu)先級到最低優(yōu)先級的順序。

ModelClass DataClass ModelLoaderFactory
Bitmap Bitmap UnitModelLoader.Factory
GifDecoder GifDecoder UnitModelLoader.Factory
File ByteBuffer ByteBufferFileLoader.Factory
File InputStream FileLoader.StreamFactory
File ParcelFileDescriptor FileLoader.FileDescriptorFactory
File File UnitModelLoader.Factory
int InputStream ResourceLoader.StreamFactory
int ParcelFileDescriptor ResourceLoader.FileDescriptorFactory
int Uri ResourceLoader.UriFactory
int AssetFileDescriptor ResourceLoader.AssetFileDescriptorFactory
Integer InputStream ResourceLoader.StreamFactory
Integer ParcelFileDescriptor ResourceLoader.FileDescriptorFactory
Integer Uri ResourceLoader.UriFactory
Integer AssetFileDescriptor ResourceLoader.AssetFileDescriptorFactory
String InputStream DataUrlLoader.StreamFactory
String InputStream StringLoader.StreamFactory
String ParcelFileDescriptor StringLoader.FileDescriptorFactory
String AssetFileDescriptor StringLoader.AssetFileDescriptorFactory
Uri InputStream DataUrlLoader.StreamFactory
Uri InputStream HttpUriLoader.Factory()
Uri InputStream AssetUriLoader.StreamFactory
Uri InputStream MediaStoreImageThumbLoader.Factory
Uri InputStream MediaStoreVideoThumbLoader.Factory
Uri InputStream UriLoader.StreamFactory
Uri InputStream UrlUriLoader.StreamFactory
Uri ParcelFileDescriptor AssetUriLoader.FileDescriptorFactory
Uri ParcelFileDescriptor UriLoader.FileDescriptorFactory
Uri AssetFileDescriptor UriLoader.AssetFileDescriptorFactory
Uri File MediaStoreFileLoader.Factory
Uri Uri UnitModelLoader.Factory
URL InputStream UrlLoader.StreamFactory
GlideUrl InputStream HttpGlideUrlLoader.Factory
byte[] InputStream ByteArrayLoader.StreamFactory
byte[] ByteBuffer ByteArrayLoader.ByteBufferFactory
Drawable Drawable UnitModelLoader.Factory

我們現(xiàn)在就用 String 類型的網(wǎng)絡(luò)圖片地址的 Model 來看一下:

尋找處理 StringModelLoader :

DataUrlLoader.StreamFactory.build=DataUrlLoader
  
StringLoader.StreamFactory.build
  =MultiModelLoaderFactory(Uri.class, InputStream.class)=MultiModelLoader
  ==>DataUrlLoader.StreamFactory=DataUrlLoader
  ==>HttpUriLoader.Factory=HttpUriLoader
  ==>==>MultiModelLoaderFactory(Uri.class, ParcelFileDescriptor.class)
  ==>==>==>AssetUriLoader.FileDescriptorFactory=AssetUriLoader
  ==>==>==>UriLoader.FileDescriptorFactory=AssetUriLoader
  ==>==>==>AssetUriLoader(去重)
  ==>AssetUriLoader.StreamFactory=AssetUriLoader
  ==>MediaStoreImageThumbLoader.Factory=MediaStoreImageThumbLoader
  ==>MediaStoreVideoThumbLoader.Factory=MediaStoreVideoThumbLoader
  ==>UriLoader.StreamFactory=AssetUriLoader
  ==>UrlUriLoader.StreamFactory
  ==>==>MultiModelLoaderFactory(GlideUrl.class, InputStream.class)
  ==>==>==>HttpGlideUrlLoader.Factory=HttpGlideUrlLoader
  
StringLoader.FileDescriptorFactory.build
  =MultiModelLoaderFactory(Uri.class, ParcelFileDescriptor.class)=MultiModelLoader
  ==>AssetUriLoader.FileDescriptorFactory=AssetUriLoader
  ==>UriLoader.FileDescriptorFactory=UriLoader
  
StringLoader.AssetFileDescriptorFactory.build
  =MultiModelLoaderFactory(Uri.class, AssetFileDescriptor.class)
  =UriLoader.AssetFileDescriptorFactory=UriLoader
在這里插入圖片描述

接下來對這一組 ModelLoader 進行處理娄涩,過濾掉無法處理 Model 數(shù)據(jù)的 ModelLoader 窗怒。其中 MultiModelLoader 中只要有一個 ModelLoader 能處理 Model 數(shù)據(jù), 那么這個 MultiModelLoader 就可以不被過濾蓄拣。

StringLoader.StreamFactory=MultiModelLoader[7]
StringLoader.FileDescriptorFactory=MultiModelLoader[2]
StringLoader.AssetFileDescriptorFactory=UriLoader
在這里插入圖片描述

Transformation

Glid-Transformation文檔:https://muyangmin.github.io/glide-docs-cn/doc/transformations.html

Transformation:用于在實現(xiàn)資源上執(zhí)行任意轉(zhuǎn)換的類扬虚。

//唯一標(biāo)識某些數(shù)據(jù)放置的接口。
public interface Key {
    String STRING_CHARSET_NAME = "UTF-8";
    Charset CHARSET = Charset.forName(STRING_CHARSET_NAME);
    //將所有唯一標(biāo)識信息添加到給定的摘要中球恤。
    void updateDiskCacheKey(@NonNull MessageDigest messageDigest);
    @Override
    boolean equals(Object o);
    @Override
    int hashCode();
}
//用于在實現(xiàn)資源上執(zhí)行任意轉(zhuǎn)換的類
//equals和hashCode來標(biāo)識內(nèi)存緩存中的轉(zhuǎn)換
//updateDiskCacheKey來標(biāo)識磁盤中的轉(zhuǎn)換緩存辜昵。
public interface Transformation<T> extends Key {
    //轉(zhuǎn)換給定資源并返回轉(zhuǎn)換后的資源
    @NonNull
    Resource<T> transform(@NonNull Context context, @NonNull Resource<T> resource, int outWidth, int outHeight);
}

DataRewinder

DataRewinder:對數(shù)據(jù)的游標(biāo)進行重置,也就是所謂的倒帶咽斧。

這個邏輯在上一篇文章 Android-Universal-Image-Loader源碼分析 中也有講到過堪置,我們拿到數(shù)據(jù)流之后可能會從它的頭部信息中獲取一些圖片本身的參數(shù),然后我們再將數(shù)據(jù)流寫入文件緩存的時候要重置數(shù)據(jù)流的游標(biāo)保證寫入的數(shù)據(jù)完整张惹。

public interface DataRewinder<T> {
    interface Factory<T> {
      @NonNull
      DataRewinder<T> build(@NonNull T data);
      @NonNull
      Class<T> getDataClass();
    }
    //將包裝的數(shù)據(jù)回退到實例化此對象時的位置舀锨,然后返回重新包裝的數(shù)據(jù)
    @NonNull
    T rewindAndGet() throws IOException;
    //當(dāng)不再需要該復(fù)卷機并可以對其進行清理時調(diào)用蛮位。
    void cleanup();
}

Glide 中的 DataRewinder 模塊設(shè)計也使用的是工廠模式逊谋。

DataClass DataRewinder Factory 添加時機
InputStream InputStreamRewinder InputStreamRewinder.Factory Glide構(gòu)造中注冊
ByteBuffer ByteBufferRewinder ByteBufferRewinder.Factory Glide構(gòu)造中注冊
ALL DefaultRewinder DEFAULT_FACTORY 默認(rèn)值,不用添加

Transition

Transition 不是由Glide進行注冊的蚁廓,而是我們在業(yè)務(wù)代碼中按照需要添加的。它作為Glide組件的一種替蔬,所以我們放在這里來進行介紹告私。

Glide-Transition文檔:https://muyangmin.github.io/glide-docs-cn/doc/transitions.html

Glide 提供了很多的過渡效果,用戶可以手動地應(yīng)用于每個請求承桥。Glide 的內(nèi)置過渡以一致的方式運行驻粟,并且將根據(jù)加載圖像的位置在某些情況下避免運行。

在這里插入圖片描述
//包裝視圖的目標(biāo)將能夠提供所有必要的參數(shù)并開始過渡凶异。
public interface Transition<R> {
    //包含視圖的接口蜀撑,該視圖公開了運行各種類型的必需的方法
    //Glide中所有ViewTarget的子類都實現(xiàn)了該接口
    interface ViewAdapter {
        //返回包裝的view
        View getView();
        //返回在視圖中顯示的當(dāng)前可繪制對象;如果不存在這樣的可繪制對象剩彬,則返回null(或無法檢索)屯掖。
        @Nullable
        Drawable getCurrentDrawable();
        //設(shè)置當(dāng)前可繪制對象(通常是動畫可繪制對象)以在包裝視圖中顯示。
        void setDrawable(Drawable drawable);
    }
    //從當(dāng)前正在使用的上一個Drawable進行動畫處理在給定視圖中顯示襟衰,
    //如果在運行過渡過程中將新資源放在視圖中贴铜,則為True,
    //如果調(diào)用者需要手動將當(dāng)前資源放在視圖上瀑晒,則為false绍坝。
    boolean transition(R current, ViewAdapter adapter);
}
//一個工廠類,可以根據(jù)請求的狀態(tài)產(chǎn)生不同的Transition
public interface TransitionFactory<R> {
    //返回一個新的Transition
    //isFirstResource如果這是要加載到目標(biāo)中的第一個資源苔悦,則為True轩褐。
    Transition<R> build(DataSource dataSource, boolean isFirstResource);
}

Glide使用

我們上面從業(yè)務(wù)代碼中自定義的@GlideModule注解講到了Glide的構(gòu)造,以及Glide中模塊&組件的注冊以及其對應(yīng)的功能和處理邏輯玖详。

接下來我們繼續(xù)根據(jù)業(yè)務(wù)代碼中Glide最常用的代碼把介,來講述Glide的其它部分。

Glide.with(context)
    .load(myUrl)
    .into(imageView);

這段代碼中有我們?nèi)齻€業(yè)務(wù)邏輯參數(shù):context蟋座、myUrl拗踢、imageView

  • context :本次加載圖片的上下文環(huán)境向臀;
  • myUrl :本次需要加載圖片的地址巢墅,也叫數(shù)據(jù);
  • imageView :本次需要加載圖片的View 券膀,也叫目標(biāo)君纫;

RequestManager

Glide.with(context)

根據(jù)這一行代碼我們進行分析:

//com.bumptech.glide.Glide.java
@NonNull
public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
}

Glide.with(context)獲取的是RequestManager

//用于管理和啟動對Glide的請求的類芹彬⌒钏瑁可以使用活動,片段和連接性生命周期事件智能地停止舒帮,啟動和重新啟動請求会喝。
//通過實例化一個新對象或利用Activity和Fragment生命周期內(nèi)置的處理功能進行檢索眨唬,請對Fragment或Activity使用靜態(tài)Glide.load方法。
public class RequestManager
    implements ComponentCallbacks2, LifecycleListener, ModelTypes<RequestBuilder<Drawable>> {
    /***代碼全都省略***/
}

ComponentCallbacks2Android 自帶的內(nèi)存管理的接口好乐,ApplicationActivity 都有實現(xiàn)。主要可以根據(jù)系統(tǒng)的內(nèi)存狀況及時調(diào)整App內(nèi)存占用瓦宜,提升用戶體驗或讓App存活更久蔚万。

LifecycleListener :activity或者fragment的聲明周期監(jiān)聽接口

public interface LifecycleListener {
    void onStart();//fragmetn或者activity的onstart
    void onStop();//fragmetn或者activity的onstop
    void onDestroy();//fragmetn或者activity的ondestroy
}

ModelTypes :通俗的說就是GlideAPI

interface ModelTypes<T> {
    @NonNull
    @CheckResult
    T load(@Nullable Bitmap bitmap);
    T load(@Nullable Drawable drawable);
    T load(@Nullable String string);
    T load(@Nullable Uri uri);
    T load(@Nullable File file);
    T load(@RawRes @DrawableRes @Nullable Integer resourceId);
    T load(@Nullable URL url);
    T load(@Nullable byte[] model);
    T load(@Nullable Object model);
}

RequestManager的獲取

//com.bumptech.glide.manager.RequestManagerRetriever.java
//Application的生命周期單獨管理
private volatile RequestManager applicationManager;
//一個是support包临庇,一個是Android的api反璃,RequestManagerFragment的臨時存儲
//RequestManagerFragment用來管理RequestManager和同步生命周期
final Map<android.app.FragmentManager, RequestManagerFragment> pendingRequestManagerFragments = new HashMap<>();
final Map<FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments = new HashMap<>();
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) {//FragmentActivity
            return get((FragmentActivity) context);
        } else if (context instanceof Activity) {//Activity
            return get((Activity) context);
        } else if (context instanceof ContextWrapper//找到context的basecontext
            && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
            return get(((ContextWrapper) context).getBaseContext());
        }
    }
    return getApplicationManager(context);
}

我們用Fragment 來舉例看看RequestManager的獲取(ActivityFragmentActivity的實現(xiàn)都類似):

public RequestManager get(@NonNull Fragment fragment) {
    Preconditions.checkNotNull(fragment.getContext(),"fragment必須被attch假夺,不能被destroy");
    if (Util.isOnBackgroundThread()) {//如果應(yīng)用在后臺淮蜈,那么切換為Application
        return get(fragment.getContext().getApplicationContext());
    } else {
        FragmentManager fm = fragment.getChildFragmentManager();
        return supportFragmentGet(fragment.getContext(), fm, fragment, fragment.isVisible());
    }
}
private RequestManager supportFragmentGet(
      @NonNull Context context,
      @NonNull FragmentManager fm,
      @Nullable Fragment parentHint,
      boolean isParentVisible) {
    //獲取當(dāng)前上下文中的SupportRequestManagerFragment
    SupportRequestManagerFragment current =
        getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
    //獲取SupportRequestManagerFragment的RequestManager
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
        //給新創(chuàng)建的SupportRequestManagerFragment添加RequestManager
        Glide glide = Glide.get(context);
        requestManager =
            factory.build(
                glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
        current.setRequestManager(requestManager);
    }
    return requestManager;
}
private SupportRequestManagerFragment getSupportRequestManagerFragment(
      @NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
    //先看看當(dāng)前的FragmentManager中有沒有SupportRequestManagerFragment,有就復(fù)用
    SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
        //pendingSupportRequestManagerFragments已卷,只是一個臨時性的存儲,因為每次put之后必定會remove
        //因為add之后立即進行findFragmentByTag是獲取不到的梧田,所以需要臨時存儲
        current = pendingSupportRequestManagerFragments.get(fm);
        if (current == null) {
            //創(chuàng)建新的SupportRequestManagerFragment
            current = new SupportRequestManagerFragment();
            current.setParentFragmentHint(parentHint);  
            //如果fragment已經(jīng)展示,那么添加的SupportRequestManagerFragment也要同步生命周期
            if (isParentVisible) {
                current.getGlideLifecycle().onStart();
            }
            //添加臨時存儲
            pendingSupportRequestManagerFragments.put(fm, current);
            //創(chuàng)建的SupportRequestManagerFragment添加到FragmentManager中
            fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
            //刪除臨時存儲
            handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
        }
    }
    return current;
}

其它功能

執(zhí)行\(zhòng)取消一個請求侧蘸、暫停所有請求裁眯、重啟所有請求等對請求的標(biāo)記,這部分會在Request 詳細(xì)講解讳癌。在說Request 之前先說一下Request需要為誰加載目標(biāo)資源穿稳。

Target

Target :在聲明周期內(nèi)Glide加載資源回調(diào)接口;

BaseTarget :用于加載Resource的基礎(chǔ) Target 大多數(shù)方法的基本或空實現(xiàn)晌坤;

TargetView :為Bitmap添加到View上提供了默認(rèn)實現(xiàn)逢艘,

public interface Target<R> extends LifecycleListener {
    //表示我們希望資源保持其原始的未修改寬度和/或高度
    int SIZE_ORIGINAL = Integer.MIN_VALUE;
    //開始加載
    void onLoadStarted(@Nullable Drawable placeholder);
    //加載失敗
    void onLoadFailed(@Nullable Drawable errorDrawable);
    //資源加載完成時將調(diào)用的方法。
    void onResourceReady(@NonNull R resource, @Nullable Transition<? super R> transition);
    //在取消負(fù)載及其資源釋放時調(diào)用
    void onLoadCleared(@Nullable Drawable placeholder);
    //一種檢索此目標(biāo)大小的方法
    void getSize(@NonNull SizeReadyCallback cb);
    //如果給定的回調(diào)仍保留骤菠,則將其從待處理集中刪除
    void removeCallback(@NonNull SizeReadyCallback cb);
    //設(shè)置為此目標(biāo)保留的當(dāng)前請求
    void setRequest(@Nullable Request request);
    //檢索對此目標(biāo)的當(dāng)前請求
    @Nullable
    Request getRequest();
}

RequestBuilder

Glide.with(context)
    .load(myUrl)

我們繼續(xù)看下一行load代碼:

//com.bumptech.glide.RequestManager.java
public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
}

RequestBuilder通用類它改,可以處理通用資源類型的設(shè)置選項和啟動負(fù)載。GlideRequest雖然繼承了RequestBuilder 但內(nèi)部的實現(xiàn)都是super調(diào)用父類的方法商乎。

//com.bumptech.glide.RequestManager.java
public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as(
      @NonNull Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
}

asDrawable 創(chuàng)建一個請求資源類型為 Drawable.classRequestBuilder 搔课。

//com.bumptech.glide.RequestBuilder.java
public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
}
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;
    isModelSet = true;
    return this;
}

接下來設(shè)置這個 RequestBuilderModelString.classmyUrl

Glide.with(context)
    .load(myUrl)
    .into(imageView);

RequestBuilderinto 方法是 Glide 加載圖片的最后一步截亦。

public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);
    BaseRequestOptions<?> requestOptions = this;
    //下面是根據(jù)view修改requestOptions的屬性
    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
      switch (view.getScaleType()) {
        case CENTER_CROP:
          requestOptions = requestOptions.clone().optionalCenterCrop();
          break;
        case CENTER_INSIDE:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case FIT_CENTER:
        case FIT_START:
        case FIT_END:
          requestOptions = requestOptions.clone().optionalFitCenter();
          break;
        case FIT_XY:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case CENTER:
        case MATRIX:
        default:
          // Do nothing.
      }
    }
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
}

into 方法主要根據(jù) View 的屬性重構(gòu)造 requestOptions 爬泥, 并生成 DrawableImageViewTarget

ViewTarget的生成

public class GlideContext extends ContextWrapper {
    public <X> ViewTarget<ImageView, X> buildImageViewTarget(
        @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
        return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
    }
    /***部分代碼省略***/
}
public class ImageViewTargetFactory {
    @NonNull
    @SuppressWarnings("unchecked")
    public <Z> ViewTarget<ImageView, Z> buildTarget(
        @NonNull ImageView view, @NonNull Class<Z> clazz) {
        if (Bitmap.class.equals(clazz)) {
            return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
        } else if (Drawable.class.isAssignableFrom(clazz)) {
            return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
        } else {
            throw new IllegalArgumentException(
            "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
      }
    }
}

transcodeClass 實際就是上面的 resourceClass 也就是 Drawable.class 崩瓤。

接下來我們繼續(xù)看它的 into 方法:

private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> options,
      Executor callbackExecutor) {
    Preconditions.checkNotNull(target);
    //檢測是否設(shè)置了model
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }
    //創(chuàng)建請求,Executors.mainThreadExecutor()標(biāo)識callback在主線程
    Request request = buildRequest(target, targetListener, options, callbackExecutor);
    Request previous = target.getRequest();
    //如果target上的當(dāng)前請求與上一次的相同袍啡,且請求緩存磁盤并且上一個請求為完成時使用上一個請求
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      //如果請求已完成,請重新開始却桶,以確保重新發(fā)送結(jié)果境输,觸發(fā)RequestListeners和Targets蔗牡。
      //如果請求失敗,將重新開始重新啟動請求嗅剖,使它有另一個完成的機會辩越。
      //如果請求已經(jīng)正在運行,我們可以讓它繼續(xù)運行而不會受到干擾信粮。
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        //使用上一個請求而不是新請求來進行優(yōu)化黔攒,例如跳過設(shè)置占位符,跟蹤和取消跟蹤目標(biāo)并獲取視圖尺寸在單獨的請求中完成强缘。 
        previous.begin();
      }
      return target;
    }
    //清除target之前的請求
    requestManager.clear(target);
    //給target設(shè)置新的請求
    target.setRequest(request);
    //target同步requestmanager的聲明周期督惰,并將request添加到請求列表
    requestManager.track(target, request);
    return target;
}
private boolean isSkipMemoryCacheWithCompletePreviousRequest(
    BaseRequestOptions<?> options, Request previous) {
    return !options.isMemoryCacheable() && previous.isComplete();
}

好吧,終于看見加載圖片的請求了~旅掂!

這里會根據(jù)之前構(gòu)建的一些參數(shù)來生成 Request 赏胚,然后和 ViewTargetRequest 進行比較決定是否重用之前的 Request ,或者清除之前的 Request 商虐。

Request

Request :為 Target 加載資源的請求觉阅。

public interface Request {
    void begin();//開始異步加載
    //防止從以前的請求中加載任何位圖,釋放由此持有的任何資源請求秘车,并將該請求標(biāo)記為具有已取消留拾。
    void clear();
    //暫停請求
    void pause();
    //如果此請求正在運行并且尚未完成或失敗,則返回true鲫尊。
    boolean isRunning();
    //如果請求成功完成痴柔,則返回true
    boolean isComplete();
    //如果請求已清除,則返回true
    boolean isCleared();
    //判斷兩個請求是否相同
    boolean isEquivalentTo(Request other);
}

Request的創(chuàng)建

Request request = buildRequest(target, targetListener, options, callbackExecutor);
  • target : 我們對 ImageView 包裝之后的的 ViewTarget疫向;
  • targetListener :默認(rèn)為null咳蔚;
  • options :為重新根據(jù)view 屬性修改之后的 requestOptions
  • callbackExecutor : 回調(diào)的線程池搔驼,在主線程中執(zhí)行回調(diào)谈火;

生成的Request 實例為 SingleRequest,它是專門為了Target而加載資源的舌涨。

如果需要設(shè)置處理 Error 的請求那么會在 Request 外包一層生成 ErrorRequestCoordinator糯耍。

ErrorRequestCoordinator :運行單個主要的Request直到完成,然后僅在單個主要請求失敗的情況下后退錯誤請求囊嘉;

如果需要設(shè)置處理縮略圖的請求那么會在 Request 外包一層生成 ThumbnailRequestCoordinator温技。

ThumbnailRequestCoordinator :一個協(xié)調(diào)器,用于協(xié)調(diào)兩個單獨的Request扭粱,它們同時加載圖像的小縮略圖版本和圖像的完整尺寸版本舵鳞。

整個GlideRequest 的實現(xiàn)類就SingleRequestErrorRequestCoordinatorThumbnailRequestCoordinator 一共三個琢蛤。

SingleRequest

SingleRequest 不僅實現(xiàn)了 Request 蜓堕,還實現(xiàn)了SizeReadyCallbackResourceCallback 接口抛虏。

SizeReadyCallback :當(dāng)目標(biāo)確定其大小時必須調(diào)用的回調(diào)。對于固定尺寸的目標(biāo)可以同步調(diào)用套才。

public interface SizeReadyCallback {
    void onSizeReady(int width, int height);
}

ResourceCallback :偵聽資源加載成功完成或失敗的回調(diào)迂猴。

public interface ResourceCallback {
  //成功加載資源時調(diào)用。
  void onResourceReady(Resource<?> resource, DataSource dataSource);
  //當(dāng)資源無法成功加載時調(diào)用背伴。
  void onLoadFailed(GlideException e);
  //返回通知單個請求時要使用的鎖
  Object getLock();
}

上面我們閱讀into代碼的時候知道了 request 的執(zhí)行是調(diào)用了RequestManager.track 沸毁。

//com.bumptech.glide.RequestManager
//對request進行追蹤,默認(rèn)的new RequestTracker()挂据;
private final RequestTracker requestTracker;
//對target進行追蹤
private final TargetTracker targetTracker = new TargetTracker();
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
    //添加到RequestManger的target追蹤容器中
    targetTracker.track(target);
    //運行request
    requestTracker.runRequest(request);
}
  • TargetTracker : 保留當(dāng)前對有效的一組Target儿普,并轉(zhuǎn)發(fā)生命周期事件崎逃。
  • RequestTracker :用于跟蹤,取消和重新啟動進行中眉孩,已完成和失敗的請求的類个绍。

Request 已經(jīng)講述了一部分了,之前 RequestManger 部分對于 Request 的請求浪汪、清除巴柿、暫停、重新啟動沒有詳細(xì)講述死遭,現(xiàn)在可以開始^_^广恢。

Request.begin

//com.bumptech.glide.manager.RequestTracker
public void runRequest(@NonNull Request request) {
    //添加到請求容器中
    requests.add(request);
    //如果當(dāng)前的RequestManager不處于暫停狀態(tài),那么直接開始請求
    if (!isPaused) {
        request.begin();
    } else {
        //否則延遲該請求
        request.clear();
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "Paused, delaying request");
        }
         pendingRequests.add(request);
    }
}

接下來看SingleRequestbegin 方法呀潭,開始真正的獲取資源钉迷。

//com.bumptech.glide.manager.RequestTracker
@Override
public void begin() {
    synchronized (requestLock) {
        assertNotCallingCallbacks();//如果已經(jīng)進行了回調(diào),那么拋異常
        stateVerifier.throwIfRecycled();//如果任務(wù)已經(jīng)被回收钠署,那么拋異常
        startTime = LogTime.getLogTime();
        if (model == null) {
            //校驗寬高是否有效
            if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
                width = overrideWidth;
                height = overrideHeight;
            }
            //詳細(xì)的日志級別進行記錄
            int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
            onLoadFailed(new GlideException("Received null model"), logLevel);
            return;
        }
        if (status == Status.RUNNING) {//如果任務(wù)已經(jīng)處于運行狀態(tài)糠聪,那么拋異常
          throw new IllegalArgumentException("Cannot restart a running request");
        }
        //如果任務(wù)已經(jīng)執(zhí)行完成,那么直接回調(diào)加載資源
        if (status == Status.COMPLETE) {
            onResourceReady(resource, DataSource.MEMORY_CACHE);
            return;
        }
        //重新啟動既未完成也未運行的請求谐鼎,可以視為新請求并可以從頭開始再次運行舰蟆。
        status = Status.WAITING_FOR_SIZE;
        if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
           onSizeReady(overrideWidth, overrideHeight);//內(nèi)部有請求資源的實現(xiàn)方法調(diào)用
        } else {
           target.getSize(this);//重新回調(diào)onSizeReady方法
        }
        //加載占位圖
        if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
            && canNotifyStatusChanged()) {
            target.onLoadStarted(getPlaceholderDrawable());
        }
    }
}

已知加載的ViewTarget的寬高才會進一步加載資源。

//com.bumptech.glide.request.SingleRequest
@Override
public void onSizeReady(int width, int height) {
    stateVerifier.throwIfRecycled();//如果任務(wù)已經(jīng)被回收狸棍,那么拋異常
    synchronized (requestLock) {//加鎖身害,進行數(shù)據(jù)請求
        if (status != Status.WAITING_FOR_SIZE) {//必須size確定
            return;
        }
        status = Status.RUNNING;//進入運行狀態(tài)
        //在加載資源之前,對Target的大小應(yīng)用乘數(shù)草戈。對于加載縮略圖或避免加載大量資源非常有用题造。默認(rèn)為1f。
        float sizeMultiplier = requestOptions.getSizeMultiplier();
        this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
        this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
        loadStatus =
                engine.load(
                        glideContext,
                        model,//myUrl
                        requestOptions.getSignature(),//緩存簽名猾瘸,默認(rèn)為EmptySignature
                        this.width,
                        this.height,
                        requestOptions.getResourceClass(),//不設(shè)置decode的話界赔,默認(rèn)為Object
                        transcodeClass,//Drawable.class
                        priority,
                        requestOptions.getDiskCacheStrategy(),
                        requestOptions.getTransformations(),
                        requestOptions.isTransformationRequired(),
                        requestOptions.isScaleOnlyOrNoTransform(),
                        requestOptions.getOptions(),
                        requestOptions.isMemoryCacheable(),
                        requestOptions.getUseUnlimitedSourceGeneratorsPool(),
                        requestOptions.getUseAnimationPool(),
                        requestOptions.getOnlyRetrieveFromCache(),
                        this,
                        callbackExecutor);
        /***部分代碼省略***/
    }
}

Engine.load

//com.bumptech.glide.load.engine.Engine
public <R> LoadStatus load(/***部分代碼省略***/) {
    long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
    EngineKey key = //構(gòu)造緩存的key
            keyFactory.buildKey(
                    model,//數(shù)據(jù)myUrl
                    signature,//簽名
                    width,
                    height,
                    transformations,//轉(zhuǎn)變
                    resourceClass,//Object.class
                    transcodeClass,//Drawable.class
                    options);
    EngineResource<?> memoryResource;
    synchronized (this) {
        //先從緩存中加載
        memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
        if (memoryResource == null) {
            return waitForExistingOrStartNewJob(/***部分代碼省略***/);
        }
    }
    //回調(diào)可以并發(fā)丢习,不需要進行鎖操作
    cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
    return null;
}

內(nèi)存緩存加載

private EngineResource<?> loadFromMemory(EngineKey key, boolean isMemoryCacheable, long startTime) {
    if (!isMemoryCacheable) {//如果不允許使用內(nèi)存,那么直接返回null
        return null;
    }
    EngineResource<?> active = loadFromActiveResources(key);//獲取活躍的資源
    if (active != null) {
        return active;
    }
    EngineResource<?> cached = loadFromCache(key);//獲取內(nèi)存緩存中的資源
    if (cached != null) {
        return cached;
    }
    return null;
}

ActiveResources

ActiveResources :利用持有Resource 弱引用的 HashMap 來進行數(shù)據(jù)保存淮悼。

private EngineResource<?> loadFromActiveResources(Key key) {
    EngineResource<?> active = activeResources.get(key);
    if (active != null) {
        active.acquire();
    }
    return active;
}
  • 添加操作:有兩個觸發(fā)點咐低,一個是從 cache 中讀取的時候會將資源添加到 ActiveResources 。另一個獲取資源完成的時候會添加到 ActiveResources 袜腥。它們有一個相同的點都在使用資源的時候添加到 ActiveRsources 见擦。
  • 刪除操作:EngineResource 在內(nèi)部有一個引用計數(shù)器,每次被獲取的時候都會進行 acquire 自加羹令。被釋放的時候也是會自減鲤屡。當(dāng)引用計數(shù)為0的時候,會被 ActivityResources 釋放福侈,并添加到 MemoryCache 中酒来。

MemoryCache

MemoryCache :同樣使用 LRU 算法,實現(xiàn)類為LruResourceCache 肪凛,它提供了一個 ResourceRemovedListener接口堰汉,當(dāng)有資源從 MemoryCache 中被移除時會回調(diào)其中的方法,Engine 中接收到這個消息后就會進行 Bitmap 的回收操作伟墙。

private EngineResource<?> loadFromCache(Key key) {
    EngineResource<?> cached = getEngineResourceFromCache(key);
    if (cached != null) {
        cached.acquire();
        activeResources.activate(key, cached);
    }
    return cached;
}
private EngineResource<?> getEngineResourceFromCache(Key key) {
    Resource<?> cached = cache.remove(key);
    final EngineResource<?> result;
    if (cached == null) {
        result = null;
    } else if (cached instanceof EngineResource) {
        result = (EngineResource<?>) cached;
    } else {
        result = new EngineResource<>(cached, true, true, key, this);
    }
    return result;
}
@Override
public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
    activeResources.deactivate(cacheKey);
    if (resource.isMemoryCacheable()) {
        cache.put(cacheKey, resource);
    } else {
        resourceRecycler.recycle(resource);
    }
}
  • 添加操作:當(dāng)資源被 ActiveResources 釋放的時候翘鸭;
  • 刪除操作:當(dāng)資源被 ActivityResource 獲取的時候;

以上兩點保證了 ActiveResourceMemoryCache 中的資源是不會有重復(fù)的戳葵。

DecodeJob

DecodeJob :負(fù)責(zé)從緩存的數(shù)據(jù)或原始源解碼資源就乓,并應(yīng)用轉(zhuǎn)換和轉(zhuǎn)碼。
接下來的部分會詳細(xì)講解上圖的每個部分拱烁。

在這里插入圖片描述

磁盤緩存加載

等待創(chuàng)建或者是獲取已經(jīng)存在的加載狀態(tài)档址。

private <R> LoadStatus waitForExistingOrStartNewJob(/***部分代碼省略***/) {
    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);//從hashmap中獲取加載key的工作引擎
    if (current != null) {//如果已經(jīng)存在那么直接返回
        current.addCallback(cb, callbackExecutor);
        return new LoadStatus(cb, current);
    }
    EngineJob<R> engineJob = //通過添加和刪除負(fù)載的回調(diào)并在負(fù)載完成時通知回調(diào)來管理負(fù)載的類。
            engineJobFactory.build(/***部分代碼省略***/);
    DecodeJob<R> decodeJob = //負(fù)責(zé)從緩存的數(shù)據(jù)或原始源解碼資源邻梆,并應(yīng)用轉(zhuǎn)換和轉(zhuǎn)碼守伸。
            decodeJobFactory.build(/***部分代碼省略***/);
    jobs.put(key, engineJob);//添加到hashmap中
    engineJob.addCallback(cb, callbackExecutor);//添加回調(diào)信息
    engineJob.start(decodeJob);//開始工作
    return new LoadStatus(cb, engineJob);
}

選擇執(zhí)行線程,開始執(zhí)行任務(wù):

//com.bumptech.glide.load.engine.EngineJob
public synchronized void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    //根據(jù)磁盤緩存策略浦妄,判斷使用哪個線程池執(zhí)行任務(wù)
    GlideExecutor executor = decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
    executor.execute(decodeJob);
}

diskCacheExecutor 線程池中執(zhí)行 DecoceJob尼摹。

//com.bumptech.glide.load.engine.DecodeJob
public void run() {
    /***部分代碼省略***/
    DataFetcher<?> localFetcher = currentFetcher;
    try {
        if (isCancelled) {//如果任務(wù)已經(jīng)取消,那么進行失敗回調(diào)
            notifyFailed();
            return;
        }
        runWrapped();//開始執(zhí)行任務(wù)
    } catch (CallbackException e) {
        //如果不受Glide控制的回調(diào)引發(fā)異常剂娄,則應(yīng)避免使用Glide下面的特定調(diào)試邏輯蠢涝。 
    } catch (Throwable t) {
        //通知失敗,進行失敗回調(diào)阅懦,拋出異常
    } finally {
        if (localFetcher != null) {
            localFetcher.cleanup();
        }
        GlideTrace.endSection();
    }
}

runWrappedrun 實現(xiàn)的包裝和二。

//com.bumptech.glide.load.engine.DecodeJob
private enum RunReason {
    //初始化
    INITIALIZE,
    //切換執(zhí)行服務(wù)
    SWITCH_TO_SOURCE_SERVICE,
    //獲取數(shù)據(jù)之后的資源編解碼
    DECODE_DATA,
}
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);
    }
}

再講下一步數(shù)據(jù)從磁盤的獲取之前,先看一下我們默認(rèn)的磁盤策略:

public static final DiskCacheStrategy AUTOMATIC =
    new DiskCacheStrategy() {
        @Override
        public boolean isDataCacheable(DataSource dataSource) {
            return dataSource == DataSource.REMOTE;
        }
        @Override
        public boolean isResourceCacheable(
            boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) {
            return ((isFromAlternateCacheKey && dataSource == DataSource.DATA_DISK_CACHE)
                    || dataSource == DataSource.LOCAL)
                && encodeStrategy == EncodeStrategy.TRANSFORMED;
        }
        @Override
        public boolean decodeCachedResource() {
            return true;
        }
        @Override
        public boolean decodeCachedData() {
            return true;
        }
    };
  • 如果數(shù)據(jù)時 REMOTE 類型那么可以進行緩存耳胎;
  • 如果是解碼資源而且Key 不是源數(shù)據(jù)的Key 惯吕,資源為 DATA_DISK_CACHE 的話惕它,那么這個資源可以緩存;
  • 可以緩存解碼資源废登;
  • 可以緩存解碼數(shù)據(jù)淹魄;

從上面我們可以了解到緩存有數(shù)據(jù)緩存解碼后的數(shù)據(jù)緩存

我們接著分析 runWrapped 方法內(nèi)調(diào)用的 getNextStage :

//com.bumptech.glide.load.engine.DecodeJob
private Stage getNextStage(Stage current) {
    switch (current) {
        case INITIALIZE://AUTOMATIC默認(rèn)為true堡距,所以初始化的第一步應(yīng)該檢測Stage.RESOURCE_CACHE
            return diskCacheStrategy.decodeCachedResource()
                ? Stage.RESOURCE_CACHE
                : getNextStage(Stage.RESOURCE_CACHE);
        case RESOURCE_CACHE://AUTOMATIC默認(rèn)為true甲锡,所以初始化的下一步應(yīng)該檢測Stage.DATA_CACHE
            return diskCacheStrategy.decodeCachedData()
                ? Stage.DATA_CACHE
                : getNextStage(Stage.DATA_CACHE);
        case DATA_CACHE:////如果用戶選擇僅從緩存中檢索資源,則從源跳過加載羽戒。否則應(yīng)該檢測Stage.SOURCE
            return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
        case SOURCE:
        case FINISHED:
            return Stage.FINISHED;
        default:
            throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
}

知道了Stage 類型接著分析 runWrapped 方法內(nèi)調(diào)用的 getNextStage :

//com.bumptech.glide.load.engine.DecodeJob
private DataFetcherGenerator getNextGenerator() {
    switch (stage) {
        case RESOURCE_CACHE://解碼之后的資源緩存
            return new ResourceCacheGenerator(decodeHelper, this);
        case DATA_CACHE://源數(shù)據(jù)的緩存
            return new DataCacheGenerator(decodeHelper, this);
        case SOURCE://數(shù)據(jù)源
            return new SourceGenerator(decodeHelper, this);
        case FINISHED:
            return null;
        default:
            throw new IllegalStateException("Unrecognized stage: " + stage);
    }
}

DataFetcherGenerator : 注冊 ModelLoadersModel 的數(shù)據(jù)提取構(gòu)造器缤沦。

interface DataFetcherGenerator {
    //嘗試使用一個新的DataFetcher,則返回true已啟動易稠,否則為false缸废。
    boolean startNext();
    //嘗試取消當(dāng)前正在運行的提取程序
    void cancel();
}

ResourceCacheGeneratorDataCacheGeneratorSourceGenerator 除過實現(xiàn)了DataFetcherGenerator 還實現(xiàn)了 DataCallback 接口缩多。

DataCallback : 數(shù)據(jù)已加載且可用時或加載時必須調(diào)用的回調(diào)失敗呆奕。

interface DataCallback<T> {
    //如果加載成功养晋,則使用加載的數(shù)據(jù)進行調(diào)用衬吆;如果加載失敗,則使用null進行調(diào)用绳泉。
    void onDataReady(@Nullable T data);
    //加載失敗時調(diào)用
    void onLoadFailed(@NonNull Exception e);
}

獲取到DataFetcherGenerator 構(gòu)造器之后執(zhí)行 startNext 逊抡,判斷是否下一步能開始加載數(shù)據(jù)。

//com.bumptech.glide.load.engine.EngineJob
private void runGenerators() {
    currentThread = Thread.currentThread();
    startFetchTime = LogTime.getLogTime();
    boolean isStarted = false;
    while (!isCancelled && currentGenerator != null 
        && !(isStarted = currentGenerator.startNext())) {//執(zhí)行當(dāng)前數(shù)據(jù)構(gòu)造的判斷是否能加載數(shù)據(jù)
        stage = getNextStage(stage);//切換數(shù)據(jù)狀態(tài)
        currentGenerator = getNextGenerator();//獲取新的數(shù)據(jù)構(gòu)造器
        if (stage == Stage.SOURCE) {//需要需要進行源數(shù)據(jù)加載那么切換執(zhí)行線程
            reschedule();
            return;
        }
    }
    //如果已經(jīng)完成或者取消零酪,而且沒開始獲取數(shù)據(jù)冒嫡。那么通知失敗
    if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
        notifyFailed();
    }
    //否則,生成器將開始新的加載四苇,我們希望將其調(diào)回onDataFetcherReady孝凌。
}

ResourceCacheGenerator

ResourceCacheGenerator :從包含降采樣/轉(zhuǎn)換后的資源緩存文件中獲取數(shù)據(jù)。

class ResourceCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback<Object> {    
    /***部分代碼省略***/
    @Override
    public boolean startNext() {
        List<Key> sourceIds = helper.getCacheKeys();//能處理Model(myUrl)的LoadData列表月腋,返回其Key
        if (sourceIds.isEmpty()) {//如果沒有蟀架,那么標(biāo)示Model(myUrl)這個數(shù)據(jù)類型無法處理,直接返回false
            return false;
        }
        //獲取glide中注冊的榆骚,能加載Model(url)片拍,能解碼resourceClass(Drawable),能轉(zhuǎn)碼transcodeClass(Object)的組件集合
        List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses();
        if (resourceClasses.isEmpty()) {//如果沒有適配的組件集合妓肢,而且轉(zhuǎn)碼的類型為文件的話那么拋異常捌省。因為File都不能轉(zhuǎn)碼那么根本無法磁盤緩存
            if (File.class.equals(helper.getTranscodeClass())) {
                return false;
            }
            throw new IllegalStateException(/***部分代碼省略***/);
        }
        while (modelLoaders == null || !hasNextModelLoader()) {//如果還有modelLoader
            resourceClassIndex++;
            //雙重判斷使LoadData列表進行完全遍歷
            if (resourceClassIndex >= resourceClasses.size()) {
                sourceIdIndex++;
                if (sourceIdIndex >= sourceIds.size()) {
                    return false;
                }
                resourceClassIndex = 0;
            }
            Key sourceId = sourceIds.get(sourceIdIndex);
            Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
            Transformation<?> transformation = helper.getTransformation(resourceClass);
            //ResourceCacheKey用于下采樣和轉(zhuǎn)換的資源數(shù)據(jù)的緩存密鑰+任何請求的簽名。
            currentKey =
                new ResourceCacheKey(
                    helper.getArrayPool(),
                    sourceId,//modelLoaddata
                    helper.getSignature(),//簽名
                    helper.getWidth(),//寬
                    helper.getHeight(),//高
                    transformation,//轉(zhuǎn)換
                    resourceClass,//解碼
                    helper.getOptions());
            cacheFile = helper.getDiskCache().get(currentKey);
            //判斷Resource是否有緩存碉钠,如果有那么加載這個緩存文件數(shù)據(jù)
            if (cacheFile != null) {
                sourceKey = sourceId;
                modelLoaders = helper.getModelLoaders(cacheFile);//獲取能加載文件的ModelLoader列表
                modelLoaderIndex = 0;
            }
        }
        loadData = null;
        boolean started = false;
        while (!started && hasNextModelLoader()) {
            ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
            //ModelLoader根據(jù)cacheFile纲缓,width卷拘,height,options構(gòu)造LoadData
            loadData = modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions());
            //該LoadData是否被Glide進行加載過色徘,是否可用
            if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
                started = true;//如果可用恭金,那么使用該LoadData中的DataFetcher進行數(shù)據(jù)獲取
                loadData.fetcher.loadData(helper.getPriority(), this);
            }
        }

        return started;
    }
    @Override
    public void cancel() {
        LoadData<?> local = loadData;
        if (local != null) {
            local.fetcher.cancel();
        }
    }
    @Override//數(shù)據(jù)已經(jīng)準(zhǔn)備好,標(biāo)識數(shù)據(jù)源為RESOURCE_DISK_CACHE
    public void onDataReady(Object data) {
        cb.onDataFetcherReady(
            sourceKey, data, loadData.fetcher, DataSource.RESOURCE_DISK_CACHE, currentKey);
    }
    @Override//加載失敗褂策,標(biāo)識數(shù)據(jù)源為RESOURCE_DISK_CACHE
    public void onLoadFailed(@NonNull Exception e) {
        cb.onDataFetcherFailed(currentKey, e, loadData.fetcher, DataSource.RESOURCE_DISK_CACHE);
    }
}

DataCacheGenerator

DataCacheGenerator : 構(gòu)造來自包含原始未修改源數(shù)據(jù)的緩存文件横腿。

class DataCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback<Object> {
    /***部分代碼省略***/
    @Override
    public boolean startNext() {
        //如果還有modelLoader
        while (modelLoaders == null || !hasNextModelLoader()) {
            sourceIdIndex++;
            //cacheKyes(helper.getCacheKeys)在構(gòu)造的時候傳入,和ResourceCacheGenerator的相同
            if (sourceIdIndex >= cacheKeys.size()) {
                return false;
            }
            Key sourceId = cacheKeys.get(sourceIdIndex);
            //原始源數(shù)據(jù)的緩存鍵和任何請求的簽名斤寂。
            Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
            cacheFile = helper.getDiskCache().get(originalKey);
            //判斷Resource是否有緩存耿焊,如果有那么加載這個緩存文件數(shù)據(jù)
            if (cacheFile != null) {
                this.sourceKey = sourceId;
                modelLoaders = helper.getModelLoaders(cacheFile);//獲取能加載文件的ModelLoader列表
                modelLoaderIndex = 0;
            }
        }
        loadData = null;
        boolean started = false;
        while (!started && hasNextModelLoader()) {
            ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
            //ModelLoader根據(jù)cacheFile,width遍搞,height罗侯,options構(gòu)造LoadData
            loadData = modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions());
            //該LoadData是否被Glide進行加載過,是否可用
            if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
                started = true;//如果可用溪猿,那么使用該LoadData中的DataFetcher進行數(shù)據(jù)獲取
                loadData.fetcher.loadData(helper.getPriority(), this);
            }
        }
        return started;
    }
    @Override
    public void onDataReady(Object data) {//數(shù)據(jù)已經(jīng)準(zhǔn)備好钩杰,標(biāo)識數(shù)據(jù)源為DATA_DISK_CACHE
        cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
    }
    @Override
    public void onLoadFailed(@NonNull Exception e) {//加載失敗,標(biāo)識數(shù)據(jù)源為DATA_DISK_CACHE
        cb.onDataFetcherFailed(sourceKey, e, loadData.fetcher, DataSource.DATA_DISK_CACHE);
    }
}

可以看出來 ResourceCacheGenerator 的實現(xiàn)其實和 DataCacheGenerator 的實現(xiàn)邏輯很相似诊县,只不過是緩存的 Key 不同而已讲弄。

源數(shù)據(jù)請求

SourceGenerator : 根據(jù)磁盤緩存策略,可以先將源數(shù)據(jù)編碼后寫入磁盤依痊,然后再加載從緩存文件而不是直接返回避除。

class SourceGenerator implements DataFetcherGenerator,DataFetcher.DataCallback<Object>,
                DataFetcherGenerator.FetcherReadyCallback {
    private DataCacheGenerator sourceCacheGenerator;//磁盤緩存的數(shù)據(jù)構(gòu)造器
    private Object dataToCache;//需要緩存的數(shù)據(jù)
    private DataCacheKey originalKey;//磁盤緩存的key
    /***部分代碼省略***/
    @Override
    public boolean startNext() {
        if (dataToCache != null) {//是否已經(jīng)擁有緩存的數(shù)據(jù)
            Object data = dataToCache;
            dataToCache = null;
            cacheData(data);//進行據(jù)緩存,緩存后sourceCacheGenerator不為空
        }
        //由SourceGenerator轉(zhuǎn)到DataCacheGenerator(網(wǎng)絡(luò)轉(zhuǎn)磁盤)
        if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
            return true;
        }
        sourceCacheGenerator = null;
        loadData = null;
        boolean started = false;
        while (!started && hasNextModelLoader()) {//如果還有LoadData(model,width胸嘁,height瓶摆,options構(gòu)造LoadData)
            loadData = helper.getLoadData().get(loadDataListIndex++);
            //fetcher.getDataSource()是否可以進行磁盤緩存
            if (loadData != null && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
                    || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {//該LoadData是否被Glide進行加載過,是否可用
                started = true;//如果可用性宏,那么使用該LoadData中的DataFetcher進行數(shù)據(jù)獲取
                loadData.fetcher.loadData(helper.getPriority(), this);
            }
        }
        return started;
    }
    /***部分代碼省略***/
    private void cacheData(Object dataToCache) {
        long startTime = LogTime.getLogTime();
        try {//獲取dataToCache對應(yīng)的解碼器群井,StreamEncoder
            Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
            DataCacheWriter<Object> writer = new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
            originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());//生成數(shù)據(jù)源的key
            helper.getDiskCache().put(originalKey, writer);//將writer寫入到磁盤緩存中,對應(yīng)的key為originalKey
        } finally {
            loadData.fetcher.cleanup();
        }
        //構(gòu)造原始數(shù)據(jù)緩存構(gòu)造器毫胜,轉(zhuǎn)到磁盤讀取數(shù)據(jù)
        sourceCacheGenerator = new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
    }
    @Override//數(shù)據(jù)加載成功回調(diào)书斜,date類型為InputStream
    public void onDataReady(Object data) {DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
        //如果數(shù)據(jù)不為空,而且可以緩存那么機進行SourceGenerator轉(zhuǎn)DataCacheGenerator
        if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
            dataToCache = data;
            cb.reschedule();//切換線程指蚁,切換完之后currentGenerator還是SourceGenerator
        } else {//否早直接將數(shù)據(jù)進行回調(diào)
            cb.onDataFetcherReady(loadData.sourceKey,data,loadData.fetcher,loadData.fetcher.getDataSource(),originalKey);
        }
    }
    @Override//數(shù)據(jù)加載失敗回調(diào)
    public void onLoadFailed(@NonNull Exception e) {
        cb.onDataFetcherFailed(originalKey, e, loadData.fetcher, loadData.fetcher.getDataSource());
    }
    @Override//DataCacheGenerator的數(shù)據(jù)加載成功回調(diào)菩佑,data類型為ByteBuffer
    public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
        // 此數(shù)據(jù)提取程序?qū)奈募屑虞d并提供錯誤的數(shù)據(jù)源,因此請覆蓋使用原始提取程序的數(shù)據(jù)源 
        cb.onDataFetcherReady(sourceKey, data, fetcher, loadData.fetcher.getDataSource(), sourceKey);
    }
    @Override//DataCacheGenerator的數(shù)據(jù)加載失敗回調(diào)
    public void onDataFetcherFailed(Key sourceKey, Exception e, DataFetcher<?> fetcher, DataSource dataSource) {
        cb.onDataFetcherFailed(sourceKey, e, fetcher, loadData.fetcher.getDataSource());
    }
}

SourceGenerator 凝化、DataCacheGeneratorResourceCacheGeneratorstartNext 方法都會尋找合適的 ModelLoader 構(gòu)建對應(yīng)的 LoadData 來加載數(shù)據(jù)稍坯。

現(xiàn)在我們的 ModelmyUrl 一個網(wǎng)絡(luò)圖片地址,在沒有加載過之前ResourceCacheGeneratorDataCacheGenerator 當(dāng)然是沒有緩存的,所以我們這里用SourceGenerator 加載一個網(wǎng)絡(luò)圖片的過程在詳細(xì)講述一下 startNext 中怎么獲取LoadData 進行數(shù)據(jù)加載(其他兩個都實現(xiàn)都類似)瞧哟。

需要注意的是Data已經(jīng)是從磁盤緩存讀出來的 ByteBuffer 混巧,但是DataSource 還是 REMOTE

數(shù)據(jù)解碼和轉(zhuǎn)碼

無論加載的緩存還是其他的數(shù)據(jù)成功后都會回調(diào) onDataFetcherReady 勤揩,然后進行數(shù)據(jù)解碼僚饭。

//com.bumptech.glide.load.engine.DecodeJob
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
    this.currentSourceKey = sourceKey;//url
    this.currentData = data;//ByteBuffer
    this.currentFetcher = fetcher;//ByteBufferFetcher
    this.currentDataSource = dataSource;//REMOTE
    this.currentAttemptingKey = attemptedKey;//url
    //如果當(dāng)前線程不是主線程括丁,那么切換到主線程執(zhí)行數(shù)據(jù)解碼(調(diào)用decodeFromRetrievedData)
    if (Thread.currentThread() != currentThread) {
        runReason = RunReason.DECODE_DATA;
        callback.reschedule(this);
    } else {
        GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
        try {
            decodeFromRetrievedData();//進行數(shù)據(jù)解碼
        } finally {
            GlideTrace.endSection();
        }
    }
}

接下來的步驟:

  1. decodeFromRetrievedData 中調(diào)用 decodeFromData 糠排;
  2. decodeFromData 中調(diào)用 decodeFromFetcher 辛慰;
  3. decodeFromFetcher 獲取 Registry.getLoadPath 以及 Registry.getRewinder

最終是為了獲得 DecodePahtLoadPath 负蠕。

解碼&漸變&轉(zhuǎn)碼

LoadPath :一個或多個 DecodePath 來進行數(shù)據(jù)的解碼蛙埂;

//com.bumptech.glide.Registry
@Nullable
public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> getLoadPath(
      @NonNull Class<Data> dataClass,//ByteBuffer.class
      @NonNull Class<TResource> resourceClass,//Object.class
      @NonNull Class<Transcode> transcodeClass) {//Drawable.class
    LoadPath<Data, TResource, Transcode> result = loadPathCache.get(dataClass, resourceClass, transcodeClass);
    if (loadPathCache.isEmptyLoadPath(result)) {
        return null;
    } else if (result == null) {
        List<DecodePath<Data, TResource, Transcode>> decodePaths = getDecodePaths(dataClass, resourceClass, transcodeClass);
        //可能無法將給定的類型解碼或轉(zhuǎn)碼為所需的類型數(shù)據(jù)類。 
        if (decodePaths.isEmpty()) {
            result = null;
        } else {
            result = new LoadPath<>(dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool);
        }
        loadPathCache.put(dataClass, resourceClass, transcodeClass, result);
    }
    return result;
}

DecodePath :嘗試從給定的數(shù)據(jù)類型解碼和轉(zhuǎn)碼資源類型遮糖;

//com.bumptech.glide.Registry
@NonNull
private <Data, TResource, Transcode> List<DecodePath<Data, TResource, Transcode>> getDecodePaths(
      @NonNull Class<Data> dataClass,
      @NonNull Class<TResource> resourceClass,
      @NonNull Class<Transcode> transcodeClass) {
    List<DecodePath<Data, TResource, Transcode>> decodePaths = new ArrayList<>();
    //GifDrawable,Bitmap,BitmapDrawable
    List<Class<TResource>> registeredResourceClasses = decoderRegistry.getResourceClasses(dataClass, resourceClass);
    for (Class<TResource> registeredResourceClass : registeredResourceClasses) {
        List<Class<Transcode>> registeredTranscodeClasses = transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass);
        for (Class<Transcode> registeredTranscodeClass : registeredTranscodeClasses) {
            List<ResourceDecoder<Data, TResource>> decoders = decoderRegistry.getDecoders(dataClass, registeredResourceClass);
            ResourceTranscoder<TResource, Transcode> transcoder = transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);
            //transcoder=DrawableBytesTranscoder
            DecodePath<Data, TResource, Transcode> path =
                new DecodePath<>(
                    dataClass,//ByteBuffer
                    registeredResourceClass,//GifDrawable/Bitmap/BitmapDrawable
                    registeredTranscodeClass,//Drawable/Drawable/Drawable
                        //ByteBufferGifDecoder/ByteBufferBitmapDecoder/BitmapDrawableDecoder
                    decoders,
                        ///UnitTranscoder/BitmapDrawableTranscoder/UnitTranscoder
                    transcoder,
                    throwableListPool);
            decodePaths.add(path);
        }
    }
    return decodePaths;
}
  1. 解碼的時候調(diào)用 DecodePath.decode 方法绣的,該方法會循環(huán)遍歷內(nèi)部的decoders列表中的 ResourceDecoder 如果ByteBufferBitmapDecoder.decode解碼成功(Bitmap)直接返回。
  2. 繼續(xù)回調(diào)判斷是或否進行transform 如果需要那么 DecodeJob.onResourceDecoded 進行 transform 進行資源的漸變欲账,漸變之后重新構(gòu)造緩存的 Key 將再次進行 Encoder 屡江。
  3. 然后再進行 BitmapDrawableTranscoder.transcode 進行轉(zhuǎn)碼得到 LazyBitmapDrawableResource
  4. 回到 notifyEncodeAndRelease 方法進行資源成功回調(diào)赛不,以及判斷判斷是否需要 Encoder 進行磁盤緩存惩嘉,回調(diào)編碼成功釋放資源。
model-data-resource.png

至此整個資源的獲取過程完成俄删,最后我們獲取出了Drawable 宏怔。

加載&釋放資源

DecodeJob

  • 進行 notifyComplete : 回調(diào) EngineJob.onResourceReady ;

  • 進行 onEncodeComplete :釋放當(dāng)前的 DecodeJob ;

EngineJob

  • 進行 notifyCallbacksOfResult 奏路,回調(diào) incrementPendingCallbacks 進行資源的引用+1 畴椰;
  • 回調(diào) Engine.onEngineJobComplete
  • 執(zhí)行 CallResourceReady 任務(wù)鸽粉,回調(diào) SingleRequest.onResourceReady 斜脂,并進行資源的引用+1;
  • 執(zhí)行 decrementPendingCallbacks 進行資源的引用-1触机;

Engine

  • 將資源添加到 activeResource ;
  • 移除當(dāng)前jobs 中對應(yīng)的 EngineJob;

SingleRequest

  • 判斷&校驗帚戳,獲取資源;
  • 進行 target.onResourceReadt 將資源加載到 View 上儡首。

總結(jié)

因為上一篇文章是 Android-Universal-Image-Loader源碼分析 片任,所以這里主要是結(jié)合 ImageLoader 來和 Glide 進行比較。

功能

  1. Glide 默認(rèn)的網(wǎng)絡(luò)請求和 ImageLoader 相同都是 HttpUrlConnection蔬胯;
  2. GlideImageLoader 都可以添加自定義的網(wǎng)絡(luò)請求比如 OkHttp ;
  3. GlideImageLoader 都支持在圖片加載前獲取圖片的數(shù)據(jù)(圖片的寬对供、高)。
  4. Glide 會根據(jù)請求時會根據(jù)當(dāng)前 context 的聲明周期進行 Request 的管理(粒度為 ApplicationActivity)。這個功能是 ImageLoader 不具備的产场,ImageLoader 只能停止一個請求鹅髓,或者停止所有請求。
  5. GlideImageLoader 都具有加載默認(rèn)圖京景、加載失敗備用圖的功能窿冯。
  6. Glide 具備加載縮略圖的功能,這個功能是 ImageLoader 不具備的确徙。
  7. Glide 可以設(shè)置請求時候的優(yōu)先級醒串,雖然說這個請求優(yōu)先級不是十分嚴(yán)格僅僅是指導(dǎo)來做請求的優(yōu)化處理,但這個功能是 ImageLoader 不具備的鄙皇。
  8. Glide 加載圖片的數(shù)據(jù)可支持多種類型厦凤,ImageLoader 只支持 String
  9. GlideImageLoader 都可以自定義配置圖片加載庫使用的 download 育苟、decoder 较鼓、executorcache 等违柏,只不過 Glide 對自定義模塊配置更加的方便以及粒度更細(xì)博烂。

緩存

  1. ImageLoader 緩存的 key 只能根據(jù)圖片地址進行處理生成。 Glide 的原始數(shù)據(jù)的磁盤緩存的 Key 是由 urlsignature 組成漱竖,資源圖片的緩存(磁盤緩存和內(nèi)存緩存)的 Key 是由圖片的(寬禽篱、高、資源類型馍惹、資源轉(zhuǎn)換類型躺率、資源解碼類型、簽名万矾、model 以及 option)組成而且簽名可以由我們自己的 request 進行設(shè)置悼吱。
  2. Gilde 可以進行請求時的設(shè)置跳過緩存,或者進行 signature 設(shè)置防止緩存失效問題良狈。這個是 ImageLoader 不具備的功能后添。
  3. Glide 進行漸變處理的圖片會被再次緩存,這個功能是 ImageLoader 不具備的薪丁。
  4. Glide 的內(nèi)存緩存分為兩級MemoryCacheActiveResource 遇西,MemoryCacheImageLoader 相同都是使用 LruCacheActiveResource 對正在使用的圖片做了弱引用严嗜,防止使用中的 資源LRU 算法回收掉粱檀。

文章到這里就全部講述完啦,若有其他需要交流的可以留言哦漫玄!茄蚯!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子第队,更是在濱河造成了極大的恐慌哮塞,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凳谦,死亡現(xiàn)場離奇詭異忆畅,居然都是意外死亡,警方通過查閱死者的電腦和手機尸执,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門家凯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人如失,你說我怎么就攤上這事绊诲。” “怎么了褪贵?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵掂之,是天一觀的道長。 經(jīng)常有香客問我脆丁,道長世舰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任槽卫,我火速辦了婚禮跟压,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘歼培。我一直安慰自己震蒋,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布躲庄。 她就那樣靜靜地躺著查剖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪读跷。 梳的紋絲不亂的頭發(fā)上梗搅,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天禾唁,我揣著相機與錄音效览,去河邊找鬼。 笑死荡短,一個胖子當(dāng)著我的面吹牛丐枉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播掘托,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼瘦锹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起弯院,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤辱士,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后听绳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體颂碘,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年椅挣,在試婚紗的時候發(fā)現(xiàn)自己被綠了头岔。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡鼠证,死狀恐怖峡竣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情量九,我是刑警寧澤适掰,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站荠列,受9級特大地震影響攻谁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜弯予,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一戚宦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧锈嫩,春花似錦受楼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至对雪,卻和暖如春河狐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瑟捣。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工馋艺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人迈套。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓捐祠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親桑李。 傳聞我的和親對象是個殘疾皇子踱蛀,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容