前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Glide的图片下载进度

Glide的图片下载进度

作者头像
包子388321
发布2020-06-17 10:09:05
1.7K0
发布2020-06-17 10:09:05
举报
文章被收录于专栏:包子的书架

前言

好久没有写简书了,都荒废了自己,今天整理了一下以前的代码和目前现有的项目代码,看了关于gradle图片下载进度的代码,这边整理了Glide3.7.0和Glide4.8.0的图片下载进度的实现

思路分析

Glide下载的进度获取是通过对http请求的Interceptor拦截器进行获取responsebody的获取返回的长度和总长度,进行计算,然后通过接口回调给UI层。

Glide的3.7.0版本的图片下载进度实现

  • gradle的依赖引用
代码语言:javascript
复制
    implementation 'com.github.bumptech.glide:glide:3.7.0'
    implementation 'com.squareup.okhttp3:okhttp:3.9.0'
  • 定义进度回调接口
代码语言:javascript
复制
public interface ProgressListener {
    void onProgress(int progress);
}
  • 实现一个继承responsebody的子类,进行对响应数据长度的计算(Glide使用的是okhttp的网络请求库),在这边其实Source相当于一个输入流InputStream,ProgressSource这个内部类就是对响应数据流进行做计算处理,得出图片下载进度。
代码语言:javascript
复制
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进行响应数据处理
代码语言:javascript
复制
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方法来添加上面定义的拦截器。
代码语言:javascript
复制
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的引用,所以需要手动添加OkHttpGlideUrlLoaderOkHttpFetcher这两个类,如下: OkHttpGlideUrlLoader类
代码语言:javascript
复制
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>
  • 最终调用实现
代码语言:javascript
复制
 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的依赖引用
代码语言:javascript
复制
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的清单文件里面注册
代码语言:javascript
复制
@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的情况
代码语言:javascript
复制
long fullLength = responseBody.contentLength();

即 fullLength 为-1

  • 出现的原因,是因为自家服务端的数据返回采用的是g-zip的格式导致的,或者其他的原因 -解决方法:
代码语言:javascript
复制
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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 思路分析
  • Glide的3.7.0版本的图片下载进度实现
  • Glide的4.8.0版本的图片下载进度实现
  • 出现过的问题
  • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档