Glide(V4)下载进度学习及实践

知识点:

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值