@ZeroGeek
2016-12-11T03:18:54.000000Z
字数 12867
阅读 2272
lib
为什么叫Glide?平滑、滑翔的意思。
Sam sjudd,google的员工开发,目前star 12000+
2014年被google,IO大会推荐
Glide是Android平台上一个高效、开源的(媒体管理)图像加载框架,包含媒体解码,内存和磁盘缓存以及资源池(复用),并简单易用。
Glide支持抓取,解码和展示(本地)视频,图片和GIF。 Glide默认使用基于HttpUrlConnection的网络协议栈,支持Volley和OkHttp网络请求库。
目的:一个是实现平滑的图片列表滚动效果,另一个是支持远程图片的获取、大小调整和展示。
Glide需要依赖Support Library v4
添加依赖
dependencies {
compile 'com.github.bumptech.glide:glide:3.7.0'
}
Glide加载图片的方法和Picasso很相似,流式接口
基本使用,只能在主线程启动。
java.lang.IllegalArgumentException: You must call this method on the main thread
记得加上网络权限
Glide.with(context)
.load("http://7xjung.com1.z0.glb.clouddn.com/zeromp3.jpg")
.into(imageView);
with(Context context)
with(Activity activity)
with(FragmentActivity activity)
将Activity/Fragment作为with()参数的好处是,图片加载会和Activity/Fragment的生命周期保持一致,比如 onPaused状态在暂停加载,在onResumed的时候又自动重新加载。
(有兴趣的可以去看下怎么实现这种生命周期绑定的)
1.String参数
load(String string)
2.资源定位符
load(Uri uri)
3.本地文件
load(File file)
4.资源文件
load(Integer resourceId)
5.URL,不建议使用,在glide 4.0移除了
load(URL url) @Deprecated
6 字节数组
load(byte[] model)
placeholder()
Glide.with(this)
.load("http://7xjung.com1.z0.glb.clouddn.com/zeromp3.jpg")
.placeholder(R.drawable.placeholder)
.into(imageView);
error()
Glide.with(context)
.load("http://7xjung.com1.z0.glb.clouddn.com/zeromp3.jpg")
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.into(imageView);
加载失败了,想知道原因,提供了失败监听的方法
private RequestListener<String, GlideDrawable> requestListener = new RequestListener<String, GlideDrawable>() {
@Override
public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
// do something
return false;
}
@Override
public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
return false;
}
};
Glide.with(context)
.load("http://7xjung.com1.z0.glb.clouddn.com/zeromp3.jpg")
.listener(requestListener)
.into(imageView);
crossFade()
crossFade(int duration),可用它来指定动画执行的时间,默认300ms
animate() 自定义动画
animate(ViewPropertyAnimation.Animator animator)
animate(int animationId)
不想任何动画,使用dontAnimate()
Glide.with(context)
.load("http://7xjung.com1.z0.glb.clouddn.com/zeromp3.jpg")
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.listener(requestListener)
.dontAnimate()
.into(imageView);
Glide.with(context)
.load(R.drawable.placeholder)
.thumbnail(0.1f)
.into(imageView);
注:先加载缩略图,再加载原图
override(int width, int height) ,像素。
Glide.with(context)
.load(R.drawable.placeholder)
.override(600, 300)
.into(imageView);
Glide加载图片大小是自动调整的,他根据ImageView的尺寸自动调整加载的图片大小,并且缓存的时候也是按图片大小缓存,每种尺寸都会保留一份缓存,如果图片不会自动适配到 ImageView,可调用这个方法。
CenterCrop():缩放图像让它填充到 ImageView 界限内并且裁剪额外的部分。ImageView 可能会完全填充,但图像可能不会完整显示
fitCenter() 缩放图像让图像都测量出来等于或小于 ImageView 的边界范围。该图像将会完全显示,但可能不会填满整个 ImageView。
Glide
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.override(600, 200)
.centerCrop()
.into(imageView);
旋转,圆角等
继承BitmapTransformation
private class CornersTransform extends BitmapTransformation {
public CornersTransform(Context context) {
super(context);
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
if (toTransform == null) return null;
Bitmap result = pool.get(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
if (result == null) {
result = Bitmap.createBitmap(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
paint.setShader(new BitmapShader(toTransform, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
paint.setAntiAlias(true);
RectF rectF = new RectF(0f, 0f, toTransform.getWidth(), toTransform.getHeight());
canvas.drawRoundRect(rectF, 5f, 5f, paint);
return result;
}
@Override
public String getId() {
return getClass().getName();
}
}
Bitmap 复用
为了减少垃圾回收,你可以说使用BitmapPool接口来释放不想要的Bitmap或者复用存在的Bitmap。一个在Transformation中复用Bitmap典型的例子:从pool中获取Bitmap,使用该Bitmap创建一个Canvas,然后使用Matrix/Paint/Shader来变换图像并绘制到Canvas上。为了正确有效地在Transformation中复用Bitmap中,遵守下面的规则:
在transform()不要回收资源或者把Bitmap放入Bitmap池中,这些步骤会自动完成。
如果你从Bitmap池中获取了多个Bitmap,或者没有使用从pool中获取到的Bitmap,确保把多余的Bitmap放回pool中。
如果你的Trasformation并没有替换调原始图片(比如,图片已经满足你要求的大小,直接返回了),请在transform()方法中返回原始的资源或者Bitmap。
Glide.with(context)
.load(pngUrl)
.transform(new CornersTransform(context))
.error(R.drawable.error)
.into(imageView);
Glide.with(context)
.load("http://7xjung.com1.z0.glb.clouddn.com/loadgif.gif")
.into(imageView);
Glide.with(context)
.load(pngUrl)
.asGif()
.error(R.drawable.error)
.into(imageView);
asGif(),检查是否gif
asBitmap(),即使是gif,也当作bitmap处理,(用第一帧)
加载本地视频,略过
Glide
.with( context )
.load( Uri.fromFile( new File( filePath ) ) )
.into( imageViewGifAsBitmap );
总体设计:
整个库分为 RequestManager(请求管理器),Engine(数据获取引擎)、 Fetcher(数据获取器)、MemoryCache(内存缓存)、DiskLRUCache、Transformation(图片处理)、Encoder(本地缓存存储)、Registry(图片类型及解析器配置)、Target(目标) 等模块。
简单的讲就是 Glide 收到加载及显示资源的任务,创建 Request 并将它交给RequestManager,Request 启动 Engine 去数据源获取资源(通过 Fetcher ),获取到后 Transformation 处理后交给 Target。
public enum Config {
// No color information is stored.With this configuration, each pixel requires 1 byte of memory.8位Alpha位图 每个像素占用1byte内存
ALPHA_8 (1),
// Each pixel is stored on 2 bytes,代表8位RGB位图 每个像素占用2byte内存
RGB_565 (3),
/**
* Each pixel is stored on 2 bytes. The three RGB color,代表16位ARGB位图 每个像素占用2byte内存
*/
@Deprecated
ARGB_4444 (4),
/**
* Each pixel is stored on 4 bytes. 代表32位ARGB位图 每个像素占用4byte内存.文档里推荐的,尽可能使用这种
*/
ARGB_8888 (5);
}
位图位数越高代表其可以存储的颜色信息越多,图像越逼真,占用内存越大。Android 在处理图片工作,它会以像素形式加载图片到内存中去.
一张图片(Bitmap)占用的内存=图片长度*图片宽度*单位像素占用的字节数。
GlideBuilder 类也允许你配置一个App全局使用的Bitmap的Config属性。 Glide默认使用RGB_565,因为它每个像素只需要2bytes(16bit)的空间,它仅需要高质量图片(既系统默认的ARGB_8888)一半的内存空间。但是,这对于某些图片这可能会出现『条带』的问题,而且它也不支持透明度。 如果在你的应用中『条带』是一个重要的问题,或者你需要尽可能好的图片质量,你可以使用GlideBuilder的setDecodeFormat方法设置DecodeFormat.ALWAYS_ARGB_8888作为首选配置。
强引用 + LRU 算法
Glide 的 BitmapPool
Glide 构建了一个 BitmapPool , Bitmap 申请和回收都是透过 BitmapPool 来处理的。新加载图片时,会先从 BitmapPool 里面找有没有相应大小的 Bitmap ,有则直接使用,没有才会申请新的 Bitmap ;回收时,则会提交给 BitmapPool , 供下次使用。 这种方式极大的减少了 Bitmap 的申请和回收操作,使得 GC 频度降低了很多。
Glide 内存缓存的特点
① Glide 的内存缓存有个 active 的设计 从内存缓存中取数据时,不像一般的实现用 get,而是用 remove ,再将这个缓存数据放到一个 value 为软引用的 activeResources map 中,并计数引用数,在图片加载完成后进行判断,如果引用计数为空则回收掉。
② 内存缓存更小图片 Glide 以 url、viewwidth、viewheight、屏幕的分辨率等做为联合 key,将处理后的图片缓存在内存缓存中,而不是原始图片以节省大小
内存缓存的是Drawable 而不是Bitmap 理由是Drawable相对Bitmap来说有很大的内存优势
2.磁盘缓存
DiskCacheStrategy.NONE 什么都不缓存
DiskCacheStrategy.SOURCE 仅仅只缓存原来的全分辨率的图像。
DiskCacheStrategy.RESULT 仅仅缓存最终的图像,转换后的
DiskCacheStrategy.ALL 缓存所有版本的图像(默认行为)
磁盘缓存策略
Glide 依赖于 DiskLRUCache 开源库去完成本地缓存工作(即重写其 put,get,delet,clear 方法),并使用 SHA-256 编码生成关键 key 。
磁盘缓存会缓存几乎同一图像的所有尺寸,包括原始图像,全分辨率图像和另外小版本的图像。比如,如果你请求的一个图像是 1000×1000 像素的,但你的 ImageView 是 500×500 像素的,Glide 将会把这两个尺寸都进行缓存。
SimpleTarget
如果你只是想加载一个Bitmap,并不想直接展示给用户而是有一些特殊用途,比如在通知栏中显示或者作为头像上传。 Glide也可以做到。 SimpleTarget为Target接口提供了大部分的默认实现。你可以专注于处理加载的结果。 为了使用SimpleTarget,你需要在它的构造函数中提供你要加载的资源的宽和高(单位像素),你还需要实现onResourceReady(T resource, GlideAnimation animation)方法。 一个典型的使用SimpleTarget的例子如下:
一些警告
正常情况下,你加载一个资源会把他们放到view中。当你的Activity或者Fragment被pause或者destroy时,Glide会暂停或取消加载,确保你不会加载那些根本不会显示的资源。 可是当我们使用SimpleTarget的时候,这可能并不是我们希望的行为。所以,当你调用Glide.with(context)的时候,你可以传入Application的context,而不是传入Activity或者Fragment。 此外,考虑到长时间的加载操作可能导致内存泄漏,请考虑使用静态内部类,而不是匿名内部类。
ViewTarget
如果你想加载一个图片到View中,但是你想观察或者覆盖Glide的默认行为。你可以覆盖ViewTarget或者它的子类。 当你想让Glide来获取view的的大小,但是由自己来启动动画和设置资源到view中,ViewTarget是个不错的选择。
NotificationTarget
AppWidgetTarget
.using(),去指定你想要Module
public class AppGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
builder.setBitmapPool(new LruBitmapPool(diskCacheSize));
builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
builder.setMemoryCache(new LruResourceCache(memoryCacheSize));
builder.setDiskCache(new ExternalCacheDiskCacheFactory(context, filePath, diskCacheSize));
builder.setDiskCacheService(new FifoPriorityThreadPoolExecutor(1));
builder.setResizeService(new FifoPriorityThreadPoolExecutor(1));
}
@Override
public void registerComponents(Context context, Glide glide) {
}
}
它们所有的值都会被收集在Bundle对象中并且使其可以作为组件的 PackageItemInfo.metaData 字段
metadata是一组供父组件使用的名值对(name-value pair)
ApplicationInfo appInfo = this.getPackageManager()
.getApplicationInfo(getPackageName(),PackageManager.GET_META_DATA);
appInfo.metaData.getString("GlideModule");
先来看看默认配置
Glide createGlide() {
if (sourceService == null) {
final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
sourceService = new FifoPriorityThreadPoolExecutor(cores);
}
if (diskCacheService == null) {
diskCacheService = new FifoPriorityThreadPoolExecutor(1);
}
MemorySizeCalculator calculator = new MemorySizeCalculator(context);
if (bitmapPool == null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
int size = calculator.getBitmapPoolSize();
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
if (memoryCache == null) {
memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
if (engine == null) {
engine = new Engine(memoryCache, diskCacheFactory, diskCacheService, sourceService);
}
if (decodeFormat == null) {
decodeFormat = DecodeFormat.DEFAULT;
}
return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
}
内存缓存和缓存池
GlideBuilder类允许你设置内存缓存大小,而且可以实现自定义的MemoryCache和BitmapPool。
大小
默认大小是由MemorySizeCalculator类决定的。MemorySizeCalculator类会考虑该设备的屏幕大小、可用内存来计算出一个合理的默认值。
Bitmap池
Glide的Bitmap池主要作用是,可以让各种尺寸的Bitmap被复用,可以可观地减少由垃圾回收引起的内存抖动。在解码图片时,需要给像素数组分配空间,这会触发垃圾回收。
类似LinkedHashMap,但是加入LRU算法
140多行
private final GroupedLinkedMap groupedMap = new GroupedLinkedMap();
为何请求特定尺寸图片
我们在做的最新项目用到一个媒体服务器,提供非常高的分辨率图片服务(图片分辨率在6000x4500像素)。我们可以使用直接链接到源文件,在考虑设备带宽、内存和电池的时候是相当低效的。即使在今天的设备上显示高分辨率,对于显示如此极端的分辨率,还是没有任何好处。因此Glide总是测量ImageView的尺寸,然后降低图片的内存消耗以匹配它。然而,无法避免要去下载和计算,完成降低图片大小的任务。
使用signature()实现自定义cacheKey:
Glide 以 url、viewwidth、viewheight、屏幕的分辨率等做为联合key,官方api中没有提供删除图片缓存的函数,官方提供了signature()方法,将一个附加的数据加入到缓存key当中,多媒体存储数据,可用MediaStoreSignature类作为标识符,会将文件的修改时间、mimeType等信息作为cacheKey的一部分,通过改变key来实现图片失效相当于软删除。
1.)使用StringSignature
Glide.with(this).load(yourFileDataModel).signature(new StringSignature("1.0.0")).into(imageView);
2.)使用MediaStoreSignature
Glide.with(this) .load(mediaStoreUri).signature(new MediaStoreSignature(mimeType, dateModified, orientation)).into(view);
5 后台下载
downloadOnly方法
Glide的downloadOnly(int, int)方法允许你把图片bytes下载到磁盘缓存中,以便以后获取使用。你可以在UI线程异步地调用downloadOnly(),也可以在后台线程同步的使用。但是,注意他们的参数有些不同,异步API的参数是Target,同步api的参数是宽和高的整数值。 为了在后台线程下载图片,你必须使用同步方法
之前我们一直在使用SoftReference软引用,SoftReference是一种现在已经不再推荐使用的方式,因为从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用变得不再可靠。
Lru缓存机制本质上就是存储在一个LinkedHashMap存储,为了保障插入的数据顺序,方便清理
DiskLruCache
Glide 依赖于 DiskLRUCache、GifDecoder 等开源库去完成本地缓存和 Gif 图片解码工作。
(2) 支持优先级处理
(3) 与 Activity/Fragment 生命周期一致,支持 trimMemory
Glide 对每个 context 都保持一个 RequestManager,通过 FragmentTransaction 保持与 Activity/Fragment 生命周期一致,并且有对应的 trimMemory 接口实现可供调用。
(4) 支持 okhttp、Volley
Glide 默认通过 UrlConnection 获取数据,可以配合 okhttp 或是 Volley 使用。实际 ImageLoader、Picasso 也都支持 okhttp、Volley。
(5) 内存友好
① Glide 的内存缓存有个 active 的设计
从内存缓存中取数据时,不像一般的实现用 get,而是用 remove,再将这个缓存数据放到一个 value 为软引用的 activeResources map 中,并计数引用数,在图片加载完成后进行判断,如果引用计数为空则回收掉。
② 内存缓存更小图片
Glide 以 url、view_width、view_height、屏幕的分辨率等做为联合 key,将处理后的图片缓存在内存缓存中,而不是原始图片以节省大小
③ 与 Activity/Fragment 生命周期一致,支持 trimMemory
④ 图片默认使用默认 RGB_565 而不是 ARGB_888
虽然清晰度差些,但图片更小,也可配置到 ARGB_888。
其他:Glide 可以通过 signature 或不使用本地缓存支持 url 过期
Glide 功能强大,但代码量大、流转复杂。
Yelp
大致150kb左右
https://futurestud.io/tutorials/glide-getting-started
http://mrfu.me/2016/02/27/Glide_Getting_Started/
https://github.com/bumptech/glide
http://frodoking.github.io/2015/10/10/android-glide/
http://angeldevil.me/2016/09/05/glide/
http://blog.qiji.tech/archives/10029
http://www.cnblogs.com/whoislcj/p/5551040.html
private SimpleTarget<GlideDrawable> target = new SimpleTarget<GlideDrawable>() {
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
// do something
imageView.setImageDrawable(resource);
}
};
Glide.with(context)
.load(pngUrl)
.into(target);
public class AppGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder){
builder.setBitmapPool(new LruBitmapPool(diskCacheSize));
builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
builder.setMemoryCache(newLruResourceCache(memoryCacheSize);
builder.setDiskCache(new ExternalCacheDiskCacheFactory(context, filePath, diskCacheSize));
builder.setDiskCacheService(new FifoPriorityThreadPoolExecutor(1));
builder.setResizeService(new FifoPriorityThreadPoolExecutor(1));
}
@Override
public void registerComponents(Context context, Glide glide) {
}
}