Android基于Glide v4.x的图片加载进度监听

Glide是一款优秀的图片加载框架,简单的配置便可以使用起来,为开发者省下了很多的功夫。不过,它没有提供其加载图片进度的api,对于这样的需求,实现起来还真颇费一番周折。

尝试

遇到这个需求,第一反应是网上肯定有人实现过,不妨借鉴一下别人的经验。

Glide加载图片实现进度条效果

可惜,这个实现是基于3.7版本的,4.0版本以上的glide改动比较大,using函数已经被移除了

using()
The using() API was removed in Glide 4 to encourage users to register their components once with a AppGlideModule to avoid object re-use. Rather than creating a new ModelLoader each time you load an image, you register it once in an AppGlideModule and let Glide inspect your model (the object you pass to load()) to figure out when to use your registered ModelLoader.
To make sure you only use your ModelLoader for certain models, implement handles() as shown above to inspect each model and return true only if your ModelLoader should be used.

思考

又要用最新的版本又希望给其增加功能,鱼与熊掌不可兼得?“贪新厌旧”的我怎会轻易放弃。我对加载图片进度的需求再理了一遍,发现可以用其他思路简化一下:

  • 加载图片分为加载本地图片和网络图片
  • 加载本地图片速度很快,主要耗时在图片解码的工作,如果图片已经在内存中,解码的步骤也省去了
  • 加载网络图片主要时间耗在下载图片数据过程中

所以,能监听到图片的下载进度就大概是图片的加载进度了。那难道要先下载图片再交给glide去加载?不用,glide支持整合其他网络模块,例如OkHttp,并且如果我们愿意的话,也可以利用其接口实现自己的网络加载模块。

glide官方仓库提供了OkHttp的整合模块,于是乎我去这些代码了寻找一下突破口。

突破

OkHttp模块是非常简单的,只有4个文件,并且文件都不长

首先,用过glide的都知道,继承GlideModule的类是用于设置glide的配置信息和加载模块的,在OkHttpGlideModule里,向系统注册了一个用于加载GlideUrl类型的组件,简单的可以理解为加载网络图片的组件。

public class OkHttpGlideModule implements GlideModule {
  public OkHttpGlideModule() {
  }

  public void applyOptions(Context context, GlideBuilder builder) {
  }

  public void registerComponents(Context context, Glide glide, Registry registry) {
    registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
  }
}

OkHttpLoader文件也很简单,实现了ModelLoader接口,这里ModelLoader是glide的抽象的资源loader的表示,例如这里,就是说加载GlideUrl的model,返回InputStream,即图片的输入流。关于Glide的loadData、model、fetch的详细介绍,可以查看 官方文档

OkHttpLoader的最终目的,也就是返回了一个LoadData对象。现在情况明确了,glide框架就是利用这个LoadData对象得到图片的输入流,从而下载图片并经过一系列的解码,裁剪,缓存等操作,最后加载出来的。LoadData的参数有一个OkHttpStreamFetcher,从名字看来,这里一定就是下载图片的地方了,我们继续看下去。

public class OkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> {
  private final okhttp3.Call.Factory client;

  public OkHttpUrlLoader(okhttp3.Call.Factory client) {
    this.client = client;
  }

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

  public LoadData<InputStream> buildLoadData(GlideUrl model, int width, int height, Options options) {
    //返回LoadData对象,泛型为InputStream
    return new LoadData(model, new OkHttpStreamFetcher(this.client, model));
  }

  public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
    private static volatile okhttp3.Call.Factory internalClient;
    private okhttp3.Call.Factory client;

    private static okhttp3.Call.Factory getInternalClient() {
      if(internalClient == null) {
        Class var0 = OkHttpUrlLoader.Factory.class;
        synchronized(OkHttpUrlLoader.Factory.class) {
          if(internalClient == null) {
            internalClient = new OkHttpClient();
          }
        }
      }

      return internalClient;
    }

    public Factory() {
      this(getInternalClient());
    }

    public Factory(okhttp3.Call.Factory client) {
      this.client = client;
    }

