Glide是一款优秀的图片加载框架,简单的配置便可以使用起来,为开发者省下了很多的功夫。不过,它没有提供其加载图片进度的api,对于这样的需求,实现起来还真颇费一番周折。

尝试
遇到这个需求,第一反应是网上肯定有人实现过,不妨借鉴一下别人的经验。
Glide加载图片实现进度条效果
可惜,这个实现是基于3.7版本的,4.0版本以上的glide改动比较大,using函数已经被移除了
using()
The using() API was removed in Glide 4 to encourage users to register their components once with a AppGlideModule to avoid object re-use. Rather than creating a new ModelLoader each time you load an image, you register it once in an AppGlideModule and let Glide inspect your model (the object you pass to load()) to figure out when to use your registered ModelLoader.
To make sure you only use your ModelLoader for certain models, implement handles() as shown above to inspect each model and return true only if your ModelLoader should be used.
思考
又要用最新的版本又希望给其增加功能,鱼与熊掌不可兼得?“贪新厌旧”的我怎会轻易放弃。我对加载图片进度的需求再理了一遍,发现可以用其他思路简化一下:
所以,能监听到图片的下载进度就大概是图片的加载进度了。那难道要先下载图片再交给glide去加载?不用,glide支持整合其他网络模块,例如OkHttp,并且如果我们愿意的话,也可以利用其接口实现自己的网络加载模块。
glide官方仓库提供了OkHttp的整合模块,于是乎我去这些代码了寻找一下突破口。
突破
OkHttp模块是非常简单的,只有4个文件,并且文件都不长
首先,用过glide的都知道,继承GlideModule的类是用于设置glide的配置信息和加载模块的,在OkHttpGlideModule里,向系统注册了一个用于加载GlideUrl类型的组件,简单的可以理解为加载网络图片的组件。
public class OkHttpGlideModule implements GlideModule {
public OkHttpGlideModule() {
}
public void applyOptions(Context context, GlideBuilder builder) {
}
public void registerComponents(Context context, Glide glide, Registry registry) {
registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
}
}
OkHttpLoader文件也很简单,实现了ModelLoader接口,这里ModelLoader是glide的抽象的资源loader的表示,例如这里,就是说加载GlideUrl的model,返回InputStream,即图片的输入流。关于Glide的loadData、model、fetch的详细介绍,可以查看 官方文档
OkHttpLoader的最终目的,也就是返回了一个LoadData对象。现在情况明确了,glide框架就是利用这个LoadData对象得到图片的输入流,从而下载图片并经过一系列的解码,裁剪,缓存等操作,最后加载出来的。LoadData的参数有一个OkHttpStreamFetcher,从名字看来,这里一定就是下载图片的地方了,我们继续看下去。
public class OkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> {
private final okhttp3.Call.Factory client;
public OkHttpUrlLoader(okhttp3.Call.Factory client) {
this.client = client;
}
public boolean handles(GlideUrl url) {
return true;
}
public LoadData<InputStream> buildLoadData(GlideUrl model, int width, int height, Options options) {
//返回LoadData对象,泛型为InputStream
return new LoadData(model, new OkHttpStreamFetcher(this.client, model));
}
public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
private static volatile okhttp3.Call.Factory internalClient;
private okhttp3.Call.Factory client;
private static okhttp3.Call.Factory getInternalClient() {
if(internalClient == null) {
Class var0 = OkHttpUrlLoader.Factory.class;
synchronized(OkHttpUrlLoader.Factory.class) {
if(internalClient == null) {
internalClient = new OkHttpClient();
}
}
}
return internalClient;
}
public Factory() {
this(getInternalClient());
}
public Factory(okhttp3.Call.Factory client) {
this.client = client;
}
public ModelLoader<GlideUrl, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new OkHttpUrlLoader(this.client);
}
public void teardown() {
}
}
}
可以看到,所有的重点都在loadData函数里面了,用过OkHttp的同学,有没有觉得好简单?哈哈。
这里直接得到图片的InputStream然后通过回调函数callback.onDataReady()返回了
问题来了,callback的glide框架里传过来的,我们并不能控制,我尝试找了一下调用loadData的地方,结果没有找到。怎么办?看最终的实现吧。
public class OkHttpStreamFetcher implements DataFetcher<InputStream> {
private static final String TAG = "OkHttpFetcher";
private final Factory client;
private final GlideUrl url;
InputStream stream;
ResponseBody responseBody;
private volatile Call call;
public OkHttpStreamFetcher(Factory client, GlideUrl url) {
this.client = client;
this.url = url;
}
public void loadData(Priority priority, final DataCallback<? super InputStream> callback) {
Builder requestBuilder = (new Builder()).url(this.url.toStringUrl());
Iterator request = this.url.getHeaders().entrySet().iterator();
while(request.hasNext()) {
Entry headerEntry = (Entry)request.next();
String key = (String)headerEntry.getKey();
requestBuilder.addHeader(key, (String)headerEntry.getValue());
}
Request request1 = requestBuilder.build();
this.call = this.client.newCall(request1);
this.call.enqueue(new Callback() {
public void onFailure(Call call, IOException e) {
if(Log.isLoggable("OkHttpFetcher", 3)) {
Log.d("OkHttpFetcher", "OkHttp failed to obtain result", e);
}
callback.onLoadFailed(e);
}
public void onResponse(Call call, Response response) throws IOException {
OkHttpStreamFetcher.this.responseBody = response.body();
if(response.isSuccessful()) {
long contentLength = OkHttpStreamFetcher.this.responseBody.contentLength();
OkHttpStreamFetcher.this.stream = ContentLengthInputStream.obtain(OkHttpStreamFetcher.this.responseBody.byteStream(), contentLength);
callback.onDataReady(OkHttpStreamFetcher.this.stream);
} else {
callback.onLoadFailed(new HttpException(response.message(), response.code()));
}
}
});
}
public void cleanup() {
try {
if(this.stream != null) {
this.stream.close();
}
} catch (IOException var2) {
;
}
if(this.responseBody != null) {
this.responseBody.close();
}
}
public void cancel() {
Call local = this.call;
if(local != null) {
local.cancel();
}
}
public Class<InputStream> getDataClass() {
return InputStream.class;
}
public DataSource getDataSource() {
return DataSource.REMOTE;
}
}
实现
在loadData函数里,有这样一句代码
复制代码 代码如下:
OkHttpStreamFetcher.this.stream = ContentLengthInputStream.obtain(OkHttpStreamFetcher.this.responseBody.byteStream(), contentLength);
ContentLengthInputStream是glide的工具类,用来读取输入流的,点进去看看,发现有几个read方法
public synchronized int read() throws IOException {
int value = super.read();
this.checkReadSoFarOrThrow(value >= 0?1:-1);
return value;
}
public int read(byte[] buffer) throws IOException {
return this.read(buffer, 0, buffer.length);
}
public synchronized int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
return this.checkReadSoFarOrThrow(super.read(buffer, byteOffset, byteCount));
}
所以,我的解决方法就是在这里计算下载进度和进行进度的报告的,把这个类从源码里拷贝出来,替换掉OkHttp模块里面的ContentLengthInputStream,并在这插入自己的下载进度逻辑就行了。
具体的代码实现就不贴出来了,写个大概的思路:
下载图片的时候传入图片进度监听,用集合容器,例如map保存监听的实例,key为url,下载过程中计算下载进度后通过url找到对应的回调返回进度,图片加载完毕后将回调从集合了移除(glide提供了图片加载完毕的回调)。
ok,就这样,有不合理的地方欢迎指出,又更好更优雅的实现方式请不吝指教。也希望大家多多支持。
# Glide
# 图片加载进度监听
# Android
# 图片加载
# 详解Android GLide图片加载常用几种方法
# Android图片加载框架Coil的详细使用总结
# Android 官推 kotlin-first 的图片加载库——Coil的使用入门
# Android编程图片加载类ImageLoader定义与用法实例分析
# Android ListView实现ImageLoader图片加载的方法
# Android中RecyclerView 滑动时图片加载的优化
# Android图片加载框架Glide的基本用法介绍
# Android图片加载利器之Picasso基本用法
# 如何在Android中高效管理图片加载
# 加载
# 下载图片
# 回调
# 自己的
# 来了
# 用过
# 移除
# 过程中
# 不吝指教
# 在这里
# 都在
# 鱼与熊掌
# 一句
# 在这
# 就不
# 可以用
# 我对
# 我去
# 一遍
# 也很
相关文章:
建站之星各版本价格是多少?
天河区网站制作公司,广州天河区如何办理身份证?需要什么资料有预约的网站吗?
C#如何使用XPathNavigator高效查询XML
制作网页的网站有哪些,电脑上怎么做网页?
如何在腾讯云服务器上快速搭建个人网站?
详解ASP.NET 生成二维码实例(采用ThoughtWorks.QRCode和QrCode.Net两种方式)
昆明网站制作哪家好,昆明公租房申请网上登录入口?
网站制作大概要多少钱一个,做一个平台网站大概多少钱?
如何选择高效响应式自助建站源码系统?
如何在企业微信快速生成手机电脑官网?
建站之星2.7模板快速切换与批量管理功能操作指南
宠物网站制作html代码,有没有专门介绍宠物如何养的网站啊?
如何快速搭建安全的FTP站点?
C++ static_cast和dynamic_cast区别_C++静态转换与动态类型安全转换
已有域名和空间,如何快速搭建网站?
GML (Geography Markup Language)是什么,它如何用XML来表示地理空间信息?
如何选择PHP开源工具快速搭建网站?
电商网站制作多少钱一个,电子商务公司的网站制作费用计入什么科目?
如何在云主机快速搭建网站站点?
简单实现Android文件上传
如何快速搭建支持数据库操作的智能建站平台?
车管所网站制作流程,交警当场开简易程序处罚决定书,在交警网站查询不到怎么办?
平台云上自主建站:模板化设计与智能工具打造高效网站
如何在服务器上配置二级域名建站?
如何在IIS中新建站点并配置端口与IP地址?
建站为何优先选择香港服务器?
网站制作公司排行榜,四大门户网站排名?
合肥制作网站的公司有哪些,合肥聚美网络科技有限公司介绍?
北京网站制作费用多少,建立一个公司网站的费用.有哪些部分,分别要多少钱?
如何通过智能用户系统一键生成高效建站方案?
ppt制作免费网站有哪些,ppt模板免费下载网站?
长沙企业网站制作哪家好,长沙水业集团官方网站?
招商网站制作流程,网站招商广告语?
营销式网站制作方案,销售哪个网站招聘效果最好?
如何用VPS主机快速搭建个人网站?
单页制作网站有哪些,朋友给我发了一个单页网站,我应该怎么修改才能把他变成自己的呢,请求高手指点迷津?
如何注册花生壳免费域名并搭建个人网站?
建站主机选虚拟主机还是云服务器更好?
建设网站制作价格,怎样建立自己的公司网站?
如何在Windows环境下新建FTP站点并设置权限?
家庭服务器如何搭建个人网站?
导航网站建站方案与优化指南:一站式高效搭建技巧解析
专业网站设计制作公司,如何制作一个企业网站,建设网站的基本步骤有哪些?
如何通过虚拟主机快速完成网站搭建?
如何选择适合PHP云建站的开源框架?
如何在阿里云虚拟机上搭建网站?步骤解析与避坑指南
网站规划与制作是什么,电子商务网站系统规划的内容及步骤是什么?
如何高效完成自助建站业务培训?
如何通过.red域名打造高辨识度品牌网站?
存储型VPS适合搭建中小型网站吗?
*请认真填写需求信息,我们会在24小时内与您取得联系。