Glide series - glide registration module analysis

Glide registration module analysis

1, Overview

Glide is an image loading library, with Picasso, Fresco, universal image loader and other libraries of the same type.

stay Glide series (I) - Analysis of glide frame structure In, we analyzed the framework design of Glide. In this paper, we will analyze the Glide registration module, which is helpful for the analysis of the later Request creation process and cache acquisition process.

This article analyzes the following roles into the registration process:

  1. Analyze the registration and acquisition process of DataLoadProvider.
  2. Analyze the registration and acquisition process of ResourceTranscoder.
  3. Analyze the registration and acquisition process of ModelLoader.

This series uses glide 3.7.0, and analyzes it from the following perspectives.

  1. Glide series (I) - Analysis of glide frame structure
  2. Glide series (II) - glide registration module analysis
  3. Glide series (3) - Request construction process analysis
  4. Glide series (IV) - Analysis of glide cache acquisition process
  5. Glide series (5) - how to implement Request lifecycle management
  6. Glide series (6) - how to realize the collaboration of original image and thumbnail Request
  7. Glide series (7) - glide interview
  8. Glide series (VIII) - Analysis of the process of obtaining glide 4.11.0 cache

Glide version: Glide 3.7.0
Github address: https://github.com/bumptech/glide/tree/v3.7.0
Gradle dependency: implementation 'com.github.bumptech.glide:glide:3.7.0'

2, DataLoadProvider

The main function of DataLoadProvider is to provide corresponding codecs. In Glide construction method, DataLoadProvider that supports type mapping (dataClass and resourceClass) will be registered uniformly.

1. DataLoadProviderRegistry registry

DataLoadProviderRegistry

private final Map<MultiClassKey, DataLoadProvider<?, ?>> providers =
        new HashMap<MultiClassKey, DataLoadProvider<?, ?>>();
        
public <T, Z> void register(Class<T> dataClass, 
		Class<Z> resourceClass, DataLoadProvider<T, Z> provider) {
	// Encapsulate the dataClass and resourceClass into a Key value.
    providers.put(new MultiClassKey(dataClass, resourceClass), provider);
}

Summary:

The registered DataLoadProvider uses MultiClassKey, a new object encapsulated by dataClass and resourceClass, as the Key value.

2. Registration of dataloadprovider

Glide

// DataLoadProvider registry
private final DataLoadProviderRegistry dataLoadProviderRegistry;

Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, 
		Context context, DecodeFormat decodeFormat) {
	// The registry of the DataLoadProvider.
    dataLoadProviderRegistry = new DataLoadProviderRegistry();
	
    StreamBitmapDataLoadProvider streamBitmapLoadProvider =
            new StreamBitmapDataLoadProvider(bitmapPool, decodeFormat);
    dataLoadProviderRegistry.register(InputStream.class, Bitmap.class, streamBitmapLoadProvider);

    FileDescriptorBitmapDataLoadProvider fileDescriptorLoadProvider =
            new FileDescriptorBitmapDataLoadProvider(bitmapPool, decodeFormat);
    dataLoadProviderRegistry.register(ParcelFileDescriptor.class, Bitmap.class, fileDescriptorLoadProvider);

    ImageVideoDataLoadProvider imageVideoDataLoadProvider =
            new ImageVideoDataLoadProvider(streamBitmapLoadProvider, fileDescriptorLoadProvider);
    dataLoadProviderRegistry.register(ImageVideoWrapper.class, Bitmap.class, imageVideoDataLoadProvider);

    GifDrawableLoadProvider gifDrawableLoadProvider =
            new GifDrawableLoadProvider(context, bitmapPool);
    dataLoadProviderRegistry.register(InputStream.class, GifDrawable.class, gifDrawableLoadProvider);

    dataLoadProviderRegistry.register(ImageVideoWrapper.class, GifBitmapWrapper.class,
            new ImageVideoGifDrawableLoadProvider(imageVideoDataLoadProvider, 
            		gifDrawableLoadProvider, bitmapPool));

    dataLoadProviderRegistry.register(InputStream.class, File.class, new StreamFileDataLoadProvider());
}

