Glide4.10.0加载图片进度监听

参考:
Glide —— 替换资源加载组件
Android Glide4.0+图片加载进度监听

主要的6文件:
ProgressGlideModel,
ProgressInterceptor,ProgressListener,ProgressResponseBody
OkHttpUrlLoader,OkHttpStreamFetcher

1.ProgressGlideModel由于使用的是InputStream.class来加载资源,而Glide本身有这个组件,因此需要将此组件替换。

@GlideModule
public class ProgressGlideModel extends AppGlideModule {
    public ProgressGlideModel() {
        super();
    }
    @Override
    public boolean isManifestParsingEnabled() {
        return super.isManifestParsingEnabled();
    }

    @Override
    public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
        super.applyOptions(context, builder);
    }

    @Override
    public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .addInterceptor(new ProgressInterceptor())
                .build();
        registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(okHttpClient));
    }
}

2.ProgressInterceptor为拦截器,监听进度的关键

public class ProgressInterceptor implements Interceptor {
    public static final Map 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(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;
    }
}
  • ProgressListener进度回调
public interface ProgressListener {
    void onProgress(int progress);
}
  • ProgressResponseBodyResponseBody改写
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;
        }
    }
}

3.OkHttpUrlLoader创建模组加载类实现ModelLoader

public class OkHttpUrlLoader implements ModelLoader {
    private final Call.Factory client;
    public OkHttpUrlLoader(Call.Factory client) {
        this.client = client;
    }

    @Override
    public boolean handles(GlideUrl url) {
        return true;
    }

    @Override
    public LoadData buildLoadData(GlideUrl model, int width, int height,
                                               Options options) {
        return new LoadData<>(model, new OkHttpStreamFetcher(client, model));
    }
    public static class Factory implements ModelLoaderFactory {
        private static volatile Call.Factory internalClient;
        private final Call.Factory client;

        private static Call.Factory getInternalClient() {
            if (internalClient == null) {
                synchronized (Factory.class) {
                    if (internalClient == null) {
                        internalClient = new OkHttpClient();
                    }
                }
            }
            return internalClient;
        }
        public Factory() {
            this(getInternalClient());
        }
        public Factory(Call.Factory client) {
            this.client = client;
        }

        @Override
        public ModelLoader build(MultiModelLoaderFactory multiFactory) {
            return new OkHttpUrlLoader(client);
        }

        @Override
        public void teardown() {
        }
    }
}
  • OkHttpStreamFetcher创建数据获取类继承DataFetcher
public class OkHttpStreamFetcher implements DataFetcher, okhttp3.Callback {
    private static final String TAG = "OkHttpFetcher";
    private final Call.Factory client;
    private final GlideUrl url;
    @SuppressWarnings("WeakerAccess")
    @Synthetic
    InputStream stream;
    @SuppressWarnings("WeakerAccess")
    @Synthetic
    ResponseBody responseBody;
    private volatile Call call;
    private DataCallback callback;

    // Public API.
    @SuppressWarnings("WeakerAccess")
    public OkHttpStreamFetcher(Call.Factory client, GlideUrl url) {
        this.client = client;
        this.url = url;
    }

    @Override
    public void loadData(Priority priority, final DataCallback callback) {
        Request.Builder requestBuilder = new Request.Builder().url(url.toStringUrl());
        for (Map.Entry headerEntry : url.getHeaders().entrySet()) {
            String key = headerEntry.getKey();
            requestBuilder.addHeader(key, headerEntry.getValue());
        }
        Request request = requestBuilder.build();
        this.callback = callback;

        call = client.newCall(request);
        if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O) {
            call.enqueue(this);
        } else {
            try {
                // Calling execute instead of enqueue is a workaround for #2355, where okhttp throws a
                // ClassCastException on O.
                onResponse(call, call.execute());
            } catch (IOException e) {
                onFailure(call, e);
            } catch (ClassCastException e) {
                // It's not clear that this catch is necessary, the error may only occur even on O if
                // enqueue is used.
                onFailure(call, new IOException("Workaround for framework bug on O", e));
            }
        }
    }

    @Override
    public void onFailure(@NonNull Call call, @NonNull IOException e) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "OkHttp failed to obtain result", e);
        }

        callback.onLoadFailed(e);
    }

    @Override
    public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
        responseBody = response.body();
        if (response.isSuccessful()) {
            long contentLength = Preconditions.checkNotNull(responseBody).contentLength();
            stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength);
            callback.onDataReady(stream);
        } else {
            callback.onLoadFailed(new HttpException(response.message(), response.code()));
        }
    }

    @Override
    public void cleanup() {
        try {
            if (stream != null) {
                stream.close();
            }
        } catch (IOException e) {
            // Ignored
        }
        if (responseBody != null) {
            responseBody.close();
        }
        callback = null;
    }

    @Override
    public void cancel() {
        Call local = call;
        if (local != null) {
            local.cancel();
        }
    }

    @NonNull
    @Override
    public Class getDataClass() {
        return InputStream.class;
    }

    @NonNull
    @Override
    public DataSource getDataSource() {
        return DataSource.REMOTE;
    }
}

4.根据地址监听

        ProgressInterceptor.addListener(uri, new ProgressListener() {
            @Override
            public void onProgress(int progress) {
                L.e("onProgress: " + progress);
            }
        });
        Glide.with(imageView)
                .load(uri)
                .apply(options)
                .addListener(new RequestListener() {
                    @Override
                    public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
                        ProgressInterceptor.removeListener(uri);
                        L.e("onProgress: 加载失败");
                        return false;
                    }

                    @Override
                    public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
                        ProgressInterceptor.removeListener(uri);
                        L.e("onProgress: 加载成功");
                        return false;
                    }
                })
                .into(imageView);
GIF.gif

问题一:设置监听进度后,全站所有的加载图片都会监听进度,明显感觉加载图片的速度慢了很多。
解决:原来是公司网速太慢,多试了几次发现去掉进度监听和不去都一样慢。

你可能感兴趣的:(Glide4.10.0加载图片进度监听)