Today I would like to share with you some Glide using skills. Glide is probably the most widely used image loading framework in Android apps, and developers love it for its silky, smooth scrolling and convenient chain calls. In development, however, we encountered some less common requirements, such as a list to display video thumbnails, a cover art to display audio, an APK icon, etc., or even more bizarre requirements. These scenarios cannot be satisfied using the framework’s default calls.

Here are some tips and techniques for dealing with these requirements.

The 4.x version of Glide is used in the following code. In particular, the 4.x version has some API changes from the previous version, please note.

Load the video thumbnail

Glide comes with loading video thumbnails, but many people don’t know that it can be set up to load through RequestOption. The following code:

Configure the GlideModule to generate GlideApp

@GlideModule
public class MediaGlideModule extends AppGlideModule {
    @Override
    public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {}}Copy the code

The video thumbnail is then loaded using GlideApp

GlideApp.with(context)
        .setDefaultRequestOptions(
                new RequestOptions()
                        .frame(frameTimeMicros)
                        .centerCrop()
                        .error(defaultHolder)
                        .placeholder(defaultHolder))
                        .load(path)
                        .into(view);
Copy the code

Note: the frame(long frameTimeMicros) method needs to specify the point in time you want to capture. The parameter is passed in microseconds. If you want to capture a frame at 2 seconds, you need to pass in 2000000.

Custom loading of Glide

This shows you how to load video thumbnails using the API supplied by Glide. But what if we want to use Glide to load more personalized data? Glide doesn’t have an API for showing all the music cover art in a playlist. This requires Glide’s custom loading. Powerful Glide takes full account of the user’s personalized use. You can customize the process of getting the data stream, and then call it back to Glide. Glide still does the scheduling and distribution of other tasks and caching for you. You don’t need to worry about this.

Glide’s custom loading is summarized as the following process:

  1. Define a Model class to wrap the data that needs to be loaded
  2. Define an implementation class for Key that implements the signature of the data in the Model from the first step to be cache-sensitive
  3. Define a DataFetcher implementation class that tells Glide how to load the audio cover and call back the loading results
  4. Define an implementation class for ModelLoader that wraps the DataFetcher
  5. Defines an implementation class for ModelLoaderFactory to generate a ModelLoader instance
  6. Put the custom load configuration into the AppGlideModule

Seems like a lot of trouble, doesn’t it? There is not much code, as follows:

  • The first step is to define a Model class, AudioCover, that wraps the data
public class AudioCover {
    final String path;
    public AudioCover(String path) {
        this.path = path; }}Copy the code
  • The second part defines a Key implementation class
public class AudioCoverSignature implements Key {

    private final File file;
    private StringBuilder stringBuilder;

    public AudioCoverSignature(String path) {
        this.file = new File(path);
        stringBuilder = new StringBuilder();
    }

    @Override public void updateDiskCacheKey(MessageDigest messageDigest) {
        stringBuilder.append(file.lastModified()).append(file.getAbsolutePath());
        byte[] bs = stringBuilder.toString().getBytes();
        messageDigest.update(bs, 0, bs.length); }}Copy the code
  • Part 3: Define an implementation class for a DataFetcher (key code – handles the acquisition of the cover and calls back to Glide)
public class AudioCoverFetcher implements DataFetcher<InputStream> {

    private final AudioCover model;
    private MediaMetadataRetriever mRetriever;

    public AudioCoverFetcher(AudioCover model) {
        this.model = model;
    }

    public AudioCover getModel(a) {
        return model;
    }

    @Override
    public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
        mRetriever = new MediaMetadataRetriever();
        try {
            mRetriever.setDataSource(model.path);
            byte[] picture = mRetriever.getEmbeddedPicture();
            if(picture ! =null) {
                callback.onDataReady(new ByteArrayInputStream(picture));
            } else {
                callback.onLoadFailed(new Exception("load audio cover fail")); }}catch(Exception e){ e.printStackTrace(); }}@Override public void cleanup(a) {
        mRetriever.release();
    }
    @Override public void cancel(a) {
        // cannot cancel
    }

    @NonNull
    @Override
    public Class<InputStream> getDataClass(a) {
        return InputStream.class;
    }

    @NonNull
    @Override
    public DataSource getDataSource(a) {
        returnDataSource.LOCAL; }}Copy the code

Note: the loadData() method is synchronized and can be fetched directly.

  • The fourth part defines an implementation class for the ModelLoader wrapper DataFetcher
public class AudioModuleLoader implements ModelLoader<AudioCover.InputStream> {

    @Nullable
    @Override
    public LoadData<InputStream> buildLoadData(@NonNull AudioCover audioCover, int width, int height, @NonNull Options options) {
        return new LoadData<>(new AudioCoverSignature(audioCover.path), new AudioCoverFetcher(audioCover));
    }

    @Override
    public boolean handles(@NonNull AudioCover audioCover) {
        return true; }}Copy the code
  • Step 5 defines an implementation class for ModelLoaderFactory to generate an instance of ModelLoader
public class AudioCoverLoaderFactory implements ModelLoaderFactory<AudioCover.InputStream> {

    @NonNull
    @Override
    public ModelLoader<AudioCover, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {
        return new AudioModuleLoader();
    }

    @Override
    public void teardown(a) {}}Copy the code
  • Step 6 Put the custom load configuration into the GlideModule
@GlideModule
public class MediaGlideModule extends AppGlideModule {

    @Override
    public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
        super.registerComponents(context, glide, registry);
        registry.append(
                AudioCover.class,
                InputStream.class,
                newAudioCoverLoaderFactory()); }}Copy the code

Then it can be used happily

GlideApp.with(context)
        // Use the Model class to wrap the data
        .load(new AudioCover(path))
        .placeholder(defaultHolder)
        .error(defaultHolder)
        .into(view);
Copy the code

It is possible to load a lot of data streams other than Glide by custom loading. The loading part gives you a lot of freedom, for example, you can also customize a load to get apK ICONS, or you can request some data interface at load time to implement more complex loading processes, etc.

The above is Glide’s custom loading use, there is a need to refer to it.