天天看點

三思系列:Glide 4.X全方位認知 -- 子產品職責概覽 | Glide(一)

最近在解決一些問題時,進行了一下檢索,發現

絕大多數文章

基于3.X

。Glide從進入4.X也有兩三年了,在3.X的基礎上,發生了很多變化。 所幸我對

4.X

的源碼還比較熟,且Glide的設計也很精彩,索性寫一寫

對4.X的剖析

當然,對于多數讀者而言,因為有一定的基礎知識,這些剖析文章并不是滿地金磚了,可以泛讀查漏。

三思系列是我最新的學習、總結形式,着重于:問題分析、技術積累、視野拓展,了解更多

本文主旨

Glide是一個

龐大

的項目,這一篇旨在對Glide項目4.X版本進行

全方位的認知

具體為:了解Glide處理哪些問題,哪些子產品與之相對應

對早年間 Square公司開源的

Picasso

比較熟悉的讀者而言,不難了解:

Picasso

是一個

小而雅

的架構,聚焦于圖檔加載的以下4點主要問題:

  • 異步過程管理與排程

  • 資源與顯示容器多對多關系

  • 多級緩存

  • 圖檔轉換支援ScaleType

而後起之秀

Glide

已經是一個

博大且細緻

的圖檔異步加載架構。

在Glide 1.X 時期,兩者的關注點基本一緻,随着官方支援Glide的發展,它的關注點也越來越多,包括但不限于:

  • 資源的封裝

  • 資源擷取方式與過程

  • 緩存

  • decode

    encode

    的過程
  • Target的抽象

    ,注:媒體資源擷取的最終目标,例如存為檔案、加載到ImageView中等
  • 圖檔轉換

  • 資源與Target多對多關系

  • 生命周期感覺與控制

仔細思考這些點之後,我們不難發現,Glide已經着眼于解決這樣一個問題:

建構一個系統,對指定的辨別媒體資源的實體,其封裝形式可以自行擴充,執行資源的擷取,并應用記憶體、磁盤進行緩存,封裝圖檔的解碼過程,按指定要求進行

裁切、縮放等圖檔轉換,最終将結果運用到指定的目标。并對整個過程按照Android平台特性進行生命周期管理。

而這個過程中的絕大多數

節點

或者稱之為

切面

,均可進行

外部擴充

這一點我們可以從項目介紹中得到印證:看重點單詞即可

Glide is a fast and efficient open source

media management

and

image loading framework

for Android that wraps

media decoding

,

memory and disk caching

, and

resource pooling

into a simple and easy to use interface.

Glide supports

fetching

,

decoding

, and

displaying

video stills, images, and animated GIFs. Glide includes a flexible API

that allows developers to plug in to almost any network stack. By default Glide uses a custom

HttpUrlConnection

based

stack, but also includes utility libraries plug in to Google’s Volley project or Square’s OkHttp library instead.

Glide’s

primary focus

is on

making scrolling any kind of a list of images

as

smooth and fast

as possible, but Glide is

also effective for

almost any case

where you need to

fetch

,

resize

, and

display

a remote image.

子產品職責劃分,從Glide的建構過程窺一斑

不難了解:一個龐大的系統需要在符合

SRP

的基礎上進行職責劃分,這樣一來,類就會變得很多,建構過程會變得複雜,利用Builder模式可以很好的解決這一問題。

