Glide添加图片下载进度监听

对于项目开发中,如果遇到加载大图,重网络上下载并在本地显示这个大图,会稍微耗时一点,这时,就会想给出一个下载进度的显示,这样用户体验会更好,但是Glide是没有提供下载进度监听的api的,为了解决这个问题,这篇文章的介绍将基于上篇文章 中,对Glide的自定义模块时,使用Okhttp替换了默认的
HttpUrlConnection作为网络情况的组件。基于这个OkHttp,可以通过添加拦截器的方式来计算下载进度,然后通过设置一个监听来监听这个下载进度,这样就达到了监听下载进度的目的。话不多说,上代码:
首先添加okhttp依赖(记得添加网络权限)

// Okhttp库
    compile 'com.squareup.okhttp3:okhttp:3.1.2'

下面是自定义的GlideModule:

import android.content.Context;

import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.load.engine.cache.ExternalCacheDiskCacheFactory;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.module.GlideModule;

import java.io.InputStream;

class MyGlideModule implements GlideModule {
    private final int DISK_CACHE_SIZE = 1024 * 1024 * 100;//缓存是100Mb

    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        builder.setDiskCache(new ExternalCacheDiskCacheFactory(context, DISK_CACHE_SIZE));
    }

    @Override
    public void registerComponents(Context context, Glide glide) {
        glide.register(GlideUrl.class, InputStream.class, new MyOkhttpGlideUrlLoader.Factory());
    }
}

下面是MyOkhttpGlideUrlLoader类的代码实现:


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 MyOkhttpGlideUrlLoader implements ModelLoader {

    private OkHttpClient mOkHttpClient;

    /**
     * The default factory for {@link com.bumptech.glide.load.model.stream.HttpUrlGlideUrlLoader}s.
     */
    public static class Factory implements ModelLoaderFactory {
        private OkHttpClient mClient;

        public Factory(){

        }

        @Override
        public ModelLoader build(Context context, GenericLoaderFactory factories) {
            return new MyOkhttpGlideUrlLoader(getOkHttpClient());
        }

        @Override
        public void teardown() {
            // Do nothing.
        }

        private synchronized  OkHttpClient getOkHttpClient(){
            if(null == mClient){
                OkHttpClient.Builder builder = new OkHttpClient.Builder();
		//添加拦截器
                builder.addInterceptor(new MyProgressInterceptor());
                mClient = builder.build();
            }
            return mClient;
        }
    }

    public MyOkhttpGlideUrlLoader(OkHttpClient okHttpClient) {
        this.mOkHttpClient = okHttpClient;
    }

    @Override
    public DataFetcher getResourceFetcher(GlideUrl model, int width, int height) {
        return new MyOkhttpUrlFetcher(model,mOkHttpClient);
    }

}

在这个自定义的MyOkhttpGlideUrlLoader类中,创建OkHttpClient对象时,添加了一个MyProgressInterceptor的拦截器,对网络请求进行拦截。下面看看具体的实现:


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 MyProgressInterceptor implements Interceptor {

    static final Map listeners = new HashMap();

    static void addListener(String url ,MyProgressListener progressListener){
        listeners.put(url,progressListener);
    }

    static void removeListener(String url){
        listeners.remove(url);
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Response response = chain.proceed(request);
        ResponseBody body = response.body();
        MyProgressBody myProgressBody = new MyProgressBody(request.url().toString(),body);
        Response newResponse = response.newBuilder().body(myProgressBody).build();
        return newResponse;
    }
}

在拦截器中,定义了两个静态方法,分别是添加监听和移除监听的方法。添加监听的方法是根据不同图片的url来作为key,来存储每个不同的图片的监听的。这样处理的好处是,可以对不同的图片的监听做各自的处理。下面是监听的接口实现:

public interface MyProgressListener {
    void onProgress(int progress);
}

很简单,就是一个接口。

intercept方法中,对返回的结果进行了包装,创建了一个MyProgressBody类,这个是继承自ResponseBody的,下面是具体实现:

import okhttp3.MediaType;
import okhttp3.ResponseBody;
import okio.BufferedSource;
import okio.Okio;

public class MyProgressBody extends ResponseBody {

    private final String mUrl;
    private final ResponseBody mResponseBody;
    private BufferedSource bufferedSource;
    private MyProgressSource myProgressSource;

    public MyProgressBody(String url, ResponseBody responseBody){
        this.mUrl = url;
        this.mResponseBody = responseBody;
    }

    @Override
    public MediaType contentType() {
        return (null != mResponseBody)?mResponseBody.contentType():null;
    }

