今天给大家分享一下Glide的一些使用技巧。Glide应该是Android APP中使用最为广泛的图片加载框架了,如丝般的顺滑滚动以及方便的链式调用为广大开发者所喜爱。但是开发中,我们总能遇上一些不太普遍的需求,比如列表中显示视频缩略图、显示音频的封面图、apk的图标等等,或者更奇葩的需求。这些场景使用框架默认的调用无法满足。

下面给大家分享一些处理以上需求的方法和技巧。

以下代码中Glide使用的均为4.X版本。尤其是自定义加载部分,4.X版本较之前版本API有些许改动,请注意。

加载视频缩略图

对于视频缩略图的加载,Glide是自带的,只是很多人并不知道,通过RequestOption可以进行设置加载。如下代码:

配置GlideModule用于生成GlideApp

@GlideModule
public class MediaGlideModule extends AppGlideModule {
    @Override
    public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
    
    }
    
}
复制代码

然后使用GlideApp加载视频缩略图

GlideApp.with(context)
        .setDefaultRequestOptions(
                new RequestOptions()
                        .frame(frameTimeMicros)
                        .centerCrop()
                        .error(defaultHolder)
                        .placeholder(defaultHolder))
                        .load(path)
                        .into(view);
复制代码

注意: 方法frame(long frameTimeMicros)需要指定你要获取的时间点,传入的参数是微秒,如果要获取2秒处的帧画面即要传入2000000.

Glide的自定义加载

上面给大家展示了如何使用Glide提供的API加载视频缩略图。但是如果我们想使用Glide去加载更加个性化的数据怎么做呢?比如在一个音乐列表里面要展示所有的音乐封面图,Glide可没提供这种API啊,咋整呢?这就需要用到Glide的自定义加载了。强大的Glide充分考虑到了用户的个性化使用。你可以自定义数据流的获取过程,然后回调给Glide就OK了,其他的什么任务的调度和分发以及缓存依旧是Glide帮你做,这块无需关心。

Glide的自定义加载总结为以下过程:

  1. 定义一个Model类用于包装需要加载的数据
  2. 定义一个Key的实现类,用于实现第一步的Model中的数据的签名用于区分缓存
  3. 定义一个DataFetcher的实现类,用于告诉Glide音频封面如何加载,并把加载结果回调出去
  4. 定义一个ModelLoader的实现类用于包装DataFetcher
  5. 定义一个ModelLoaderFactory的实现类用于生成ModelLoader实例
  6. 将自定义加载配置到AppGlideModule中

看着好像挺麻烦的,是吧。其实代码没多少,如下:

  • 第一步 定义一个包装数据的Model类AudioCover
public class AudioCover {
    final String path;
    public AudioCover(String path) {
        this.path = path;
    }
}
复制代码
  • 第二部 定义一个Key的实现类
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);
    }
}
复制代码
  • 第三部 定义一个DataFetcher的实现类(关键代码-处理封面的获取并回调给Glide)
public class AudioCoverFetcher implements DataFetcher<InputStream> {

    private final AudioCover model;
    private MediaMetadataRetriever mRetriever;

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

    public AudioCover getModel() {
        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() {
        mRetriever.release();
    }
    @Override public void cancel() {
        // cannot cancel
    }

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

    @NonNull
    @Override
    public DataSource getDataSource() {
        return DataSource.LOCAL;
    }
}
复制代码

注意: 方法loadData()是同步的方法,直接处理获取即可。

  • 第四部 定义一个ModelLoader的实现类包装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;
    }
}
复制代码
  • 第五步 定义一个ModelLoaderFactory的实现类用于生成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() {

    }
}
复制代码
  • 第六步 将自定义加载配置到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,
                new AudioCoverLoaderFactory());
    }

}
复制代码

然后就可以愉快的使用了

GlideApp.with(context)
        //使用Model类包装数据即可
        .load(new AudioCover(path))
        .placeholder(defaultHolder)
        .error(defaultHolder)
        .into(view);
复制代码

通过Glide的自定义加载可以实现很多Glide之外的数据流加载,加载部分给了充分的自由度,比如你还可以自定义一个用于获取apk图标加载,又或者你可以在加载时请求某些数据接口而实现更为复杂的加载过程等等。

以上就是Glide的自定义加载使用,有需要的可以参考一下。