class GlideBuilder {
    public Glide build(@NonNull Context context) {
        if (sourceExecutor == null) {
            sourceExecutor = GlideExecutor.newSourceExecutor();
        }

        if (diskCacheExecutor == null) {
            diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
        }

        if (animationExecutor == null) {
            animationExecutor = GlideExecutor.newAnimationExecutor();
        }

        if (memorySizeCalculator == null) {
            memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
        }

        if (connectivityMonitorFactory == null) {
            connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
        }

        if (bitmapPool == null) {
            int size = memorySizeCalculator.getBitmapPoolSize();
            if (size > 0) {
                bitmapPool = new LruBitmapPool(size);
            } else {
                bitmapPool = new BitmapPoolAdapter();
            }
        }

        if (arrayPool == null) {
            arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
        }

        if (memoryCache == null) {
            memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
        }

        if (diskCacheFactory == null) {
            diskCacheFactory = new InternalCacheDiskCacheFactory(context);
        }

        if (engine == null) {
            engine = new Engine(
                    memoryCache,
                    diskCacheFactory,
                    diskCacheExecutor,
                    sourceExecutor,
                    GlideExecutor.newUnlimitedSourceExecutor(),
                    GlideExecutor.newAnimationExecutor(),
                    isActiveResourceRetentionAllowed);
        }

        RequestManagerRetriever requestManagerRetriever =
                new RequestManagerRetriever(requestManagerFactory);

        return new Glide(
                context,
                engine,
                memoryCache,
                bitmapPool,
                arrayPool,
                requestManagerRetriever,
                connectivityMonitorFactory,
                logLevel,
                defaultRequestOptions.lock(),
                defaultTransitionOptions);
    }
}
           

從這裡我們可以收集到一下有效資訊

  • 緩存與池
    • MemorySizeCalculator
    • LruBitmapPool
    • BitmapPoolAdapter
    • LruArrayPool
    • LruResourceCache
    • InternalCacheDiskCacheFactory
  • 資源擷取
    • RequestManagerRetriever
    • DefaultConnectivityMonitorFactory
  • 核心流程管理
    • Engine
  • 線程池
    • GlideExecutor

再看到構造函數:代碼挺長,略去了部分内容

class Glide {
    Glide(
            @NonNull Context context,
            @NonNull Engine engine,
            @NonNull MemoryCache memoryCache,
            @NonNull BitmapPool bitmapPool,
            @NonNull ArrayPool arrayPool,
            @NonNull RequestManagerRetriever requestManagerRetriever,
            @NonNull ConnectivityMonitorFactory connectivityMonitorFactory,
            int logLevel,
            @NonNull RequestOptions defaultRequestOptions,
            @NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions) {

        this.engine = engine;
        this.bitmapPool = bitmapPool;
        this.arrayPool = arrayPool;
        this.memoryCache = memoryCache;
        this.requestManagerRetriever = requestManagerRetriever;
        this.connectivityMonitorFactory = connectivityMonitorFactory;

        DecodeFormat decodeFormat = defaultRequestOptions.getOptions().get(Downsampler.DECODE_FORMAT);
        bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat);

        final Resources resources = context.getResources();

        registry = new Registry();
        registry.register(new DefaultImageHeaderParser());

        Downsampler downsampler = new Downsampler(registry.getImageHeaderParsers(),
                resources.getDisplayMetrics(), bitmapPool, arrayPool);
        ByteBufferGifDecoder byteBufferGifDecoder =
                new ByteBufferGifDecoder(context, registry.getImageHeaderParsers(), bitmapPool, arrayPool);
        ResourceDecoder<ParcelFileDescriptor, Bitmap> parcelFileDescriptorVideoDecoder =
                VideoDecoder.parcel(bitmapPool);
        ByteBufferBitmapDecoder byteBufferBitmapDecoder = new ByteBufferBitmapDecoder(downsampler);
        StreamBitmapDecoder streamBitmapDecoder = new StreamBitmapDecoder(downsampler, arrayPool);
        ResourceDrawableDecoder resourceDrawableDecoder =
                new ResourceDrawableDecoder(context);
        ResourceLoader.StreamFactory resourceLoaderStreamFactory =
                new ResourceLoader.StreamFactory(resources);
        ResourceLoader.UriFactory resourceLoaderUriFactory =
                new ResourceLoader.UriFactory(resources);
        ResourceLoader.FileDescriptorFactory resourceLoaderFileDescriptorFactory =
                new ResourceLoader.FileDescriptorFactory(resources);
        ResourceLoader.AssetFileDescriptorFactory resourceLoaderAssetFileDescriptorFactory =
                new ResourceLoader.AssetFileDescriptorFactory(resources);
        BitmapEncoder bitmapEncoder = new BitmapEncoder(arrayPool);