Summary: the relationship between the dataloadproviders registered above is shown in the following figure.

3. Obtaining dataloadprovider

Glide

<T, Z> DataLoadProvider<T, Z> buildDataProvider(Class<T> dataClass, Class<Z> decodedClass) {
    return dataLoadProviderRegistry.get(dataClass, decodedClass);
}

Call elsewhere Glide.buildDataProvider() method, get the matching DataLoadProvider from the registry through the KeyMultiClassKey built by dataclass and decodedclass.

4. Example code

DrawableTypeRequest

private static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide, 
	    ModelLoader<A, InputStream> streamModelLoader,
		// The resourceClass passed in here is of type GifBitmapWrapper.
        ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader, Class<Z> resourceClass,
        Class<R> transcodedClass,
        ResourceTranscoder<Z, R> transcoder) {
	// Omit code
	// So the DataLoadProvider object returned here is ImageVideoGifDrawableLoadProvider.
    DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider = 
    			glide.buildDataProvider(ImageVideoWrapper.class, resourceClass);
    // Omit code
    return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);
}

Summary:

At DrawableTypeRequest.buildProvider() Glide.buildDataProvider() method, the passed in parameter dataClass type is ImageVideoWrapper, and the passed in parameter resourceClass type is GifBitmapWrapper. Therefore, the matching in the registry DataLoadProviderRegistry is ImageVideoGifDrawableLoadProvider (the matching process can be directly seen in the above figure).

3, ResourceTranscoder

1. TranscoderRegistry registry

TranscoderRegistry

private final Map<MultiClassKey, ResourceTranscoder<?, ?>> factories =
        new HashMap<MultiClassKey, ResourceTranscoder<?, ?>>();
            
public <Z, R> void register(Class<Z> decodedClass, Class<R> transcodedClass, 
			ResourceTranscoder<Z, R> transcoder) {
    factories.put(new MultiClassKey(decodedClass, transcodedClass), transcoder);
}

Summary:

Similar to the DataLoadProvider registration method, this method encapsulates the decodedClass and transcodedClass into a MultiClassKey object as the Key, which is stored in the HashMap.

2. Registration of resourcetranscoder

Glide

// Transcoder transcoder registry
private final TranscoderRegistry transcoderRegistry = new TranscoderRegistry();
    
Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, 
		Context context, DecodeFormat decodeFormat) {
    transcoderRegistry.register(Bitmap.class, GlideBitmapDrawable.class,
            new GlideBitmapDrawableTranscoder(context.getResources(), bitmapPool));
    transcoderRegistry.register(GifBitmapWrapper.class, GlideDrawable.class,
            new GifBitmapWrapperDrawableTranscoder(
                    new GlideBitmapDrawableTranscoder(context.getResources(), bitmapPool)));
}

Summary:
The relationship between resourcetranscoders in the TranscoderRegistry registry.

3. Access to resourcetranscoder

Glide

<Z, R> ResourceTranscoder<Z, R> buildTranscoder(Class<Z> decodedClass, 
		Class<R> transcodedClass) {
    return transcoderRegistry.get(decodedClass, transcodedClass);
}

Call elsewhere Glide.buildTranscoder() method, obtain the matching ResourceTranscoder from the registry through the MultiClassKey constructed by the passed decodedclass and transcodedclass types.

4. Example code

DrawableTypeRequest.buildProvider()

private static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide, 
			ModelLoader<A, InputStream> streamModelLoader,
            ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader, Class<Z> resourceClass,
            Class<R> transcodedClass,
            ResourceTranscoder<Z, R> transcoder) {
	// Omit code
	if (transcoder == null) {
		// The resourceClass passed in here is of type GifBitmapWrapper, transcodedClass is of type GlideDrawable,
		// Therefore, GifBitmapWrapperDrawableTranscoder is matched.
        transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
    }
    // Omit code
    return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);
}

