美文网首页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的图片下载进度

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