Glide4图片加载添加进度

作者: 傲娇的狗蛋 | 来源:发表于2019-07-24 11:57 被阅读0次

    前言

    最近在做一个Glide的工具类,公司的项目里没有cdn加载图片很慢,想添加图片加载进度。找了一圈发现Glide本身并不提供进度监听

    添加依赖

        api 'com.github.bumptech.glide:glide:4.9.0'
        api 'com.github.bumptech.glide:okhttp3-integration:4.9.0'
        annotationProcessor 'com.github.bumptech.glide:annotations:4.9.0'
        annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
    

    如果你已经移植了andorid x

        api 'com.github.bumptech.glide:glide:4.9.0'
        api 'com.github.bumptech.glide:okhttp3-integration:4.9.0'
        //解决Glide找不到Android声明库问题
        annotationProcessor 'androidx.annotation:annotation:1.0.0' 
        annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
    

    注意 这里第三个依赖一定要使用annotationProcessor。

    生成GlideApp

    @GlideModule
    public class HydraGlideModule extends AppGlideModule {
    }
    
    

    这里其实直接继承AppGlideModule 添加@GlideModule,同步项目就可以生成GlideApp。

    添加监听

    关于添加进度监听我就直接贴代码了

    OkHttpUrlLoader

    public class OkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> {
    
        private final Call.Factory client;
    
        // Public API.
        @SuppressWarnings("WeakerAccess")
        public OkHttpUrlLoader(Call.Factory client) {
            this.client = client;
        }
    
        @Override
        public boolean handles(GlideUrl url) {
            return true;
        }
    
        @Override
        public LoadData<InputStream> buildLoadData(GlideUrl model, int width, int height,
                                                   Options options) {
            return new LoadData<>(model, new OkHttpStreamFetcher(client, model));
        }
    
        /**
         * The default factory for {@link OkHttpUrlLoader}s.
         */
        // Public API.
        @SuppressWarnings("WeakerAccess")
        public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
            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;
            }
    
            /**
             * Constructor for a new Factory that runs requests using a static singleton client.
             */
            public Factory() {
                this(getInternalClient());
            }
    
            /**
             * Constructor for a new Factory that runs requests using given client.
             *
             * @param client this is typically an instance of {@code OkHttpClient}.
             */
            public Factory(Call.Factory client) {
                this.client = client;
            }
    
            @Override
            public ModelLoader<GlideUrl, InputStream> build(MultiModelLoaderFactory multiFactory) {
                return new OkHttpUrlLoader(client);
            }
    
            @Override
            public void teardown() {
                // Do nothing, this instance doesn't own the client.
            }
        }
    }
    
    

    OkHttpStreamFetcher

    public class OkHttpStreamFetcher implements DataFetcher<InputStream>,
            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<? super InputStream> 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<? super InputStream> callback) {
            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();
            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<InputStream> getDataClass() {
            return InputStream.class;
        }
    
        @NonNull
        @Override
        public DataSource getDataSource() {
            return DataSource.REMOTE;
        }
    }
    

    ProgressListener

    public interface ProgressListener {
    
        /**
         * 图片加载进度回调
         *
         * @param isDone
         * @param progress
         */
        void onLoadProgress(boolean isDone, int progress);
    
        /**
         * 加载失败
         */
        void onLoadFailed();
    }
    
    

    ProgressInterceptor 拦截器

    public class ProgressInterceptor implements Interceptor {
    
        public 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(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;
        }
    }
    

    ProgressResponseBody 计算加载进度,并在自定义的拦截器中使用

    public class ProgressResponseBody extends ResponseBody {
    
        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);
                if (listener != null && progress != currentProgress) {
                    listener.onLoadProgress(progress == 100, progress);
                }
                if (listener != null && totalBytesRead == fullLength) {
                    listener = null;
                }
                currentProgress = progress;
                return bytesRead;
            }
        }
    }
    

    在Glide中使用

    @GlideModule
    public class HydraGlideModule extends AppGlideModule {
    
    
        @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));
        }
    }
    

    具体使用

    public void loadWhitListener(final Context context, final String url, final ImageView imageView,
                                     ProgressListener progressListener) {
    
    
            if (check(context, url, imageView)) {
                LogUtils.i("ImageUtils 传入的参数错误,请检查!!!");
                return;
            }
            
            ProgressInterceptor.addListener(url, progressListener);
    
            GlideApp.with(context)
                    .load(url)
                    .diskCacheStrategy(DiskCacheStrategy.NONE)
                    .skipMemoryCache(true)
                    .into(new DrawableImageViewTarget(imageView) {
    
                        @Override
                        public void onLoadStarted(@Nullable Drawable placeholder) {
                            super.onLoadStarted(placeholder);
                           imageView.setImageResource(R.color.color_646464);
                        }
    
    
                        @Override
                        public void onLoadFailed(@Nullable Drawable errorDrawable) {
                            super.onLoadFailed(errorDrawable);
                            ProgressInterceptor.LISTENER_MAP.get(url).onLoadFailed();
                            imageView.setImageResource(R.color.color_transparent);
                        }
    
                        @Override
                        public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<
                                ? super Drawable> transition) {
                            super.onResourceReady(resource, transition);
    
                            ProgressInterceptor.removeListener(url);
    
                        }
                    });
        }
    
    

    传送门

    相关资料

    Android Glide4.0+图片加载进度监听
    基于Glide V4.0封装的GlideImageView,可监听加载图片时的进度
    实现带进度的Glide图片加载功能(基于3.7)

    相关文章

      网友评论

        本文标题:Glide4图片加载添加进度

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