“我正在参加「掘金·启航计划」” 我们日常开发中图片加载都是基操,本地的,服务器的,资源文件里的,内存的图片都轻而易举。我们常见的文件路径也就 "file://" , "http/https://" 这些路径。但就是今天,在对接一款相机的SDK时,我遇到了一个前所未见的文件路径,我看着demo硬生生的把它装进了ImageLoader去处理,它长这个样子: tutk://fileHandle=20&fileName=G1E2_0018.JPG&fileSize=3280536&thumbnail;这我当时就懵了,我害怕极了,知识盲区啊,这什么东西ImageLoader竟然能加载出来。我先是单纯的想到可能这是什么我没见过的协议而已,不管,ImageLoader这么low的东西能解析图片,Glide更加手拿把掐,那我直接把地址丢给Glide解析。理想很丰满,现实相当骨感:我堂堂Glide竟然不能load source。眉头一皱,发现事情并不简单。
一、发现华点
我百思不得其解,我还去百度tutk是什么协议,是否需要建立什么连接。搜到的知识少之又少还毫不相干。我在demo的几千行代码里反复的扒拉,终于发现,demo竟然背着我在不知名的角落重写了ImageLoader的BaseImageDownloader。打开它重写的类看了下重写的内容,那感觉就像是翻到了我掉垃圾桶里的100克拉大钻戒。其实问题就在于该图片地址并不是真正的用于直接读取的网络图片地址,而是需要传给SDK的接口,接口返回图片的byte[],然后把byte[]转换成BaseImageDownloader重写需要返回的InputStream。当然举一反三,ImageLoader可以,那Glide一样也可以。下面我将就两种方法去介绍怎么把一个前所未见的图片地址变为图片加载到我们的ImageView上。
二、ImageLoader自定义图片请求方式
ImageLoader自定义图片请求方式就是继承BaseImageDownloader类,并且重写该类的getStreamFromOtherSource方法
public class ICatchtekImageDownloader extends BaseImageDownloader{
//*****
@Override
protected InputStream getStreamFromOtherSource(String imageUri, Object extra) throws IOException {
//特殊路径特殊处理,避免其它常规路径特殊处理
if (TutkUriUtil.isTutkUri(imageUri)) {
//自定义获取图片的方式,返回值是InputStream
return getStreamFromTUTK(imageUri, extra);
}
return super.getStreamFromOtherSource(imageUri, extra);
}
//*****
}
复制代码
自定义完我们的下载类后,将我们的下载类配置到ImageLoader中,然后大功告成。
ImageLoaderConfiguration.Builder config = new ImageLoaderConfiguration.Builder(context);
//........其他配置
config.imageDownloader(imageDownloader);
ImageLoader.getInstance().init(config.build());
复制代码
三、Glide自定义图片请求方式
Glide自定义图片请求方式相对来说复杂一点,它需要实现三个接口。
3.1 DataFetcher
DataFetcher的泛型为我们重写后图片数据处理完后的类型,Glide支持ByteBuffer和InputStream类型的数据转为图片,我们以ByteBuffer为例子,如果你的图片获取转InputStream方便些的话用InputStream也是一样的。
public class ICatchtekDataFetcher implements DataFetcher<ByteBuffer> {
private String model;
ICatchtekDataFetcher(String model) {
this.model = model;
}
@Override
public void loadData(Priority priority, DataCallback<? super ByteBuffer> callback) {
//我们自己的实现,最终转为转为ByteBuffer
byte[] data = getBufferFromTUTK(model);
ByteBuffer byteBuffer = ByteBuffer.wrap(data);
callback.onDataReady(byteBuffer);
}
@Override
public void cleanup() {
// Intentionally empty only because we're not opening an InputStream or another I/O resource!
}
@Override
public void cancel() {
// Intentionally empty.
}
@NonNull
@Override
public Class<ByteBuffer> getDataClass() {
return ByteBuffer.class;
}
@NonNull
@Override
public DataSource getDataSource() {
return DataSource.LOCAL;
}
}
复制代码
3.2、ModelLoader<String, ByteBuffer>
String为我们的路径,其实也可以是其它对象,ByteBuffer为解析后的图片数据
public final class ICatchtekModelLoader implements ModelLoader<String, ByteBuffer> {
private static final String DATA_URI_PREFIX = "tutk:";
@Nullable
@Override
public LoadData<ByteBuffer> buildLoadData(String model, int width, int height, Options options) {
return new LoadData<>(new ObjectKey(model), new ICatchtekDataFetcher(model));
}
@Override
public boolean handles(String model) {
//符合特殊路径的才处理 表示是否交由该类处理
return model.startsWith(DATA_URI_PREFIX);
}
}
复制代码
3.3、ModelLoaderFactory<String, ByteBuffer>
String为我们的路径,ByteBuffer为解析后的图片数据
public class ICatchtekModelLoaderFactory implements ModelLoaderFactory<String, ByteBuffer> {
@Override
public ModelLoader<String, ByteBuffer> build(MultiModelLoaderFactory multiFactory) {
return new ICatchtekModelLoader();
}
@Override
public void teardown() {
// Do nothing.
}
}
复制代码
3.4、注册组件
前面三步完成相当于自定义一个我们的组件,现在把我们的组件注册到Glide
@GlideModule
public class MyAppGlideModule extends AppGlideModule {
@Override
public void registerComponents(Context context, Glide glide, Registry registry) {
registry.prepend(String.class, ByteBuffer.class, new ICatchtekModelLoaderFactory());
}
}
复制代码
注解@GlideModule需要添加注解解析依赖
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
复制代码
但是会存在找不到注解的问题,通过issues查到问题是 kotlin annotationProcessor需要换成kapt,大功告成。
四、总结
如果看到奇怪的图片路径出不来图不要慌,首先排查图片的路径来源,是否需要特殊处理,以Glide的例子来说,它是将Base64路径转为图片。其它的情况其实也是一样的思路,不管数据是什么,从哪里来,只有最终能变成ByteBuffer或者InputStream就能加载图片。Glide的ModelLoader不单单是下载的功能,还可以做其他自定义的功能组件,有兴趣的可以看看它的官方组件。欢迎大家批评指正,交流指导。