        BitmapBytesTranscoder bitmapBytesTranscoder = new BitmapBytesTranscoder();
        GifDrawableBytesTranscoder gifDrawableBytesTranscoder = new GifDrawableBytesTranscoder();

        ContentResolver contentResolver = context.getContentResolver();

        registry
                .append(ByteBuffer.class, new ByteBufferEncoder())
                /*略去一段内容,太長了*/
                .register(GifDrawable.class, byte[].class, gifDrawableBytesTranscoder);

        ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
        glideContext =
                new GlideContext(
                        context,
                        arrayPool,
                        registry,
                        imageViewTargetFactory,
                        defaultRequestOptions,
                        defaultTransitionOptions,
                        engine,
                        logLevel);
    }
}
           

這一段可以分為即可部分:

  • builder 的 field 對應取值指派
  • 建構非開放的

    BitmapPreFiller

    執行個體
  • 建構

    Registry

    執行個體

    registry

  • 建構

    DefaultImageHeaderParser

    ResourceLoader

    Decoder

    ResourceTranscoder

    并将之注冊到

    registry

  • 建構

    GlideContext

    執行個體

    glideContext

按照經驗推斷,Registry 是一個政策系統資料庫,服務于

政策模式

不難了解:Glide的工作環境非常複雜,例如擷取資源,其資源存在位置可能在網絡中的某個位置、 可能在應用的Assets中,也可能在其他位置,對于某一個目标,根據場景不同,存在不同的實作政策

繼續按照經驗推斷,注冊會分為不同的政策組,對應不同的目标,這一點我們稍後再看。

再按照我們常見的使用習慣,追蹤一下初始化過程:泛讀即可

class Glide {
    public static Glide get(@NonNull Context context) {
        if (glide == null) {
            synchronized (Glide.class) {
                if (glide == null) {
                    checkAndInitializeGlide(context);
                }
            }
        }

        return glide;
    }

    private static void checkAndInitializeGlide(@NonNull Context context) {
        //...
        initializeGlide(context);
        //...
    }

    //重載略去
    public static RequestManager with(@NonNull Activity activity) {
        return getRetriever(activity).get(activity);
    }

    @NonNull
    private static RequestManagerRetriever getRetriever(@Nullable Context context) {
        // Context could be null for other reasons (ie the user passes in null), but in practice it will
        // only occur due to errors with the Fragment lifecycle.
        Preconditions.checkNotNull(
                context,
                "You cannot start a load on a not yet attached View or a Fragment where getActivity() "
                        + "returns null (which usually occurs when getActivity() is called before the Fragment "
                        + "is attached or after the Fragment is destroyed).");
        return Glide.get(context).getRequestManagerRetriever();
    }
}
           

按照使用習慣,一般從

Glide.with(...)

開始得到

RequestManager

,當然,也可以以

Glide.get(context).getRequestManagerRetriever()

方式擷取。如果Glide沒有初始化,則會調用

initializeGlide

進行初始化。

跟進一下代碼,泛讀即可

class Glide {
    private static void initializeGlide(@NonNull Context context) {
        initializeGlide(context, new GlideBuilder());
    }