    public ModelLoader<GlideUrl, InputStream> build(MultiModelLoaderFactory multiFactory) {
      return new OkHttpUrlLoader(this.client);
    }

    public void teardown() {
    }
  }
}

可以看到,所有的重点都在loadData函数里面了,用过OkHttp的同学,有没有觉得好简单?哈哈。

这里直接得到图片的InputStream然后通过回调函数callback.onDataReady()返回了

问题来了,callback的glide框架里传过来的,我们并不能控制,我尝试找了一下调用loadData的地方,结果没有找到。怎么办?看最终的实现吧。

public class OkHttpStreamFetcher implements DataFetcher<InputStream> {
  private static final String TAG = "OkHttpFetcher";
  private final Factory client;
  private final GlideUrl url;
  InputStream stream;
  ResponseBody responseBody;
  private volatile Call call;

  public OkHttpStreamFetcher(Factory client, GlideUrl url) {
    this.client = client;
    this.url = url;
  }

  public void loadData(Priority priority, final DataCallback<? super InputStream> callback) {
    Builder requestBuilder = (new Builder()).url(this.url.toStringUrl());
    Iterator request = this.url.getHeaders().entrySet().iterator();

    while(request.hasNext()) {
      Entry headerEntry = (Entry)request.next();
      String key = (String)headerEntry.getKey();
      requestBuilder.addHeader(key, (String)headerEntry.getValue());
    }

    Request request1 = requestBuilder.build();
    this.call = this.client.newCall(request1);
    this.call.enqueue(new Callback() {
      public void onFailure(Call call, IOException e) {
        if(Log.isLoggable("OkHttpFetcher", 3)) {
          Log.d("OkHttpFetcher", "OkHttp failed to obtain result", e);
        }

        callback.onLoadFailed(e);
      }

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

      }
    });
  }

  public void cleanup() {
    try {
      if(this.stream != null) {
        this.stream.close();
      }
    } catch (IOException var2) {
      ;
    }

    if(this.responseBody != null) {
      this.responseBody.close();
    }

  }

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

  }

  public Class<InputStream> getDataClass() {
    return InputStream.class;
  }

  public DataSource getDataSource() {
    return DataSource.REMOTE;
  }
}

实现

在loadData函数里,有这样一句代码

代码如下:

OkHttpStreamFetcher.this.stream = ContentLengthInputStream.obtain(OkHttpStreamFetcher.this.responseBody.byteStream(), contentLength);

ContentLengthInputStream是glide的工具类,用来读取输入流的,点进去看看,发现有几个read方法

  public synchronized int read() throws IOException {
    int value = super.read();
    this.checkReadSoFarOrThrow(value >= 0?1:-1);
    return value;
  }

  public int read(byte[] buffer) throws IOException {
    return this.read(buffer, 0, buffer.length);
  }

  public synchronized int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
    return this.checkReadSoFarOrThrow(super.read(buffer, byteOffset, byteCount));
  }

所以,我的解决方法就是在这里计算下载进度和进行进度的报告的,把这个类从源码里拷贝出来,替换掉OkHttp模块里面的ContentLengthInputStream,并在这插入自己的下载进度逻辑就行了。

具体的代码实现就不贴出来了,写个大概的思路:

下载图片的时候传入图片进度监听,用集合容器,例如map保存监听的实例,key为url,下载过程中计算下载进度后通过url找到对应的回调返回进度,图片加载完毕后将回调从集合了移除(glide提供了图片加载完毕的回调)。

ok,就这样,有不合理的地方欢迎指出,又更好更优雅的实现方式请不吝指教。也希望大家多多支持我们。

(0)