    @Override
    public long contentLength() {
        return (null != mResponseBody)?mResponseBody.contentLength():0;
    }

    @Override
    public BufferedSource source() {
        if(null == bufferedSource){
            myProgressSource = new MyProgressSource(mResponseBody.source(),mUrl, mResponseBody);
            bufferedSource = Okio.buffer(myProgressSource);
        }
        return bufferedSource;
    }

}

这个类中, 要重写父类的三个方式,其中contentType方法中, 直接返回传入的ResponseBody实例的contentType()方法的值作为返回结果即可。contentLength()方法也是一样的处理,也是调用传入的ResponseBody的实例对象的contentLength()的值作为返回结果。最关键的是source()方法,这个方法中,才是处理下载的进度的关键方法。这个方法中,传入了一个继承自ForwardingSource的类MyProgressSource。这个MyProgressSource类中,完成了具体的下载进度的计算。

import java.io.IOException;

import okhttp3.ResponseBody;
import okio.Buffer;
import okio.ForwardingSource;
import okio.Source;
import test.cn.example.com.util.LogUtil;

public class MyProgressSource extends ForwardingSource {
    private ResponseBody mResponseBody;
    private long totalReadLength;
    private long fullLength;
    private int currentProgress;
    private MyProgressListener progressListener;

    public MyProgressSource(Source delegate) {
        super(delegate);
    }

    public MyProgressSource(Source delegate,String url,ResponseBody responseBody){
        this(delegate);
        this.mResponseBody = responseBody;
        fullLength = mResponseBody.contentLength();
	//根据请求的图片的url地址来获取相应的监听
        progressListener = MyProgressInterceptor.listeners.get(url);
    }

    @Override
    public long read(Buffer sink, long byteCount) throws IOException {
        long readLength = super.read(sink, byteCount);
        if(-1 == readLength){
            totalReadLength = fullLength;
        }else {
            totalReadLength +=readLength;
        }
        int progress = (int) (100f * totalReadLength/fullLength);
        LogUtil.i("当前进度:    "+currentProgress);
        if(null != progressListener && currentProgress!=progress){
            progressListener.onProgress(progress);
        }

        if(null !=progressListener && currentProgress==fullLength){
            progressListener = null;
        }

        currentProgress = progress;
        return totalReadLength;
    }

}

在read方法中,计算当前读取的自己的长度,然后将其累加,通过累加的结果除以总的字节长度,算出一个100以内的整数。在MyProgressSource的构造方法中,获取当前这个url的对应的监听,之所以这样处理,是因为项目中会有多个图片需要加载,根据不同的url实现对不同的图片的下载监听,这样就能够更加准确的处理各个不同的图片的下载进度的监听。

定义完后,还需要在manifest文件中,做如下配置:


    
        

其中meta-data的name要配置成自定义的GlideModule的类全路径名称,value写成固定的GlideModule即可。

在项目中,还是和平常使用Glide加载图片的方式一样,不用做任何其他额外处理。

import android.app.ProgressDialog;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;

import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.request.target.GlideDrawableImageViewTarget;

import test.cn.example.com.androidskill.R;

public class GlideDemoActivity3 extends AppCompatActivity implements View.OnClickListener {

    private ImageView iv;
    LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
            LinearLayout.LayoutParams.WRAP_CONTENT);
    private LinearLayout ll_root;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_glide_demo3);
        ll_root = findViewById(R.id.ll_root);
        findViewById(R.id.btn_3).setOnClickListener(this); 
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_3:
                reset();
                final ProgressDialog progressDialog = new ProgressDialog(this);
                progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
                final String url3 = "https://up.sc.enterdesk.com/edpic/fb/6a/4c/fb6a4c2e7c044a22d785a18a01b8b6d1.jpg";
                MyProgressInterceptor.addListener(url3, new MyProgressListener() {
                    @Override
                    public void onProgress(int progress) {
                        progressDialog.setProgress(progress);
                    }
                });
                Glide.with(this).load(url3)
                        .skipMemoryCache(true)
                        .diskCacheStrategy(DiskCacheStrategy.NONE)
                        .into(new GlideDrawableImageViewTarget(iv){
                            @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();
                                MyProgressInterceptor.removeListener(url3);

                            }
                        });
                break;

        }
    }

    private void reset() {
        ll_root.removeView(iv);
        iv = new ImageView(this);
        iv.setLayoutParams(layoutParams);
        ll_root.addView(iv);
    }

}

布局文件如下:




    

        

下面是显示下载进度的效果:
Glide添加图片下载进度监听_第1张图片

你可能感兴趣的:(android)