Glide缓存机制和与Picasso的区别

 

网络上关于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

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值