    @SuppressWarnings("deprecation")
    private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
        Context applicationContext = context.getApplicationContext();
        GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
        List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
        if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
            manifestModules = new ManifestParser(applicationContext).parse();
        }

        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;
                }
                if (Log.isLoggable(TAG, Log.DEBUG)) {
                    Log.d(TAG, "AppGlideModule excludes manifest GlideModule: " + current);
                }
                iterator.remove();
            }
        }

        if (Log.isLoggable(TAG, Log.DEBUG)) {
            for (com.bumptech.glide.module.GlideModule glideModule : manifestModules) {
                Log.d(TAG, "Discovered GlideModule from manifest: " + glideModule.getClass());
            }
        }

        RequestManagerRetriever.RequestManagerFactory factory =
                annotationGeneratedModule != null
                        ? annotationGeneratedModule.getRequestManagerFactory() : null;
        builder.setRequestManagerFactory(factory);
        for (com.bumptech.glide.module.GlideModule module : manifestModules) {
            module.applyOptions(applicationContext, builder);
        }
        if (annotationGeneratedModule != null) {
            annotationGeneratedModule.applyOptions(applicationContext, builder);
        }
        Glide glide = builder.build(applicationContext);
        for (com.bumptech.glide.module.GlideModule module : manifestModules) {
            module.registerComponents(applicationContext, glide, glide.registry);
        }
        if (annotationGeneratedModule != null) {
            annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
        }
        applicationContext.registerComponentCallbacks(glide);
        Glide.glide = glide;
    }
}
           

泛讀之後,我們發現主要有

三步

  • 建構GlideBuilder
  • 通過反射,加載

    可能存在

    的自定義子產品,設定到Builder中
  • 建構Glide
思危:如果直接使用GlideBuilder進行建構,則無法直接運用自定義子產品的内容,需要自行注入。

子產品職責劃分,于Register挖細節

上一節中,我們略去了Glide執行個體化過程的一些細節,其實作内容為:向Register注冊了各種子產品,可以泛讀一下源碼:

