美文网首页Android开发图片
Gradle的图片下载进度

Gradle的图片下载进度

作者: JasonChen8888 | 来源:发表于2020-02-27 14:08 被阅读0次

前言

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

思路分析

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

gradle的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的子类,进行对响应数据长度的计算(gradle使用的是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));
    }
}
  • 项目早期使用gradle,并没有引用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);
          }
        });

gradle的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

相关文章

  • Gradle的图片下载进度

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

  • NSURLConnection带有进度的图片下载

    第一步: 继承两个协议 第二步:创建4个属性 第三步:发送请求,开始下载 代理方法 第四步:接收服务器响应 第五步...

  • 使用Picasso加载带饼状进度条的图片

    本文介绍了如何在Picasso中显示图片下载的进度,并实现了一种类似于iOS中ProgressHUD的自定义进度条...

  • SDWebImage 图片下载缓存框架 常用方法及原理

    功能:图片下载、图片缓存、下载进度监听、gif处理等等项目地址:https://github.com/rs/SDW...

  • SDWebImage

    SDWebImage介绍 OS中著名的网络图片处理框架. 包含的功能:图片下载,图片缓存,下载进度监听,GIF处理...

  • SDWebImage

    SDWebImage介绍 iOS中著名的网络图片处理框架. 包含的功能:图片下载,图片缓存,下载进度监听,GIF处...

  • SDWebImage加进度条展示图片下载进度

    #import "ViewController.h"#import "UIImageView+WebCache.h...

  • URLSessionDataDelegate回调函数的执行问题

    最近在项目开发中,想实现一个图片下载并展示进度百分比的功能。很自然的,用到了URLSessionDataTask,...

  • SDWebImage

    一、简介 iOS中著名的牛逼的网络图片处理框架包含的功能:图片下载、图片缓存、下载进度监听、gif处理等等用法极其...

  • Gradle下载加速

    Gradle下载加速 在利用Gradle下载内容的时候,经常会出现,长时间卡在那里,下载的进度条一动不动。 这个时...

网友评论

    本文标题:Gradle的图片下载进度

    本文链接:https://www.haomeiwen.com/subject/hfcthhtx.html