使用Glide实现在非WiFi环境手动点击下载图片(判断Glide是否缓存了图片)
来源:互联网 发布:手机淘宝私人定制 编辑:程序博客网 时间:2024/05/19 00:41
1、概述
Glide作为Google推荐的一套快速高效的图片加载框架,有很多人都在使用,我也不例外。不过在项目的需求中,难免会遇到一个这样的需求:在非WiFi环境下,需要手动点击才能下载图片。
这初步实现起来是很简单的,但一些细节却不好解决。比如,在使用移动数据的情况下,我不能去自动加载图片,但已经缓存过的图片我们得让他自动显示出来。这个时候我们会发现,Glide没有直接的、明确的接口去立马判断某图片(Url等)是否已经缓存了。
为了实现这个功能,我们就只能自己去Glide的缓存目录寻找那图片是否已经缓存下来了,但Glide缓存文件名字是不会是Url或图片文件名,因此我们得采取一些其他手段。
若我的思路中有如何错误,欢迎指正。
另外,在此感谢郭神的Glide解析,为我解决这个问题提供帮助(博客指路:Android图片加载框架最全解析(三),深入探究Glide的缓存机制)
2、实现
(PS. 以下代码是对在非WiFi环境下,需要手动点击才能下载图片这一需求的完全实现)
(1)、初步的判断
/** * 加载图片 * * @param context 加载这个行为所处的Activity或Fragment * @param url 图片的网址 * @param imageView 加载到哪个ImageView上 * @param widthPixels 图片的宽 * @param heightPixels 图片的高 */ public static void loadNetworkImage(final Context context, final String url, final ImageView imageView, final int widthPixels, final int heightPixels) { if (isLoadFailed(imageView)) { // 判断是否是正在加载中的ImageView return; } imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (isLoadFailed(imageView)) { // 判断是否加载失败 loadImage(context, url, imageView, widthPixels, heightPixels); } else if (!isLoading(imageView) && sOnImageViewClickListener != null) { // 判断是否加载中 与 是否有设置监听 sOnImageViewClickListener.onClick(v); } } }); if (AppUtil.loadIsManualLoadPhotoInNotWiFi(context) && !NetworkUtil.isWifi()) { // 判断用户是否开启了 在非WiFi环境下,需要手动点击才能下载图片,后在判断是否处于WiFi中 loadCacheImage(context, url, imageView, widthPixels, heightPixels); return; } loadImage(context, url, imageView, widthPixels, heightPixels); }
这里的第一个if判断是为了不让ImageView在RecyclerView等控件中,应滑动关系而不断刷新布局而设定的,避免多次对同一控件调用加载图片。
中间的OnClickListener自然是为了让图片加载失败后可以重新加载,或者是给用户手动点击加载而用,其中里面第二个if判断是为了解决在加载成功后,还需要对图片控件进行点击,从而进行其他逻辑(如QQ里对加载完成的图片进行查看大图)。
这里需要注意,在每个Activity或Fragment被销毁的时候,需要清空正在加载中的图片集合与置空图片点击监听,避免出现问题。置空代码与判断代码如下:
/** * 正在加载中的控件集合 */ private static Set<ImageView> sImageViews = new HashSet<>(); /** * 加载失败所显示的图片 */ private static Drawable sErrorImg = ViewUtil.getDrawable(R.mipmap.img_default); /** * 判断图片是否加载失败 */ private static boolean isLoadFailed(ImageView imageView) { return sErrorImg.equals(imageView.getDrawable()); } /** * 判断图片是否在加载中 */ private static boolean isLoading(ImageView imageView) { return sImageViews.contains(imageView); } /** * 取消所有图片加载,并置空监听 */ public static void clearLoadingImg() { for (ImageView imageView : sImageViews) { Glide.clear(imageView); } sImageViews.clear(); sOnImageViewClickListener = null; }
(2)、判断图片是否已经缓存了
private static void loadCacheImage(Context context, String url, ImageView imageView, int widthPixels, int heightPixels) { // 寻找缓存图片 File file = DiskLruCacheWrapper.get(Glide.getPhotoCacheDir(context), 250 * 1024 * 1024).get(new OriginalKey(url, EmptySignature.obtain())); if (file != null) { loadImage(context, url, imageView, widthPixels, heightPixels); } else { imageView.setImageDrawable(sErrorImg); sImageViews.remove(imageView); } }
这里就是这个需求中最大的难点了,如何去判断Glide已经缓存了该图片。
通过阅读、分析Glide的源码(也可以选择去看郭神的Glide解析,网址也在上面发了),我们可以知道,Glide对其缓存Key的构建是比较复杂的,有着十多个参数。但我们也会发现,Glide所缓存的原图的Key实际用到的参数只有两个url和signature,而大多数情况下,signature只会是个空值。因此,我们只需要想办法把url转化成Glide的缓存Key就大功告成了。
不过通过源码我们也知道,原图的缓存Key类——OriginalKey是缺省的,也就是所,我们这些外部应用是无法使用。于是我就采用了一种取巧的方法,直接将Glide的这个类复制到自己的项目里,该类代码如下:
/** * Glide原图缓存Key */ private static class OriginalKey implements Key { private final String id; private final Key signature; private OriginalKey(String id, Key signature) { this.id = id; this.signature = signature; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } OriginalKey that = (OriginalKey) o; return id.equals(that.id) && signature.equals(that.signature); } @Override public int hashCode() { int result = id.hashCode(); result = 31 * result + signature.hashCode(); return result; } @Override public void updateDiskCacheKey(MessageDigest messageDigest) throws UnsupportedEncodingException { messageDigest.update(id.getBytes(STRING_CHARSET_NAME)); signature.updateDiskCacheKey(messageDigest); } }
这类里有两个参数,第一个就是我们图片的Url,第二个则是签名,用于方面标识图片是否需要更新的,而这里,我是直接选择调用EmptySignature.obtain(),传入一个空签名。
在得到原图的缓存Key之后,我们就只需要得到Glide的磁盘缓存了。
通过DiskLruCacheWrapper.get(File directory, int maxSize)方法,我们就可以得到Glide的磁盘缓存对象DiskCache了(在这里建议传入Glide的缓存目录与250M。虽说Glide有进行判断,如果缓存对象以存在就直接把已存在的返回过来,但为了避免出现什么莫名的异常,就按着Glide源码里的调用方式使用)。
接着通过DiskCache.get(Key key)方法去获得缓存图片文件。
在找到缓存文件后为什么不直接使用缓存文件加载呢,则是为了避免Glide的二次缓存。我具体的Glide加载图片方法都是用的同一个,里面都是设定进行缓存,如果这里传入的是缓存文件,就会导致二次缓存,浪费!
(3)、具体的Glide加载图片方法
private static void loadImage(final Context context, Object url, final ImageView imageView, int widthPixels, int heightPixels) { DrawableRequestBuilder<Object> builder = Glide.with(context) .load(url) .placeholder(sLoadingImg) .error(sErrorImg) .diskCacheStrategy(DiskCacheStrategy.SOURCE) // 设置磁盘缓存只缓存原图 .skipMemoryCache(false) //进行内存缓存 .centerCrop(); if (widthPixels != 0 || heightPixels != 0) { builder.override(widthPixels, heightPixels); } builder.into(new GlideDrawableImageViewTarget(imageView) { @Override public void onLoadStarted(Drawable placeholder) { super.onLoadStarted(placeholder); Animation rotationAnimation = AnimationUtils.loadAnimation(context, R.anim.rotate_loading); imageView.startAnimation(rotationAnimation); sImageViews.add(imageView); // 将其添加到 正在加载中的控件集合 中 } @Override public void onLoadFailed(Exception e, Drawable errorDrawable) { super.onLoadFailed(e, errorDrawable); removeViewInLoadingSet(); } @Override public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) { super.onResourceReady(resource, animation); removeViewInLoadingSet(); } /** * 将 已加载完成/加载失败的控件 从 正在加载中的控件集合 中移除 */ private void removeViewInLoadingSet() { imageView.clearAnimation(); sImageViews.remove(imageView); } }); }
最后这里就是Glide加载的具体实现了,里面我通过GlideDrawableImageViewTarget对加载状态进行监听,设置加载中的动画。
到此,整个需求就完成了。
- 使用Glide实现在非WiFi环境手动点击下载图片(判断Glide是否缓存了图片)
- 使用Glide下载图片
- 使用Glide下载图片
- 使用Glide下载图片
- android 使用Glide实现listview图片缓存
- 开始使用Glide图片缓存
- Glide加载图片并实现图片缓存
- Glide图片缓存框架
- Glide图片缓存知识点
- 图片缓存框架Glide
- Glide图片缓存
- Glide--Android图片缓存
- 浅谈Glide图片缓存
- 图片缓存-glide
- 使用glide加载图片
- 使用Glide加载图片
- Glide图片框架使用
- Android图片缓存框架-Glide
- 关于滚动条的应用
- [LC解题报告]16. 3Sum Closest
- 1271-面向对象程序设计上机练习九(对象指针)
- 360加固签名失败:jarsigner: 无法对 jar 进行签名: java.util.zip.ZipException: invalid LOC header (bad signature)
- Access几个小技巧总结
- 使用Glide实现在非WiFi环境手动点击下载图片(判断Glide是否缓存了图片)
- 神州专车全站HTTPS加密,保护用户数据安全
- 深度学习:长短期记忆模型LSTM
- openGl2.0 3D
- js原型初窥-伪类模式
- QGis 二次开发基础 -- 显示数据
- 读《第一行代码》遇到问题5:通知的一些问题
- 计算机网络
- 关于响应式开发的设计规范