Glide实现图片预加载,提前缓存

当下很多App都有开屏广告,App打开的同时广告图片立马就显示出来了,可能有些人就有疑问了”这些广告图片是在应用打包的时候一起打包进去的吗?不然要是通过网络现加载的话怎么可能这么快,现在5G又没普及,而且在没网或者网络不好的情况下也没有影响到这些广告图片的展示,那不是提前打包好的还是什么?”。

针对上面的疑问,答案只有一个,那就是“预加载”,提前将广告资源缓存到本地,在需要的时候直接从本地读取加载资源,这样避免了临时加载的不稳定性,同时也能带来更好的展示效果。

针对资源的预加载方式有很多,比如自己写个缓存工具类,将资源缓存到本地指定的位置,按需加载。因为经常使用Glide作为图片资源的加载框架,并且Glide提供了预加载的功能,于是就使用Glide做一次预加载的示例。

使用过Glide的人应该知道加载图片时经常要用到一个名为into()的方法,这个方法用来将图片加载到指定的ImageView中并且在本地进行一份缓存,那么我此时只需要缓存不需要加载到控件中怎么办呢?使用preLoad()方法或者downloadOnly()方法。

以下内容针对Glide4.9.0版本进行编辑

preload()方法

preLoad()方法有两个重载,一个没参数,一个带有加载宽高的参数,其实没参数的那个方法也是调用有参数的,只不过它加载的是图片的原始宽高。

public Target<TranscodeType> preload() {
    return preload(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);
  }

public Target<TranscodeType> preload(int width, int height) {
    final PreloadTarget<TranscodeType> target = PreloadTarget.obtain(requestManager, width, height);
    return into(target);
  }

从这里也能看出preload()最后还是调用的into(Target)方法,有些人可能对into()方法的这个重载不熟悉,因为一般都是使用into(ImageView)这个重载直接加载图片到控件上的,如果不想将资源直接加载到控件上,可以使用into(Target)。

其实我们直接使用into(Target)并且对传入的Target不做处理的效果和preload()方法是一样的,都能将资源缓存到并且不显示。但是为什么还有preload()存在呢,因为它在封装好的Target内对缓存在内存方面还做了一些处理,那就是在资源缓存完成后将资源在内存中释放。

public final class PreloadTarget<Z> extends SimpleTarget<Z> {
  private static final int MESSAGE_CLEAR = 1;
  private static final Handler HANDLER = new Handler(Looper.getMainLooper(), new Callback() {
    @Override
    public boolean handleMessage(Message message) {
      if (message.what == MESSAGE_CLEAR) {
        ((PreloadTarget<?>) message.obj).clear();
        return true;
      }
      return false;
    }
  });

  /.../

  @Override
  public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
    HANDLER.obtainMessage(MESSAGE_CLEAR, this).sendToTarget();
  }

  @SuppressWarnings("WeakerAccess")
  @Synthetic void clear() {
    requestManager.clear(this);
  }
}

接着如果我们要使用preload()的空参数方法,最好要将diskCacheStrategy的缓存策略指定成DiskCacheStrategy.DATA。因为preload()空参方法是预加载的图片的原始大小,而into()方法则默认会根据ImageView控件的大小来动态决定加载图片的大小。因此,如果不将diskCacheStrategy的缓存策略指定成DiskCacheStrategy.DATA的话,很容易会造成我们在预加载完成之后再使用into()方法加载图片,却仍然还是要从网络上去请求图片这种现象。但是你要是从头到尾使用的都是指定宽高的方法,那么这里无需设置缓存策略。

DiskCacheStrategy.DATA的含义

 /**
   * Writes retrieved data directly to the disk cache before it's decoded.
   * 在解码之前,将检索到的数据直接写入磁盘缓存。
   */
  public static final DiskCacheStrategy DATA = new DiskCacheStrategy() {}

到这就可以使用preload()方法去预加载了,但是我怎么知道预加载有没有成功呢?我需要知道预加载完成的状态,还有如果Glide加载图片失败了,我该怎样调试错误的原因呢?

莫担心,别忘了Glide提供了listener()方法,我们只需要设置一个RequestListener的实例,实现它的onResourceReady()和onException()方法就可以解决预加载监听的问题了,这两个方法从名字上就能知道是干什么用的。

 Glide.with(this).load(mUrl).diskCacheStrategy(DiskCacheStrategy.DATA).listener(object : RequestListener<Drawable> {
            override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
                Log.d("kkk", "预加载失败")
                return true
            }

            override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
                Log.d("kkk", "预加载完成")
                return true
            }
        }).preload()

关于这两个方法的返回值,返回false就表示这个事件没有被处理,还会继续向下传递,返回true就表示这个事件已经被处理掉了,从而不会再继续向下传递,举个例子,如果我们在RequestListener的onResourceReady()方法中返回了true,那么就不会再回调Target的onResourceReady()方法了。

预加载完成后想显示图片资源的话,就像平时使用Glide那样into()就可以。

 Glide.with(this).load(mUrl).diskCacheStrategy(DiskCacheStrategy.DATA).into(mImageView)

这里最好仍然使用diskCacheStrategy()方法将硬盘缓存策略指定成DiskCacheStrategy.DATA,以保证Glide一定会去读取刚才预加载的图片缓存。

downloadOnly()方法

相比preload()方法它使用起来相对复杂一点,不过downloadOnly()方法已经被废弃了,也不清楚是从哪个版本开始的,所以就不推荐大家使用它了。你要是想使用也没关系,它有两个重载,一个参数的在主线程使用,两个参数的在子线程使用。一般使用两个参数的,一个参数的还需要实现自己的Target(),比较麻烦。两个参数的downloadOnly()方法与preload()方法最大的区别就是它会返回一个表示加载资源的File对象,通过返回的FutureTarget<File>.get()方法获得,如果此时图片没有下载完,get()会阻塞线程,直到返回File对象,所以它才需要在子线程中进行调用。

Thread(Runnable {
            val target = Glide.with(applicationContext).load(mUrl).downloadOnly(Target.SIZE_ORIGINAL,Target.SIZE_ORIGINAL)
            val file = target.get()
            Log.i("kkk", file.absolutePath)
        }).start()

之后就可以使用into()方法去显示用downloadOnly()预加载的图片,和调用preload()后一样的操作。

 Glide.with(this).load(mUrl).diskCacheStrategy(DiskCacheStrategy.DATA).into(mImageView)

总结

其实preload()和downloadOnly()缓存的位置一样,只是后者可以在预加载完成后直接拿到缓存文件,方便开发者对缓存文件进行其他操作?但是不懂为什么被作者废弃掉了,可能是觉得使用不方便,也没有需要进行其他操作的,看着鸡肋就去掉了吧。

发布了57 篇原创文章 · 获赞 26 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/Ever69/article/details/104237329