At DrawableTypeRequest.buildProvider() Glide.buildTranscoder() method, the passed in parameter resourceClass type is GifBitmapWrapper, and the passed in parameter transcodedClass type is GlideDrawable. Therefore, GifBitmapWrapperDrawableTranscoder is matched in the registry TranscoderRegistry.

4, ModelLoader

The main function of ModelLoader is to return the corresponding DataFetcher object according to the specified load type (that is, load(T type)).

Before analyzing the ModelLoader, let's take a look at the GenericLoaderFactory registration class.

1. GenericLoaderFactory registry

GenericLoaderFactory.register()

// The factory class that constructs the ModelLoader is stored.
Map<Class/*T*/, Map<Class/*Y*/, ModelLoaderFactory/*T, Y*/>> modelClassToResourceFactories =
        new HashMap<Class, Map<Class, ModelLoaderFactory>>();
// Cache the ModelLoader that was created before.
Map<Class/*T*/, Map<Class/*Y*/, ModelLoader/*T, Y*/>> cachedModelLoaders =
        new HashMap<Class, Map<Class, ModelLoader>>();
            
public synchronized <T, Y> ModelLoaderFactory<T, Y> register(Class<T> modelClass, 
		Class<Y> resourceClass, ModelLoaderFactory<T, Y> factory) {
    cachedModelLoaders.clear();
    
	// 1. First obtain a Map from resourceToFactories according to modelClass.
    Map<Class/*Y*/, ModelLoaderFactory/*T, Y*/> resourceToFactories 
    						= modelClassToResourceFactories.get(modelClass);
    if (resourceToFactories == null) {
        resourceToFactories = new HashMap<Class/*Y*/, ModelLoaderFactory/*T, Y*/>();
        modelClassToResourceFactories.put(modelClass, resourceToFactories);
    }
	// 2. Use resourceClass as the Key of the built-in HashMap, and store ModelLoaderFactory as vlaue.
    ModelLoaderFactory/*T, Y*/ previous = resourceToFactories.put(resourceClass, factory);
    // ... omit code

    return previous;
}

Summary:

  1. There are two cache pools in GenericLoaderFactory, one for caching ModelLoaderFactory objects (for building ModelLoader objects), and the other for building ModelLoader objects.
  2. The two cache pools use nested hashmaps for caching. The first layer uses modelClass as Key for storage, and the second layer uses resourceClass as Key for storage.
  3. The data structure of the registry is as follows:

2. Registration of modelloader

Glide

