Glide的图片下载进度

前言

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

思路分析

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

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

  • gradle的依赖引用
    implementation 'com.github.bumptech.glide:glide:3.7.0'
    implementation 'com.squareup.okhttp3:okhttp:3.9.0'
  • 定义进度回调接口
public interface ProgressListener {
    void onProgress(int progress);
}
  • 实现一个继承responsebody的子类,进行对响应数据长度的计算(Glide使用的是okhttp的网络请求库),在这边其实Source相当于一个输入流InputStream,ProgressSource这个内部类就是对响应数据流进行做计算处理,得出图片下载进度。
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进行响应数据处理
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方法来添加上面定义的拦截器。
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类
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类

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

 <application
        .........
        <activity android:name=".ui.image.ImageDownloadActivity" />
        <meta-data
            android:name="cn.xxxx.demoset.glide.MyGlideModule"
            android:value="GlideModule" />
    </application>
  • 最终调用实现
 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的依赖引用
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的清单文件里面注册
@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的情况
long fullLength = responseBody.contentLength();

即 fullLength 为-1

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 155,704评论 4 358
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,084评论 1 286
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 105,560评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,360评论 0 201
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,686评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,150评论 1 204
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,528评论 2 306
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,256评论 0 193
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,878评论 1 235
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,215评论 2 239
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,757评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,121评论 2 249
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,659评论 3 228
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,932评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,638评论 0 191
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,159评论 2 263
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,112评论 2 258

推荐阅读更多精彩内容

  • 7.1 压缩图片 一、基础知识 1、图片的格式 jpg:最常见的图片格式。色彩还原度比较好,可以支持适当压缩后保持...
    AndroidMaster阅读 2,418评论 0 13
  • 一、简介 在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫Glide的图片加载库,作者是bumptech。这...
    天天大保建阅读 7,303评论 2 28
  • 第36天 主题:请介绍一个自己的起居中的习惯。 我每天早上早起在家吃早饭,是我自怀孕后养成的一个习惯。 我一直有早...
    张茜007er阅读 183评论 0 0
  • 今天,忙碌又充实的一天。忙碌到没时间坐下来拿笔写字拿笔画画,尽管心中惦记,但实在没有一些些空闲时间可以腾出来做我自...
    骑着蜗牛闯天下阅读 507评论 8 14
  • 早上上班的路上,看到路边一个小伙子,身高有一米八左右,皮肤很白净,身穿一身浅灰色运动装。带着一副金边眼镜,...
    盼盼_9ba9阅读 226评论 0 1