Glide源码学习八:实现带进度的Glide图片加载功能
发布日期:2021-05-14 18:09:50 浏览次数:18 分类:精选文章

本文共 9631 字,大约阅读时间需要 32 分钟。

如何在Glide中实现下载进度监听功能

一、项目准备工作

  • 创建新项目

    创建一个名为 GlideProgressTest 的新项目,用于测试Glide的自定义下载进度功能。

  • 添加依赖库

    app/build.gradle 中添加必要的组件依赖:

    dependencies {
    compile 'com.github.bumptech.glide:glide:3.7.0'
    compile 'com.squareup.okhttp3:okhttp:3.9.0'
    }
  • 网络权限声明

    AndroidManifest.xml 文件中添加网络权限:

  • 二、替换Glide的HTTP通讯组件到OkHttp

  • 创建自定义OkHttpFetcher类

    这个类实现 DataFetcher 接口,用于处理HTTP请求:

    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()) {
    requestBuilder.addHeader(headerEntry.getKey(), 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;
    }
    @Override
    public ModelLoader
    build(
    Context context,
    GenericLoaderFactory fyFactory) {
    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);
    }
    }
  • 注册组件到Glide中

    MyGlideModule 类中注册自定义组件:

    public class MyGlideModule implements GlideModule {
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {}
    @Override
    public void registerComponents(Context context, 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模块

    AndroidManifest.xml 中添加模块声明:

  • 三、实现下载进度监听

  • 创建ProgressInterceptor拦截器类

    该类实现 Interceptor 接口,用于拦截HTTP请求并处理进度信息:

    public class ProgressInterceptor implements Interceptor {
    private static final Map
    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(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;
    }
    }
  • 创建ProgressResponseBody类

    该类继承 ResponseBody,用于处理下载流并计算进度:

    public class ProgressResponseBody extends ResponseBody {
    private static final String TAG = "ProgressResponseBody";
    private BufferedSource bufferedSource;
    private ProgressListener listener;
    private ResponseBody responseBody;
    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 {
    private long totalBytesRead;
    private 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);
    boolean hasChanged = progress != currentProgress;
    currentProgress = progress;
    if (listener != null && hasChanged) {
    listener.onProgress(progress);
    }
    return bytesRead;
    }
    }
    }
  • 在Glide中启用拦截器

    修改 MyGlideModule 类,使其在注册组件时启用OkHttp客户端和拦截器:

    public class MyGlideModule implements GlideModule {
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {}
    @Override
    public void registerComponents(Context context, 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));
    }
    }
  • 四、集成进度显示到用户界面

  • 在MainActivity中创建ProgressDialog

    MainActivity 中定义并初始化一个进度条:

    public class MainActivity extends AppCompatActivity {
    String url = "http://guolin.tech/book.png";
    ImageView image;
    ProgressDialog progressDialog;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    image = (ImageView) findViewById(R.id.image);
    progressDialog = new ProgressDialog(this);
    progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
    progressDialog.setMessage("加载中");
    }
    public void loadImage(View view) {
    ProgressInterceptor.addListener(url, new ProgressListener() {
    @Override
    public void onProgress(int progress) {
    progressDialog.setProgress(progress);
    }
    });
    Glide.with(this)
    .load(url)
    .diskCacheStrategy(DiskCacheStrategy.NONE)
    .override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
    .into(new GlideDrawableImageViewTarget(image) {
    @Override
    public void onLoadStarted(Drawable placeholder) {
    super.onLoadStarted(placeholder);
    progressDialog.show();
    }
    @Override
    public void onResourceReady(GlideDrawable resource, GlideAnimation animation) {
    super.onResourceReady(resource, animation);
    progressDialog.dismiss();
    ProgressInterceptor.removeListener(url);
    }
    });
    }
    }
  • 在GlideDrawableImageViewTarget中添加进度显示逻辑

    确保在图片加载开始和完成时更新进度条:

    class GlideDrawableImageViewTarget extends GlideDrawableImageViewTarget {
    GlideDrawableImageViewTarget(ImageView view) {
    super(view);
    }
    @Override
    public void onLoadStarted(Drawable placeholder) {
    super.onLoadStarted(placeholder);
    progressDialog.show();
    }
    @override
    public void onResourceReady(GlideDrawable resource, GlideAnimation animation) {
    super.onResourceReady(resource, animation);
    progressDialog.dismiss();
    ProgressInterceptor.removeListener(url);
    }
    }
  • 五、测试与验证

  • 运行应用程序

    在 Android Studio 中运行应用程序,点击 Load Image 按钮加载图片。

  • 观察清单

    通过 logcat 工具观察下载进度信息,确保进度监听功能正常工作。

  • 测试不同图片类型

    尝试加载不同大小和类型的图片(如GIF图),验证进度计算和显示的准确性。

  • 优化UI布局

    为进度条添加更多自定义样式,如颜色、动画效果,以提升用户体验。

  • 六、总结

    通过以上步骤,我们成功实现了Glide的下载进度监听功能。关键点包括替换Glide的HTTP通讯组件为OkHttp,利用OkHttp的强大拦截器机制实现进度监听,并通过自定义组件集成进度显示到用户界面。这种方法确保了下载进度能够实时反馈给用户,提升用户体验。

    上一篇:Glide源码学习九:带你全面了解Glide 4的用法
    下一篇:Glide源码学习七:自定义模块功能

    发表评论

    最新留言

    留言是一种美德,欢迎回访!
    [***.207.175.100]2025年04月14日 05时58分38秒