// ModelLoader registry
private final GenericLoaderFactory loaderFactory;

Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, 
			Context context, DecodeFormat decodeFormat) {
	loaderFactory = new GenericLoaderFactory(context);

	register(File.class, ParcelFileDescriptor.class, new FileDescriptorFileLoader.Factory());
    register(File.class, InputStream.class, new StreamFileLoader.Factory());
    register(int.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
    register(int.class, InputStream.class, new StreamResourceLoader.Factory());
    register(Integer.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
    register(Integer.class, InputStream.class, new StreamResourceLoader.Factory());
    register(String.class, ParcelFileDescriptor.class, new FileDescriptorStringLoader.Factory());
    register(String.class, InputStream.class, new StreamStringLoader.Factory());
    register(Uri.class, ParcelFileDescriptor.class, new FileDescriptorUriLoader.Factory());
    register(Uri.class, InputStream.class, new StreamUriLoader.Factory());
    register(URL.class, InputStream.class, new StreamUrlLoader.Factory());
    register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
    register(byte[].class, InputStream.class, new StreamByteArrayLoader.Factory());
}

public <T, Y> void register(Class<T> modelClass, Class<Y> resourceClass, ModelLoaderFactory<T, Y> factory) {
    ModelLoaderFactory<T, Y> removed = loaderFactory.register(modelClass, resourceClass, factory);
    if (removed != null) {
        removed.teardown();
    }
}

The above mapping relationship after the registration of ModelLoader is shown as follows:

3. Obtaining modelloader

Glide.buildModelLoader()

public static <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, 
		Class<Y> resourceClass, Context context) {
    // Gets the specified ModelLoader from the ModelLoader registry.
    return Glide.get(context).getLoaderFactory().buildModelLoader(modelClass, resourceClass);
}

GenericLoaderFactory.buildModelLoader()

public synchronized <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, 
		Class<Y> resourceClass) {
	// 1. Try to fetch from the cachedmoderloaders cache.
    ModelLoader<T, Y> result = getCachedLoader(modelClass, resourceClass);
    if (result != null) {
        if (NULL_MODEL_LOADER.equals(result)) {
            return null;
        } else {
        	// 2. Get the cached ModelLoader and return it directly.
            return result;
        }
    }
	// 3. Try to get the factory class that constructs the ModelLoader from the modelClassToResourceFactories cache.
    final ModelLoaderFactory<T, Y> factory = getFactory(modelClass, resourceClass);
    if (factory != null) {
    	// 4. Create the ModelLoader object.
        result = factory.build(context, this);
        // 5. Save the created ModelLoader object to the cachedmoderloaders cache.
        cacheModelLoader(modelClass, resourceClass, result);
    } else {
        cacheNullLoader(modelClass, resourceClass);
    }
    return result;
}

Summary: there are four steps to find the specified ModelLoader from the registry:

  1. Try to fetch from the cachedModelLoaders cache. If the cached ModelLoader is obtained, it will return directly.
  2. After step 1 is not obtained, try to get the factory class that constructs the ModelLoader from the modelclasstoresourcefactors cache.
  3. Execute the factory class obtained in step 2 to create the ModelLoader object.
  4. Save the ModelLoader object created in step 3 to the cached modelloaders cache.

4. Example code

As we know, Glide supports loading multiple types of data. Let's take a look at RequestManager.load (type T) method.

RequestManager

public DrawableTypeRequest<File> load(File file);
public DrawableTypeRequest<Integer> load(Integer resourceId);
public DrawableTypeRequest<String> load(String string);
public DrawableTypeRequest<Uri> load(Uri uri)
public DrawableTypeRequest<URL> load(URL url)
public <T> DrawableTypeRequest<T> load(T model)
public DrawableTypeRequest<byte[]> load(byte[] model);

// Take String as an example
public DrawableTypeRequest<String> fromString() {
    return loadGeneric(String.class);
}
private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
	// 1. Find streammodeloader
    ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
    // 2. Find FileDescriptorModelLoader
    ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
            Glide.buildFileDescriptorModelLoader(modelClass, context);
	// 3. Associate the found streammodeloader and FileDescriptorModelLoader to DrawableTypeRequest.
    return optionsApplier.apply(
            new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
                    glide, requestTracker, lifecycle, optionsApplier));
}

The following figure shows the flow chart of the streammodeloader call.

The following figure shows the call flow chart of FileDescriptorModelLoader.

Summary:

The calling process of ModelLoader uses the responsibility chain mode, and each ModelLoader holds the next ModelLoader object. If the current ModelLoader cannot process the given data type, it will be handed over to the next ModelLoader for processing until the ModelLoader that can process the data type is found, and the DataFetcher object held by the ModelLoader will be returned.

5, Summary

This article introduces the registration and acquisition process of DataLoadProvider, ResourceTranscoder and ModelLoader. After reading this section, you can remember the following points:

  1. Understand the storage structure (HashMap) of DataLoadProvider and ResourceTranscoder.
  2. Understand the acquisition process of DataLoadProvider and ResourceTranscoder.
  3. Understand the storage structure of ModelLoader (nested HashMap)
  4. Understand the process of getting DataFetcher from ModelLoader (responsibility chain pattern).

Tags: github

Posted on Sun, 07 Jun 2020 23:54:38 -0400 by violinrocker