registry
        .append(ByteBuffer.class,new ByteBufferEncoder())
        .append(InputStream.class,new StreamEncoder(arrayPool))
        /* Bitmaps */
        .append(Registry.BUCKET_BITMAP,ByteBuffer.class,Bitmap.class,byteBufferBitmapDecoder)
        .append(Registry.BUCKET_BITMAP,InputStream.class,Bitmap.class,streamBitmapDecoder)
        .append(
        Registry.BUCKET_BITMAP,
        ParcelFileDescriptor.class,
        Bitmap.class,
        parcelFileDescriptorVideoDecoder)
        .append(
        Registry.BUCKET_BITMAP,
        AssetFileDescriptor.class,
        Bitmap.class,
        VideoDecoder.asset(bitmapPool))
        .append(Bitmap.class,Bitmap.class,UnitModelLoader.Factory.<Bitmap>getInstance())
        .append(
        Registry.BUCKET_BITMAP,Bitmap.class,Bitmap.class,new UnitBitmapDecoder())
        .append(Bitmap.class,bitmapEncoder)
        /* BitmapDrawables */
        .append(
        Registry.BUCKET_BITMAP_DRAWABLE,
        ByteBuffer.class,
        BitmapDrawable.class,
        new BitmapDrawableDecoder<>(resources,byteBufferBitmapDecoder))
        .append(
        Registry.BUCKET_BITMAP_DRAWABLE,
        InputStream.class,
        BitmapDrawable.class,
        new BitmapDrawableDecoder<>(resources,streamBitmapDecoder))
        .append(
        Registry.BUCKET_BITMAP_DRAWABLE,
        ParcelFileDescriptor.class,
        BitmapDrawable.class,
        new BitmapDrawableDecoder<>(resources,parcelFileDescriptorVideoDecoder))
        .append(BitmapDrawable.class,new BitmapDrawableEncoder(bitmapPool,bitmapEncoder))
        /* GIFs */
        .append(
        Registry.BUCKET_GIF,
        InputStream.class,
        GifDrawable.class,
        new StreamGifDecoder(registry.getImageHeaderParsers(),byteBufferGifDecoder,arrayPool))
        .append(Registry.BUCKET_GIF,ByteBuffer.class,GifDrawable.class,byteBufferGifDecoder)
        .append(GifDrawable.class,new GifDrawableEncoder())
        /* GIF Frames */
        // Compilation with Gradle requires the type to be specified for UnitModelLoader here.
        .append(
        GifDecoder.class,GifDecoder.class,UnitModelLoader.Factory.<GifDecoder>getInstance())
        .append(
        Registry.BUCKET_BITMAP,
        GifDecoder.class,
        Bitmap.class,
        new GifFrameResourceDecoder(bitmapPool))
        /* Drawables */
        .append(Uri.class,Drawable.class,resourceDrawableDecoder)
        .append(
        Uri.class,Bitmap.class,new ResourceBitmapDecoder(resourceDrawableDecoder,bitmapPool))
        /* Files */
        .register(new ByteBufferRewinder.Factory())
        .append(File.class,ByteBuffer.class,new ByteBufferFileLoader.Factory())
        .append(File.class,InputStream.class,new FileLoader.StreamFactory())
        .append(File.class,File.class,new FileDecoder())
        .append(File.class,ParcelFileDescriptor.class,new FileLoader.FileDescriptorFactory())
        // Compilation with Gradle requires the type to be specified for UnitModelLoader here.
        .append(File.class,File.class,UnitModelLoader.Factory.<File>getInstance())
        /* Models */
        .register(new InputStreamRewinder.Factory(arrayPool))
        .append(int.class,InputStream.class,resourceLoaderStreamFactory)
        .append(
        int.class,
        ParcelFileDescriptor.class,
        resourceLoaderFileDescriptorFactory)
        .append(Integer.class,InputStream.class,resourceLoaderStreamFactory)
        .append(
        Integer.class,
        ParcelFileDescriptor.class,
        resourceLoaderFileDescriptorFactory)
        .append(Integer.class,Uri.class,resourceLoaderUriFactory)
        .append(
        int.class,
        AssetFileDescriptor.class,
        resourceLoaderAssetFileDescriptorFactory)
        .append(
        Integer.class,
        AssetFileDescriptor.class,
        resourceLoaderAssetFileDescriptorFactory)
        .append(int.class,Uri.class,resourceLoaderUriFactory)
        .append(String.class,InputStream.class,new DataUrlLoader.StreamFactory())
        .append(String.class,InputStream.class,new StringLoader.StreamFactory())
        .append(String.class,ParcelFileDescriptor.class,new StringLoader.FileDescriptorFactory())
        .append(
        String.class,AssetFileDescriptor.class,new StringLoader.AssetFileDescriptorFactory())
        .append(Uri.class,InputStream.class,new HttpUriLoader.Factory())
        .append(Uri.class,InputStream.class,new AssetUriLoader.StreamFactory(context.getAssets()))
        .append(
        Uri.class,
        ParcelFileDescriptor.class,
        new AssetUriLoader.FileDescriptorFactory(context.getAssets()))
        .append(Uri.class,InputStream.class,new MediaStoreImageThumbLoader.Factory(context))
        .append(Uri.class,InputStream.class,new MediaStoreVideoThumbLoader.Factory(context))
        .append(
        Uri.class,
        InputStream.class,
        new UriLoader.StreamFactory(contentResolver))
        .append(
        Uri.class,
        ParcelFileDescriptor.class,
        new UriLoader.FileDescriptorFactory(contentResolver))
        .append(
        Uri.class,
        AssetFileDescriptor.class,
        new UriLoader.AssetFileDescriptorFactory(contentResolver))
        .append(Uri.class,InputStream.class,new UrlUriLoader.StreamFactory())
        .append(URL.class,InputStream.class,new UrlLoader.StreamFactory())
        .append(Uri.class,File.class,new MediaStoreFileLoader.Factory(context))
        .append(GlideUrl.class,InputStream.class,new HttpGlideUrlLoader.Factory())
        .append(byte[].class,ByteBuffer.class,new ByteArrayLoader.ByteBufferFactory())
        .append(byte[].class,InputStream.class,new ByteArrayLoader.StreamFactory())
        .append(Uri.class,Uri.class,UnitModelLoader.Factory.<Uri>getInstance())
        .append(Drawable.class,Drawable.class,UnitModelLoader.Factory.<Drawable>getInstance())
        .append(Drawable.class,Drawable.class,new UnitDrawableDecoder())
        /* Transcoders */
        .register(
        Bitmap.class,
        BitmapDrawable.class,
        new BitmapDrawableTranscoder(resources))
        .register(Bitmap.class,byte[].class,bitmapBytesTranscoder)
        .register(
        Drawable.class,
        byte[].class,
        new DrawableBytesTranscoder(
        bitmapPool,bitmapBytesTranscoder,gifDrawableBytesTranscoder))
        .register(GifDrawable.class,byte[].class,gifDrawableBytesTranscoder);
           

