网络上关于Glide的分析不知凡几,但是有一些总不能写出自己想要的东西,下面是参考大神的博客,加上自己总结的一些知识点。
Glide用法
Glide.with(this).load(url).placeholder(R.drawable.loading).error(R.drawable.error).into(imageView);
with()方法中传入的参数,决定了glide的生命周期,可以传入Context,Activity或者Fragment,Glide的生命周期和传入的context一致,比如此处传入appcontext,那么只有等到app退出,图片加载才会停止,如此容易造成内存泄漏,因此,context要慎重选择。
load()方法,可以加载各种图片资源,包括网络,本地图片,应用资源,二进制流,Uri对象等等,因此load()方法有很多方法重载。
使用方法如下:
// 加载本地图片
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);
至于into方法,则传入imageView类型的参数即可。 placeholder()则是作为一个占位符,加载图片的过程可以在控件中展示。
另外除了placeholder之外,还可以展示在加载错误时的图片。方法是error()方法。
Glide缓存机制
Glide的缓存机制分为,内存缓存、磁盘缓存、网络缓存,有些人不认为从网络获取图片是缓存,所以有的地方叫三级缓存,有的地方叫二级缓存,称谓问题不重要,此处不做讨论。
Glide的内存缓存和磁盘缓存互不干扰,并且彼此可以进行配置。
Glide默认开启内存缓存,Glide缓存机制分为内存缓存和硬盘缓存,另外在缓存的基础上又加上了图片压缩机制。
仅用内存缓存的方法,加上.skipMemoryCache(true)即可。
Glide.with(this)
.load(url)
.skipMemoryCache(true)
.into(imageView);
压缩机制
在app中,加载的图片大小往往大于实际控件需要的大小,比如一个低分辨率的手机,加载一个高分辨率的图片,这个时候,图片本身给用户的体验并不会因为图片的分辨率更高,而效果更好,反而会浪费内存,过多的图片是造成app运行内存过高的罪魁祸首,因此对图片进行压缩势在必行。
图片大小计算方法
Android中对图片的解码主要有两种:ARGB_8888,和RGB_565,
A:代表透明度,R:红色,G:绿色,B:蓝色
后缀的四位分别代表对应的位数,比如ARGB_8888,代表一个像素点占8+8+8+8 = 32位(4字节)
RGB_565,代表没有透明度,一个像素点5+6+5 = 16(2字节)
如果一个480*480的图片,在图片解码模式为ARGB_8888情况下,占据的空间为:
480*480*4/1024 = 900kb内存,而在RGB_565下,内存为480*480*2/1024 = 450kb
我们既然知道了图片的大小计算方法,下面介绍如何把一个高分辨率的图片进行压缩为我们想要的大小。
BitmapFactory类中提供了多个解析办法,(decodeByteArray,decodeFile,decodeResource等)用于创建Bitmap对象,我们应该根据图片的来源选择合适的方法。
Sdcard中的图片可以使用decodeResource方法。网络图片使用decodeStream方法,资源文件中的图片使用decodeResource方法。
这些方法提供一个可选的BitmapFactory.Options参数,将这个参数的inJustDecodeBounds属性设置为true,就可以让解析方法禁止为bitmap分配内存,返回值也不再是一个Bitmap对象,而是null。虽然Bitmap是null了,但是BitmapFactory.Options的outWidth、outHeight和outMimeType属性都会被复制,这个技巧让我们可以在加载图片之前就获取到图片的长宽值和MIME雷行,从而更具情况对图片压缩。
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
为了避免OOM,最好在解析每一张图片的时候都检查一下图片大小,除非非常信任图片的来源,保证不会超出程序可用内存。
在获取到图片大小之后,我们来确定是否需要压缩对图片进行处理,压缩办法如下。
首先计算出何时的压缩比例:
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// 源图片的高度和宽度
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// 计算出实际宽高和目标宽高的比率
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高
// 一定都会大于等于目标的宽和高。
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
使用下面方法要首先把inJustDecodeBounds标志位设置为true,获取图片的宽高参数,然后在计算出压缩的
比例,赋值给option,之后对inJustDecodeBounds赋值为false,之后就可以再次进行变麻,即可得到压缩后的图片。
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// 调用上面定义的方法计算inSampleSize值
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// 使用获取到的inSampleSize值再次解析图片
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
如此便可以将任意一个图片压缩为想要的大小进行展示,
imageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(),R.drawable.image,100,100));
LruCache算法
说到缓存算法,使用最多的就是LruCache算法,Glide内存缓存使用的正是LruCache算法和弱引用的结合。
LruCache类在v4包中,算法的原理是,把最近对象用强引用存储在LinkedHashMap中,并把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。
LruCache的构造如下,实现原理依赖LinkHashMap,把LinkHashMap构造的第三个参数accessOrder,设置为true,表示LinkHashMap中的数据排序基于访问顺序(最近访问的数据会在链表尾部),在容量溢出的时候,将链表头数据移除,从而视线LRU数据缓存机制。
LruCache内部的put、get、remove、trimToSize都是线程安全的,LruCache自身并没有释放内存,而是将双向链表中的数据移除,如果数据在别的地方引用,还是存在内存泄露问题,覆写entryRemoved方法可以知道LruCache数据移除是否冲突,可以手动释放资源。
下面是LruCache的构造方法:
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
}
与压缩算法结合使用,实现图片缓存示例如下。
private LruCache<String, Bitmap> mMemoryCache;
@Override
protected void onCreate(Bundle savedInstanceState) {
// 获取到可用内存的最大值,使用内存超出这个值会引起OutOfMemory异常。
// LruCache通过构造函数传入缓存值,以KB为单位。
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// 使用最大可用内存值的1/8作为缓存的大小。
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// 重写此方法来衡量每张图片的大小,默认返回图片数量。
return bitmap.getByteCount() / 1024;
}
};
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
public void loadBitmap(int resId, ImageView imageView) {
final String imageKey = String.valueOf(resId);
final Bitmap bitmap = getBitmapFromMemCache(imageKey);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
imageView.setImageResource(R.drawable.image_placeholder);
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
task.execute(resId);
}
}
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
// 在后台加载图片。
@Override
protected Bitmap doInBackground(Integer... params) {
final Bitmap bitmap = decodeSampledBitmapFromResource(
getResources(), params[0], 100, 100);
addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
return bitmap;
}
}
内存缓存
Glide内存缓存默认是开启状态,如果想要禁用方法如下,
Glide.with(this)
.load(url)
.skipMemoryCache(true)
.into(imageView);
向skipMemoryCache中传入true就可以禁用内存缓存功能。
Glide内存缓存使用LruCache方法和弱引用结合的机制,正在使用的图片使用弱引用进行缓存,不在使用的图片使用LruCache进行缓存。
内存缓存的流程
1、先从LruCache中读取,如果取不到再从弱引用中读取。
2、内存缓存中取不到,从网络拉下来,放到弱引用中,渲染图片,图片对象Resources使用计数加一。
3、渲染完图片,图片对象Resources使用计数减一,如果计数为0,图片缓存从弱引用中闪出,放入LruCache缓存中
硬盘缓存
Glide.with(this)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(imageView);
调用diskCacheStrategy()方法并传入DiskCacheStrategy.NONE,就可以禁用掉Glide的硬盘缓存功能了。
这个diskCacheStrategy()方法基本上就是Glide硬盘缓存功能的一切,它可以接收四种参数:
DiskCacheStrategy.NONE: 表示不缓存任何内容。
DiskCacheStrategy.SOURCE: 表示只缓存原始图片。
DiskCacheStrategy.RESULT: 表示只缓存转换过后的图片(默认选项)。
DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。
Glide加载一张图片,默认并不会把原始图片展示出来,而是经过转换之后进行展示,默认就是DiskCacheStrategy.RESULT这种模式,我们可以通过不同的参数改变默认行为。
Glide与Picasso的区别
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0327/2650.html
上述文章对picasso和Glide的区别进行了详细的探讨,这里做一下简单的总结。
1、Glide和Picasso默认的加载图片的格式分别是,ARGB_565和ARGB_8888,这就导致glide在默认情况下的内存开销要小于Picasso,当然也可以自己新建一个module,把Glide的bitmap各式转化为ARGB_8888。
2、Picasso加载全尺寸的图片到内存中,然后让CPU实时重绘大笑,而Glide加载的大小和ImageView的大小是一致的,因此更小,当然Picasso也可以指定加载图片的大笑,但是需要主动计算imageView的大小,而不是wrap_content
3、Glide加载图片没有Picasso那么平滑。
4、磁盘缓存策略,picasso是全尺寸的,Glide缓存是跟ImageView尺寸相同。也就是说,如果Glide加载同一张图片,但是在不同的地方尺寸不同,那么需要重新下载,并且调整尺寸大小后,把这个尺寸也缓存下来。但是可以配置diskCacheStrategy.ALL,下次在加载任何图片的时候,都会把原始图片取出,然后重新调整大小,然后缓存,Glide这种特点让加载图片异常迅速。
5、Glide可以加载动画,甚至是video(慎用!!)
6、glide的包比picasso大
备注:
本文参照郭霖大神的Glide缓存机制文章,加上一些自己的补充,如有侵权,直接联系作者
参考文章:
https://blog.csdn.net/guolin_blog/article/details/54895665