相关推荐

  • Android App中使用Glide加载图片的教程

    与其他图片加载库相同,Glide除了可以加载网络图片之外,也可以加载本地图片.甚至还可以从各种各样奇葩的数据源中加载图片. 加载网络图片 很多情况下,我们使用图片加载库就是为了加载网络图片.网络操作是一个很复杂的东西.试想一下,如果没有图片加载库,我们就要手动去下载图片,缓存图片,最后再从文件里面读取bitmap并设置到Imageview里面.这还算好的,要是在Listview里面你会更头疼的.原因我就不说了,你懂的~~再加上各种各样的Bitmap操作,保准你再也不想撸代码了.而且Bitmap这

  • Android关于Glide的使用(高斯模糊、加载监听、圆角图片)

    高斯模糊.加载监听.圆角图片这些相信大家都很熟悉,那如何实现这些效果,请大家参考本文进行学习. 1.引用 compile 'com.github.bumptech.glide:glide:3.7.0' 2.加载图片 2.1 基本加载 Glide.with(context)     .load(url)     .into(imageView); 2.2 设置加载中和加载失败的情况 Glide.with(context) .load(url) .placeholder(R.drawable.loa

  • Glide实现加载图片显示进度条效果

    先来看看效果图: Glide作为最近几年刚开始流行起来的图片加载库,功能非常强大,我相信好多人都开始在项目中使用了,网上关于Glide的使用教程也非常多,最近在项目中要实现图片的进度条加载,在网上也没看到有现成的,就想着自己研究一下. 使用 Glide.with(MainActivity.this).using(new ProgressModelLoader( new ProgressHandler(MainActivity.this, progressImageView))). load("h

  • Android图片加载框架Glide的基本用法介绍

    简介 Glide是一款图片加载框架,可以在Android平台上以简单的方式加载和展示图片. dependencies { compile 'com.github.bumptech.glide:glide:3.7.0' } 在清单文件中加入权限 <uses-permission android:name="android.permission.INTERNET" /> 加载图片 http://sc.jb51.net/uploads/allimg/150709/14-150FZ

  • Android图片加载缓存框架Glide

    Glide开源框架是Google推荐的图片加载和缓框架,其在Github上的开源地址是:https://github.com/bumptech/glide 当然一个Google推荐的框架肯定就是Volley啦. 目前Android主流开发工具是AndroidStudio,在AndroidStudio如何使用Glide,https://github.com/bumptech/glide上有详细的介绍说明. 因为刚换新工作不久,公司和的还是Eclipse,所以学习Glide我暂时还用的Eclipse

  • Android中Glide加载图片并实现图片缓存

    今天工作中遇到Glide的缓存问题,之前在项目中一直用Glide加载本地及网络图片,但是没有考虑过缓存的问题,但是需求中需要提到了,所以在网上查了一下,再这里和大家简单的分享一下Glide的使用方法以及缓存 首先,Glide是Github上开源的一个图片库,作者是bumptech,所以要使用的话,必须添加依赖: compile 'com.github.bumptech.glide:glide:3.6.1' 加载方式: Glide.with(context) .load("https://ss0.

  • Android中Glide加载圆形图片和圆角图片实例代码

    一.简介: 介绍两种使用 BitmapTransformation 来实现 Glide 加载圆形图片和圆角图片的方法.Glide 并不能直接支持 Round Pictures ,需要使用 BitmapTransformation 来进行处理. 二.网上的实现方式 这里介绍下网上常见的方式和使用 RoundedBitmapDrawable 两种方法,本质上是差不多的: 使用 Canvas 和 Paint 来绘制 使用 Android.support.v4.graphics.drawable.Rou

  • Android Glide图片加载(加载监听、加载动画)

    本文实例为大家分享了Android Glide图片加载的具体代码,供大家参考,具体内容如下 1.普通用法 Glide.with(context) .load(url) .into(view); with中可以放context.activity.fragment..:当放activity.fragment时glide会根据生命周期来加载图片.推荐使用activity. 2.设置加载中和加载失败的图片 Glide.with(context) .load(url) .placeholder(R.dra

  • Android基于Glide v4.x的图片加载进度监听

    Glide是一款优秀的图片加载框架,简单的配置便可以使用起来,为开发者省下了很多的功夫.不过,它没有提供其加载图片进度的api,对于这样的需求,实现起来还真颇费一番周折. 尝试 遇到这个需求,第一反应是网上肯定有人实现过,不妨借鉴一下别人的经验. Glide加载图片实现进度条效果 可惜,这个实现是基于3.7版本的,4.0版本以上的glide改动比较大,using函数已经被移除了 using() The using() API was removed in Glide 4 to encourage

  • Android自定义View基础开发之图片加载进度条

    学会了Paint,Canvas的基本用法之后,我们就可以动手开始实践了,先写个简单的图片加载进度条看看. 按照惯例,先看效果图,再决定要不要往下看: 既然看到这里了,应该是想了解这个图片加载进度条了,我们先看具体用法,再看自定义View的实现: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:custom="http://schemas.android.co

  • 微信小程序image图片加载完成监听

    需求 在应用中显示的图片很多情况不满足业务需求,我们需要动态根据图片的宽高进行缩放或加载中显示的缺省图片,这是我没就需要监听图片加载完成回调,来看看微信小程序怎么实现图片加载完成回调. 实现 1. 绑定回调 通过image标签的bindload属性绑定图片加载完成回调函数,src根据图片加载是否完成绑定是否显示缺省图: <image src='{{loadComplete?srcImagePath:defaultImagePath}}' bindload="imageLoad"/

  • Android基于ListView实现类似Market分页加载效果示例

    本文实例讲述了Android基于ListView实现类似Market分页加载效果.分享给大家供大家参考,具体如下: 最近几天研究ListView实现分页加载和滚动加载,发现可以用listView的OnScroll方法来实现,直接上代码 ListViewScroll.java package zy.lucifer.ListViewScroll; import android.app.Activity; import android.os.Bundle; import android.util.Lo

  • Android实现图片加载进度提示

    本文实例为大家分享了Android实现图片加载进度提示的具体代码,供大家参考,具体内容如下 先上图: 实现原理: 第一个控件的实现原理是重写ImageView的onDraw()方法,利用Canvas的clipRect()方法控制图片的显示区域,主键扩大图片的显示区域,从而实现逐渐增加的效果 关键代码: public class LoadingImageView extends ImageView { /*** 背景图片 */ private Drawable bgDrawable; /**前景图

  • android中Glide实现加载图片保存至本地并加载回调监听

    Glide 加载图片使用到的两个记录 Glide 加载图片保存至本地指定路径 /** * Glide 加载图片保存到本地 * * imgUrl 图片地址 * imgName 图片名称 */ Glide.with(context).load(imgUrl).asBitmap().toBytes().into(new SimpleTarget<byte[]>() { @Override public void onResourceReady(byte[] bytes, GlideAnimation

  • 详解Android 教你打造高效的图片加载框架

    1.概述 优秀的图片加载框架不要太多,什么UIL , Volley ,Picasso,Imageloader等等.但是作为一名合格的程序猿,必须懂其中的实现原理,于是乎,今天我就带大家一起来设计一个加载网络.本地的图片框架.有人可能会说,自己写会不会很渣,运行效率,内存溢出神马的.放心,我们拿demo说话,拼得就是速度,奏事这么任性. 关于加载本地图片,当然了,我手机图片比较少,7000来张: 1.首先肯定不能内存溢出,但是尼玛现在像素那么高,怎么才能保证呢?我相信利用LruCache统一管理你

  • 深入剖析Android的Volley库中的图片加载功能

    一.基本使用要点回顾 Volley框架在请求网络图片方面也做了很多工作,提供了好几种方法.本文介绍使用ImageLoader来进行网络图片的加载. ImageLoader的内部使用ImageRequest来实现,它的构造器可以传入一个ImageCache缓存形参,实现了图片缓存的功能,同时还可以过滤重复链接,避免重复发送请求. 下面是ImageLoader加载图片的实现方法: public void displayImg(View view){ ImageView imageView = (Im

  • Android仿微信公众号文章页面加载进度条

    前言: 微信公众号文章详情页面加载的时候,WebView会在头部显示一个进度条,这样做的好处就是用户可以一边加载网页内容的同时也可浏览网页内容,不需要等完全加载完之后才全部显示出来.如何实现呢? 其实很简单,自定义一个WebView就可以实现了. 详细实现步骤如下 : 1.自定义一个ProgressWebView 继续 Webview @SuppressWarnings("deprecation") public class ProgressWebView extends WebVie

随机推荐