直接講解

這段代碼是

無意義

的,我們從

Register

類入手,分析注冊的内容。

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

    private final ModelLoaderRegistry modelLoaderRegistry;
    private final EncoderRegistry encoderRegistry;
    private final ResourceDecoderRegistry decoderRegistry;
    private final ResourceEncoderRegistry resourceEncoderRegistry;
    private final DataRewinderRegistry dataRewinderRegistry;
    private final TranscoderRegistry transcoderRegistry;
    private final ImageHeaderParserRegistry imageHeaderParserRegistry;

    private final ModelToResourceClassCache modelToResourceClassCache =
            new ModelToResourceClassCache();
    private final LoadPathCache loadPathCache = new LoadPathCache();
    private final Pool<List<Throwable>> throwableListPool = FactoryPools.threadSafeList();

    public Registry() {
        this.modelLoaderRegistry = new ModelLoaderRegistry(throwableListPool);
        this.encoderRegistry = new EncoderRegistry();
        this.decoderRegistry = new ResourceDecoderRegistry();
        this.resourceEncoderRegistry = new ResourceEncoderRegistry();
        this.dataRewinderRegistry = new DataRewinderRegistry();
        this.transcoderRegistry = new TranscoderRegistry();
        this.imageHeaderParserRegistry = new ImageHeaderParserRegistry();
        setResourceDecoderBucketPriorityList(
                Arrays.asList(BUCKET_GIF, BUCKET_BITMAP, BUCKET_BITMAP_DRAWABLE));
    }
    //API略去
}
           

資源Model加載器

modelLoaderRegistry

用于注冊

資源Model加載方式

資訊。例如,資源的定義Model為

Uri

類,注冊一個

ModelLoaderFactory

,生産對應的

ModelLoader

,将

Uri

媒體實體,加載為

InputStream

或者其他形式的

内容

相關類:

  • ModelLoaderFactory

  • ModelLoader

編碼器

encoderRegistry

用于注冊

内容encode方式

資訊。例如,媒體實體已被加載為

InputStream

,注冊一個對應處理的

Encoder<InputStream>

用于處理内容的encode,如寫入磁盤過程

相關類:

Encoder

接口及其實作類

相應的,

resourceEncoderRegistry

用于注冊

Resource 的encode方式

,這些encoder面向

Resource

,相比于

InputStream

等低級形式的編碼内容封裝,

Resource

實作類進行了更進階别的編碼内容封裝。

相關類:

  • ResourceEncoder

    接口及其實作類
  • Resource

    接口及其實作類
  • EncodeStrategy

不難了解:媒體資源的資料封裝存在不同

層級

  • 讀取資料的InputSteam
  • Bitmap,BitmapDrawable

是以 encode過程 對不同層級的封裝類,有不同的處理,當然,encoder可以通過

wrapper

adapter

,

transfer

等形式,利用其它encoder擴充功能

解碼器

decoderRegistry

用于注冊

媒體資料解碼為Resource方式

的資訊。

前面我們提到

modelLoaderRegistry

注冊的資訊中,可以得到

ModelLoader

, 它們負責将

資源Model

加載為

資源内容

,這種

資源内容

InputStream

等低級形式存在即可。

不難了解,這可以有效的解決

類數量爆炸

問題。

