Android:图片加载框架 Glide 基础用法
环境:Android 11
一、概述
目前 Glide 在Android的开发中非常的受欢迎,很多项目都会使用 Glide 当图片加载框架。
其主要特点如下:
1、支持Memory和Disk图片缓存
2、支持gif和webp格式图片
3、根据Activity/Fragment生命周期自动管理请求
4、使用Bitmap Pool可以使用Bitmap复用
5、对于回收的Bitmap会主动调用recycle,减少系统回收压力
二、添加依赖和权限
1、添加依赖
在 build.gradle 中添加依赖
implementation 'com.github.bumptech.glide:glide:4.13.2'
2、添加权限
<uses-permission android:name="android.permission.INTERNET" />
注意:如果你使用的是 Http 协议,需要在 AndroidManifest.xml 的 application 中配置 android:usesCleartextTraffic="true" 。
三、用法
1、加载图片
实例:在 ImageView 中加载网络图片,如下:
ImageView imageView = findViewById(R.id.imageView);
String url = "http://www.iprac.cn/your.jpg";
// Glide.with()方法用于创建一个加载图片的实例。
//with()方法可以接收Context、Activity或者Fragment类型的参数。
// load()方法用于指定待加载的图片资源
Glide.with(this).load(url).into(imageView);
Glide支持加载各种各样的图片资源,包括网络图片、本地图片、应用资源、二进制流、Uri对象等等。
用法如下:
// 加载本地图片
File file = new File(getExternalCacheDir() + "/image.jpg");
Glide.with(this).load(file).into(imageView);
// 加载应用资源
int resource = R.drawable.image;
Glide.with(this).load(resource).into(imageView);
// 加载二进制流
byte[] image = getImageBytes();
Glide.with(this).load(image).into(imageView);
// 加载Uri对象
Uri imageUri = getImageUri();
Glide.with(this).load(imageUri).into(imageView);
2、设置加载动画
通常的用法如下,使用api提供的几个常用动画:
Glide.with(this)
.load(url)
.transition(DrawableTransitionOptions.withCrossFade(2000))
.into(imageView);
有三种TransitionOptions:GenericTransitionOptions、DrawableTransitionOptions、BitmapTransitionOptions。
TransitionOptions是和你要加载的资源的类型绑定的,也就是说,如果你请求一张位图Bitmap,你就需要使用BitmapTransitionOptions,而不是DrawableTransitionOptions。如果既不是Bitmap也不是Drawable就使用GenericTransitionOptions。Glide加载网络图片默认的类型是Drawable,所以加载网络图片我们使用DrawableTransitionOptions即可。
如果要使用自定义的动画,可以使用GenericTransitionOptions.with(int viewAnimationId)或者BitmapTransitionOptions.withCrossFade(int animationId, int duration)或者DrawableTransitionOptions.withCrossFade(int animationId, int duration)。
出于性能考虑,最好不要在ListView,GridView,RecycleView中使用过渡动画,使用TransitionOptions.dontTransition()可以不加载动画,也可以使用dontAnimate不加载动画。如:RequestOptions options = new RequestOptions().dontTransform();
3、占位图
(1)加载前占位图
当加载网络图片时,可能 要等一会图片才能加载出来。因为从网络上下载图片是需要时间的。为了提升用户体验,Glide 提供了很多支持,其中就包括了占位图功能。
占位图就是指在图片的加载过程中,先显示一张临时的图片,等图片加载出来了再替换成要加载的图片。用法如下所示:
Glide.with(this)
.load(url)
.transition(DrawableTransitionOptions.withCrossFade(2000))
.apply(options)
.into(imageView);
(2)异常占位图
异常占位图就是指异常情况导致图片加载失败,比如说手机网络信号不好,这个时候就显示这张异常占位图。用法如下所示:
RequestOptions options = new RequestOptions()
.placeholder(R.mipmap.ic_launcher)
.error(R.drawable.error);
Glide.with(this)
.load(url)
.apply(options)
.into(imageView);
4、指定图片加载格式
Glide其中一个非常亮眼的功能就是可以加载GIF图片。使用 Glide 加载 GIF 图很简单,不用额外写代码,修改地址即可,Glide 会自动判断图片格式。
但是如果我想指定图片的格式该怎么办呢?比如,希望加载的图片是一张静态图片,我不需要Glide自动帮我判断它到底是静图还是GIF图。想实现这个功能仍然非常简单,代码如下所示:
Glide.with(this)
.asBitmap()
.load(url)
.apply(options)
.into(imageView);
用法如下:
Glide.with(this)
.asBitmap()
.load(url)
.apply(options)
.into(imageView);
注意:asBitmap()方法必须跟在with()方法的后面,load()方法的前面
类似地,强制指定加载动态图片的方法是asGif()。如果指定了只能加载动态图片,而传入的图片却是一张静图的话,自然就会加载失败。
Glide 4 中新增了asFile()方法和 asDrawable()方法,分别用于强制指定文件格式的加载和Drawable格式的加载,用法都比较简单,就不再进行演示了。
5.指定图片大小
Glide 在绝大多数情况下我们都是不需要指定图片大小的,完全不用担心图片内存浪费,甚至是内存溢出的问题。因为Glide从来都不会直接将图片的完整尺寸全部加载到内存中,而是用多少加载多少。Glide会自动判断 ImageView 的大小,对图片进行压缩,然后只将这么大的图片像素加载到内存当中,帮助我们节省内存开支。如果你真的有这样的需求,必须给图片指定一个固定的大小,Glide仍然是支持这个功能的。修改Glide加载部分的代码,如下所示:
RequestOptions options = new RequestOptions()
.override(100, 100);
Glide.with(this)
.load(url)
.apply(options)
.into(imageView);
如果你想加载一张图片的原始尺寸的话,可以使用Target.SIZE_ORIGINAL关键字,如下所示:
RequestOptions options = new RequestOptions()
override(Target.SIZE_ORIGINAL);
Glide.with(this)
.load(url)
.apply(options)
.into(imageView);
6.缓存机制
Glide 将缓存分成了两块,一个是内存缓存,一个是硬盘缓存。这两个缓存模块的作用各不相同,内存缓存的主要作用是防止应用重复将图片数据读取到内存当中,而硬盘缓存的主要作用是防止应用重复从网络或其他地方重复下载和读取数据。内存缓存和硬盘缓存的相互结合才构成了Glide极佳的图片缓存效果。
(1)内存缓存
默认情况下,Glide 内存缓存是开启的。也就是说,当我们使用 Glide 加载了一张图片之后,这张图片就会被缓存到内存当中,只要在它还没从内存中被清除之前,下次使用Glide再加载这张图片都会直接从内存当中读取,而不用重新从网络或硬盘上读取了,这样可以大幅度提升图片的加载效率。比方说你在一个RecyclerView当中反复上下滑动,RecyclerView中只要是Glide加载过的图片都可以直接从内存当中迅速读取并展示出来,从而大大提升了用户体验。
Glide 甚至不需要编写任何额外的代码就能使用这个便利的功能。
如果你有什么特殊的原因需要禁用内存缓存功能,Glide对此提供了接口:
RequestOptions options = new RequestOptions()
.skipMemoryCache(true);
Glide.with(this)
.load(url)
.apply(options)
.into(imageView);
可以看到,只需要调用 skipMemoryCache() 方法并传入 true,就禁用掉 Glide 的内存缓存功能。
(2)硬盘缓存
Glide 默认也是开启磁盘缓存的。禁止Glide对图片进行硬盘缓存可使用如下代码:
RequestOptions options = new RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.NONE);
Glide.with(this)
.load(url)
.apply(options)
.into(imageView);
diskCacheStrategy()方法基本上就是Glide硬盘缓存功能的一切,它可以接收五种参数:
- DiskCacheStrategy.NONE: 表示不缓存任何内容。
- DiskCacheStrategy.DATA: 表示只缓存原始图片。
- DiskCacheStrategy.RESOURCE: 表示只缓存转换过后的图片。
- DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。
- DiskCacheStrategy.AUTOMATIC: 表示让Glide根据图片资源智能地选择使用哪一种缓存策略(默认选项)。
(3)清理缓存
注意:清理磁盘缓存因为涉及到文件操作不能放主线程中。
//清理内存缓存 可以在UI主线程中进行
Glide.get(this).clearMemory();
//清理磁盘缓存 需要在子线程中执行
new Thread(new Runnable() {
@Override
public void run() {
Glide.get(MainActivity.this).clearDiskCache();
}
}).start();
(4)读取磁盘缓存数据大小
有些 APP 里面会有手动清理缓存功能,然后会把缓存数据大小显示出来,这个其实就是磁盘缓存数据大小,实现其实很简单,只要读取出 Glide 磁盘缓存文件下所有文件大小然后把算出所有数据大小之和就好了。
这里创建了个Glide磁盘缓存数据的工具类,使用时只需要调用获取磁盘缓存大小的方法并传入Context就可获取到Glide磁盘缓存数据大小了,代码如下:
public class GlideDiskCacheUtil {
/**
* 获取Glide造成的磁盘缓存大小
* @return DiskCacheSize
*/
public static String getDiskCacheSize(Context context) {
try {
return getFormatSize(getFolderSize(new File(context.getCacheDir() + "/"+ InternalCacheDiskCacheFactory.DEFAULT_DISK_CACHE_DIR)));
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
/**
* 获取指定文件夹内所有文件大小的和
* @param file file
* @return size
* @throws Exception
*/
private static long getFolderSize(File file) throws Exception {
long size = 0;
try {
File[] fileList = file.listFiles();
for (File aFileList : fileList) {
if (aFileList.isDirectory()) {
size = size + getFolderSize(aFileList);
} else {
size = size + aFileList.length();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return size;
}
/**
* 格式化单位
* @param size size
* @return size
*/
private static String getFormatSize(double size) {
double kiloByte = size / 1024;
if (kiloByte < 1) {
return size + "Byte";
}
double megaByte = kiloByte / 1024;
if (megaByte < 1) {
BigDecimal result1 = new BigDecimal(Double.toString(kiloByte));
return result1.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "KB";
}
double gigaByte = megaByte / 1024;
if (gigaByte < 1) {
BigDecimal result2 = new BigDecimal(Double.toString(megaByte));
return result2.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "MB";
}
double teraBytes = gigaByte / 1024;
if (teraBytes < 1) {
BigDecimal result3 = new BigDecimal(Double.toString(gigaByte));
return result3.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "GB";
}
BigDecimal result4 = new BigDecimal(teraBytes);
return result4.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "TB";
}
}
(5)高级技巧
虽说Glide将缓存功能高度封装之后,使得用法变得非常简单,但同时也带来了一些问题。
比如当项目的图片资源是存放在七牛云上面的,而七牛云为了对图片资源进行保护,会在图片 url 地址的基础之上再加上一个 token 参数。也就是说,一张图片的 url 地址可能会是如下格式:
http://url.com/image.jpg?token=d9caa6e02c990b0a
而使用Glide加载这张图片的话,也就会使用这个url地址来组成缓存Key。
但是接下来问题就来了,token作为一个验证身份的参数并不是一成不变的,很有可能时时刻刻都在变化。而如果token变了,那么图片的url也就跟着变了,图片url变了,缓存Key也就跟着变了。结果就造成了,明明是同一张图片,就因为token不断在改变,导致Glide的缓存功能完全失效了。
这其实是个挺棘手的问题,大家在使用Glide的时候很有可能都会遇到这个问题。那么该如何解决这个问题呢?这个时候就体现出了研究源码的作用了,通过研究源码我们可以知道缓存Key是从GlideUrl类中的getCacheKey()方法将图片的url地址直接进行返回得到的,因此我们只需要创建一个MyGlideUrl继承自GlideUrl,在getCacheKey()方法方法中加入一些自己的处理逻辑把url中token这部分参数去掉然后在Glide加载图片时使用我们创建的MyGlideUrl传入url就可以了,代码如下所示:
public class MyGlideUrl extends GlideUrl {
private String mUrl;
public MyGlideUrl(String url) {
super(url);
}
@Override
public String getCacheKey() {
//将token部分参数替换为空字符串后返回作为缓存Key
return mUrl.replace(findTokenParam(), "");
}
/**
* 查找token部分参数的方法
* @return token部分参数String
*/
private String findTokenParam() {
String tokenParam = "";
int tokenKeyIndex = mUrl.indexOf("?token=") >= 0 ? mUrl.indexOf("?token=") : mUrl.indexOf("&token=");
if (tokenKeyIndex != -1) {
int nextAndIndex = mUrl.indexOf("&", tokenKeyIndex + 1);
if (nextAndIndex != -1) {
tokenParam = mUrl.substring(tokenKeyIndex + 1, nextAndIndex + 1);
} else {
tokenParam = mUrl.substring(tokenKeyIndex);
}
}
return tokenParam;
}
}
使用我们创建的MyGlideUrl传入url代码:
Glide.with(this)
.load(new MyGlideUrl(url))
.into(imageView);