知识点:
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<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; } }第二步:新建一个OkHttpGlideUrlLoader类,实现ModelLoader<GlideUrl, InputStream>接口:
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); } }第三步:新建一个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的配置
<application> <meta-data android:name=".extend.glide.AppGlideModule" android:value="GlideModule" /> ... </application>以上我们完成了对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<WeakReference<OnProgressListener>> listeners = Collections.synchronizedList(new ArrayList<WeakReference<OnProgressListener>>()); 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<OnProgressListener> listener = findProgressListener(progressListener); if (listener != null) { listeners.remove(listener); } } private static WeakReference<OnProgressListener> 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<OnProgressListener> 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<OnProgressListener> 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