腦暴:假定 Model 的類數量為m個,解碼後輸出的形式有n個
  • 不采用這種方式處理,一共需要 m*n 個 decoder 類
  • 采用這種方式處理,一共需要 m+n 個 decoder 類

層級

增加時,差距會更加明顯

按照經驗推斷,Glide在處理decode過程時,采用了

Wrapper/delegate

模式 或者

bridge

模式。
思變:項目中是否可以使用類似方式,處理不同層級之間

資料Bean

轉化

或者

包裝

相關類:

  • ResourceDecoder

    及其實作類
  • Resource

    及其實作類

轉換器

雖然直譯為轉碼器,但是容易概念混淆,按照其功能稱為轉換器

transcoderRegistry

用于注冊

轉化器,及其負責的原始類型和轉換類型

資訊,轉換器将資源從一種形式轉換為另一種形式。例如:

Bitmap -> byte[]

相關類:

  • ResourceTranscoder

    及其實作類
  • Resource

    及其實作類

頭資訊解析器

imageHeaderParserRegistry

用于注冊

頭資訊解析器

資訊

注,部分作業系統為了更簡便的決定檔案的處理方式,利用了檔案擴充名。但檔案實質内容的編碼方式資訊,存儲在檔案頭資訊中,又稱:

File Sigs

檔案魔數

例如 FF D8 代表 Generic JPEG image file,即 JPE, JPEG, JPG

了解更多關于File Sigs内容

相關類:

  • ImageHeaderParser

    及其實作類
  • ImageType

子產品職責總結

通過前文的分析,并查閱Glide源碼,我們可以對子產品職責進行一次初步總結。

在之後的文章中,我們将對具體子產品進行

更細緻

的挖掘,此處總結的類資訊,就是

突破口

考慮到目前收集的資訊

并不全面

,我不打算繪制一個圖,下面的資訊也

并非

十分重要,是以将其作為一個

臨時的備忘錄

即可。

在後續内容中,我會對各個子產品單獨總結成圖

下一篇,我們将對

資源Model加載、擷取流程

進行

深度挖掘

緩存與池

  • MemorySizeCalculator
  • LruBitmapPool
  • BitmapPoolAdapter
  • LruArrayPool
  • LruResourceCache
  • InternalCacheDiskCacheFactory

資源擷取

  • RequestManagerRetriever
  • RequestManager
  • RequestBuilder
  • Request
  • DefaultConnectivityMonitorFactory

核心流程

  • Engine

其他資訊尚不能确定。主要流程的相關資訊如下:

資源Model加載器

  • ModelLoaderFactory

  • ModelLoader

編碼器

  • Encoder

    接口及其實作類
  • ResourceEncoder

    接口及其實作類
  • Resource

    接口及其實作類
  • EncodeStrategy

解碼器

  • ResourceDecoder

    及其實作類
  • Resource

    及其實作類

轉換器

  • ResourceTranscoder

    及其實作類
  • Resource

    及其實作類

頭資訊解析器

  • ImageHeaderParser

    及其實作類
  • ImageType

線程池

  • GlideExecutor

Glide的建立與配置

GlideBuilder

用于建立,

com.bumptech.glide.module

包下的内容用于自定義子產品,諸如:

  • AppGlideModule

  • LibraryGlideModule

以及一些已廢棄的内容,不再贅述

思危、思退、思變

可能有讀者習慣性地拉到尾部,前面提到,結尾處的大篇幅内容都是

備忘錄

性質,而且之後的系列文章會在這些内容的基礎上展開,可讀可不讀

但既然拉到了這裡,就思考一下吧,閱讀優秀的架構,不僅僅可以深入了解架構内容,也可以鍛煉設計能力:

  • 思危:使用GlideBuilder建構Glide執行個體是否有沒有注意到的危險?
  • 思危:項目中再次對Glide實作單例是否存在危險?
  • 思變:項目中是否存在可以借鑒Glide的設計思路的地方?