前言
好久没有写简书了,都荒废了自己,今天整理了一下以前的代码和目前现有的项目代码,看了关于gradle图片下载进度的代码,这边整理了Glide3.7.0和Glide4.8.0的图片下载进度的实现
思路分析
Glide下载的进度获取是通过对http请求的Interceptor拦截器进行获取responsebody的获取返回的长度和总长度,进行计算,然后通过接口回调给UI层。
Glide的3.7.0版本的图片下载进度实现
- gradle的依赖引用
implementation 'com.github.bumptech.glide:glide:3.7.0'
implementation 'com.squareup.okhttp3:okhttp:3.9.0'
- 定义进度回调接口
public interface ProgressListener {
void onProgress(int progress);
}
- 实现一个继承responsebody的子类,进行对响应数据长度的计算(Glide使用的是okhttp的网络请求库),在这边其实Source相当于一个输入流InputStream,ProgressSource这个内部类就是对响应数据流进行做计算处理,得出图片下载进度。
package cn.xxxx.demoset.glide;
import android.util.Log;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;
import okio.ForwardingSource;
import okio.Okio;
import okio.Source;
public class ProgressResponseBody extends ResponseBody {
private static final String TAG = "ProgressResponseBody";
private BufferedSource bufferedSource;
private ResponseBody responseBody;
private ProgressListener listener;
public ProgressResponseBody(String url, ResponseBody responseBody) {
this.responseBody = responseBody;
listener = ProgressInterceptor.LISTENER_MAP.get(url);
}
@Override
public MediaType contentType() {
return responseBody.contentType();
}
@Override
public long contentLength() {
return responseBody.contentLength();
}
@Override
public BufferedSource source() {
if (bufferedSource == null) {
bufferedSource = Okio.buffer(new ProgressSource(responseBody.source()));
}
return bufferedSource;
}
private class ProgressSource extends ForwardingSource {
long totalBytesRead = 0;
int currentProgress;
ProgressSource(Source source) {
super(source);
}
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long bytesRead = super.read(sink, byteCount);
long fullLength = responseBody.contentLength();
if (bytesRead == -1) {
totalBytesRead = fullLength;
} else {
totalBytesRead = bytesRead;
}
int progress = (int) (100f * totalBytesRead / fullLength);
Log.d(TAG, "download progress is " progress);
if (listener != null && progress != currentProgress) {
listener.onProgress(progress);
}
if (listener != null && totalBytesRead == fullLength) {
listener = null;
}
currentProgress = progress;
return bytesRead;
}
}
}
- 定义拦截器ProgressInterceptor, 通过拦截器获取
ResponseBody
对象,再通过我们上面定义的ProgressResponseBody
进行响应数据处理
package cn.xxxx.demoset.glide;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
public class ProgressInterceptor implements Interceptor {
static final Map<String, ProgressListener> LISTENER_MAP = new HashMap<>();
public static void addListener(String url, ProgressListener listener) {
LISTENER_MAP.put(url, listener);
}
public static void removeListener(String url) {
LISTENER_MAP.remove(url);
}
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
String url = request.url().toString();
ResponseBody body = response.body();
Response newResponse = response.newBuilder().body(new ProgressResponseBody(url, body)).build();
return newResponse;
}
}
- 实现一个继承GlideModule类,用来配置 GlideModule 来修改 Glide 的一些初始化配置,这边通过复写
registerComponents
方法来添加上面定义的拦截器。
public class MyGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
}
@Override
public void registerComponents(Context context, Glide glide) {
//添加拦截器到Glide
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.addInterceptor(new ProgressInterceptor());
OkHttpClient okHttpClient = builder.build();
glide.register(GlideUrl.class, InputStream.class, new OkHttpGlideUrlLoader.Factory(okHttpClient));
}
}
- 项目早期使用Glide,并没有引用
com.github.bumptech.glide:okhttp3-integration
的引用,所以需要手动添加OkHttpGlideUrlLoader
和OkHttpFetcher
这两个类,如下: OkHttpGlideUrlLoader类
package cn.xxxx.demoset.glide;
import android.content.Context;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.model.GenericLoaderFactory;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.load.model.ModelLoaderFactory;
import java.io.InputStream;
import okhttp3.OkHttpClient;
public class OkHttpGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {
private OkHttpClient okHttpClient;
public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
private OkHttpClient client;
public Factory() {
}
public Factory(OkHttpClient client) {
this.client = client;
}
private synchronized OkHttpClient getOkHttpClient() {
if (client == null) {
client = new OkHttpClient();
}
return client;
}
@Override
public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
return new OkHttpGlideUrlLoader(getOkHttpClient());
}
@Override
public void teardown() {
}
}
public OkHttpGlideUrlLoader(OkHttpClient client) {
this.okHttpClient = client;
}
@Override
public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
return new OkHttpFetcher(okHttpClient, model);
}
}
OkHttpFetcher类
代码语言:javascript复制package cn.xxxx.demoset.glide;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.util.ContentLengthInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
public class OkHttpFetcher implements DataFetcher<InputStream> {
private final OkHttpClient client;
private final GlideUrl url;
private InputStream stream;
private ResponseBody responseBody;
private volatile boolean isCancelled;
public OkHttpFetcher(OkHttpClient client, GlideUrl url) {
this.client = client;
this.url = url;
}
@Override
public InputStream loadData(Priority priority) throws Exception {
Request.Builder requestBuilder = new Request.Builder()
.url(url.toStringUrl());
for (Map.Entry<String, String> headerEntry : url.getHeaders().entrySet()) {
String key = headerEntry.getKey();
requestBuilder.addHeader(key, headerEntry.getValue());
}
Request request = requestBuilder.build();
if (isCancelled) {
return null;
}
Response response = client.newCall(request).execute();
responseBody = response.body();
if (!response.isSuccessful() || responseBody == null) {
throw new IOException("Request failed with code: " response.code());
}
stream = ContentLengthInputStream.obtain(responseBody.byteStream(),
responseBody.contentLength());
return stream;
}
@Override
public void cleanup() {
try {
if (stream != null) {
stream.close();
}
if (responseBody != null) {
responseBody.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public String getId() {
return url.getCacheKey();
}
@Override
public void cancel() {
isCancelled = true;
}
}
-在AndroidManifest配置MyGlideModule
代码语言:javascript复制 <application
.........
<activity android:name=".ui.image.ImageDownloadActivity" />
<meta-data
android:name="cn.xxxx.demoset.glide.MyGlideModule"
android:value="GlideModule" />
</application>
- 最终调用实现
ProgressInterceptor.addListener(url, new ProgressListener() {
@Override
public void onProgress(int progress) {
progressDialog.setProgress(progress);
}
});
Glide.with(this)
.load(url)
.asGif()
.toBytes()
.into(new SimpleTarget<byte[]>(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) {
@Override
public void onLoadStarted(Drawable placeholder) {
super.onLoadStarted(placeholder);
progressDialog.show();
}
@Override
public void onResourceReady(byte[] bytes, GlideAnimation<? super byte[]> glideAnimation) {
progressDialog.dismiss();
ProgressInterceptor.removeListener(url);
// 下载成功回调函数
// 数据处理方法,保存bytes到文件
File externalCacheDir = ImageDownloadActivity.this.getExternalCacheDir();
String folder = externalCacheDir.getAbsolutePath() File.separator "Pictures";
boolean result = FileUtil.writeFileToCache(bytes, folder, "aa.gif");
if (result) {
Toast.makeText(ImageDownloadActivity.this, "保存成功", Toast.LENGTH_SHORT).show();
String path = folder File.separator "aa.gif";
File file = new File(path);
if (file.exists()) {
Glide.with(ImageDownloadActivity.this)
.load(file).asGif().into(showImage);
}
}
}
@Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
// 下载失败回调
progressDialog.dismiss();
ProgressInterceptor.removeListener(url);
}
});
Glide的4.8.0版本的图片下载进度实现
- gradle的依赖引用
implementation "com.github.bumptech.glide:glide:4.8.0"
annotationProcessor "com.github.bumptech.glide:compiler:4.8.0"
implementation "com.github.bumptech.glide:okhttp3-integration:4.8.0"
- 接口ProgressListener、ProgressResponseBody 和ProgressInterceptor这三个代码同上不再贴了
- 编写GlideModule,实现的代码是一样的,唯一和上面的实现区别是,这边是直接通过注解
@GlideModule
的形式引用,不需要在到AndroidManifest的清单文件里面注册
@GlideModule
public class OkHttpLibraryGlideModule extends AppGlideModule {
@Override
public void registerComponents(Context context, Glide glide, Registry registry) {
//添加拦截器到Glide
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.addInterceptor(new ProgressInterceptor());
OkHttpClient okHttpClient = builder.build();
registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(okHttpClient));
}
@Override
public boolean isManifestParsingEnabled() {
//完全禁用清单解析
return false;
}
}
- 实现调用通上面的一样,不在重复
出现过的问题
- 在开发中出现过,获取数据的总长度位-1的情况
long fullLength = responseBody.contentLength();
即 fullLength 为-1
- 出现的原因,是因为自家服务端的数据返回采用的是g-zip的格式导致的,或者其他的原因 -解决方法:
LazyHeaders.Builder builder = new LazyHeaders.Builder();
//添加当前head头部是为了处理okhttp的responsebody.contentLength()获取到的值为-1
builder.addHeader("Accept-Encoding", "identity");
GlideUrl glideUrl = new GlideUrl(url, builder.build());
File file = Glide.with(context.getApplicationContext()).download(glideUrl)
.submit(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL).get();
结语
以上就是个人在做glide实现图片下载带有进度的全部内容,欢迎各位同学点评,如果问题的dia