知识点:
1、Glide内部的HTTP通讯组件的底层实现是基于HttpUrlConnection实现的。
2、Glide默认的Bitmap格式是RGB_565
,比Picasso的ARGB_8888
格式的内存开销要小一半。
3、RGB565格式(占16位)下每个像素占用2字节,RGB8888 格式(32位)下每个像素占用4字节。【8bit = 1 byte】
4、占用内存 = 宽像素 * 高像素 * 每像素占用的字节数;
思路:要实现下载进度监听,可以将Glide的Http通讯组件替换为OkHttp,然后使用OkHttp的Interceptor拦截器捕获Http通讯的过程,然后通过计算ResponseBody的大小获取进度。
首先:创建一个 OkHttpFetcher ,实现DataFetcher< InputStream >接口
public class OkHttpFetcher implements DataFetcher {
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 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;
}
}
第二步:新建一个OkHttpGlideUrlLoader类,实现ModelLoader
public class OkHttpGlideUrlLoader implements ModelLoader {
private OkHttpClient okHttpClient;
public static class Factory implements ModelLoaderFactory {
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 build(Context context, GenericLoaderFactory factories) {
return new OkHttpGlideUrlLoader(getOkHttpClient());
}
@Override
public void teardown() {
}
}
public OkHttpGlideUrlLoader(OkHttpClient client) {
this.okHttpClient = client;
}
@Override
public DataFetcher getResourceFetcher(GlideUrl model, int width, int height) {
return new OkHttpFetcher(okHttpClient, model);
}
}
第三步:新建一个AppGlideModule类并实现GlideModule接口,然后在registerComponents()方法中将原来的HTTP通讯组件替换为刚创建的OkHttpGlideUrlLoader:
@GlideModule
public class AppGlideModule extends AppGlideModule {
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull
Registry registry) {
super.registerComponents(context, glide, registry);
registry.replace(GlideUrl.class,
InputStream.class,
new OkHttpGlideUrlLoader.Factory());
}
}
第四步:在清单文件中加入Glide的配置
...
以上我们完成了对Gilde中Http通讯组件的替换。接下来我们需要给OkHttp添加拦截器,实现对进度的监听。
第五步:要实现下载进度的监听,我们需要对ResponseBody进行运算:
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;
}
}
}
第六步:我们创建一个拦截器
public class ProgressInterceptor implements Interceptor {
private static List> listeners =
Collections.synchronizedList(new ArrayList>());
public static void addListener(String url, ProgressListener listener) {
if (progressListener == null) return;
if (findProgressListener(progressListener) == null) {
listeners.add(new WeakReference<>(progressListener));
}
}
public static void removeProgressListener(OnProgressListener progressListener) {
if (progressListener == null) return;
WeakReference listener
= findProgressListener(progressListener);
if (listener != null) {
listeners.remove(listener);
}
}
private static WeakReference
findProgressListener(OnProgressListener listener)
{
if (listener == null) return null;
if (listeners == null || listeners.size() == 0) return null;
for (int i = 0; i < listeners.size(); i++) {
WeakReference progressListener = listeners.get(i);
if (progressListener.get() == listener) {
return progressListener;
}
}
return null;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
return response.newBuilder()
.body(newProgressResponseBody(
request.url().toString(),
response.body(),
onProgressListener))
.build();
}
private static final OnProgressListener onProgressListener= new OnProgressListener() {
@Override
public void onProgress(String imageUrl, long bytesRead, long totalBytes, boolean isDone, GlideException exception) {
if (listeners == null || listeners.size() == 0) return;
for (int i = 0; i < listeners.size(); i++) {
WeakReference listener = listeners.get(i);
OnProgressListener progressListener = listener.get();
if (progressListener == null) {
listeners.remove(i);
} else {
progressListener.onProgress(imageUrl, bytesRead, totalBytes, isDone, exception);
}
}
}
};
}
第七步:我们需要将我们的拦截器添加到我们替换的OkHttp中:
@GlideModule
public class AppGlideModule extends AppGlideModule {
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull
Registry registry) {
super.registerComponents(context, glide, registry);
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.addInterceptor(new ProgressInterceptor());
OkHttpClient okHttpClient = builder.build();
registry.replace(GlideUrl.class,
InputStream.class,
new OkHttpGlideUrlLoader.Factory(okHttpClient));
}
}
第八步:要实现链式调用我们还需要:
@GlideExtension
public class AppGlideExtension {
private AppGlideExtension() {
}
/**
* 添加进度监听,实现链式调用
*
* @param options
* @param listener
*/
@GlideOption
public static void addListener(RequestOptions options, OnProgressListener listener) {
ProgressManager.addProgressListener(listener);
}
}
最后:
String imageUrl = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1540810811885&di=9f30b7c9795e4f0ab98135f89d1ad634&imgtype=0&src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fimages%2F20180527%2Ff662cb8eea5e4da3a1d28ba62444bc3e.jpeg";
GlideApp.with(this).
load(imageUrl)
.addListener(new OnProgressListener() {
@Override
public void onProgress(String imageUrl, long bytesRead, long totalBytes, boolean isDone, GlideException exception) {
KLog.e("bytesRead:" + bytesRead + ">>>>totalBytes:" + totalBytes + "是否完成:" + isDone + "----:" + Float.valueOf(bytesRead) / Float.valueOf(totalBytes));
}
}).into(imageView);
以下是log日志
至此我们已经实现Glide加载图片的进度监听。
参考:https://blog.csdn.net/gpf1320253667/article/details/83280951
参考GitHub : https://github.com/sunfusheng/GlideImageView