Android中Glide加载库的图⽚缓存配置究极指南
零、选择Glide
为什么图⽚加载我⾸先推荐Glide?
图⽚加载框架⽤了不少,从afinal框架的afinalBitmap,Xutils的BitmapUtils,⽼牌框架universalImageLoader,著名开源组织square的picasso,google推荐的glide到FaceBook推出的fresco。这些我前前后后都体验过,那么⾯对这么多的框架,该如何选择呢?下⾯简单分析下我的看法。
afinal和Xuils在github上作者已经停⽌维护了,开源社区最新的框架要属KJFramework,不过这种快速开发框架看似很好⽤,功能也应有尽有,⼩型项⽬也罢,⼤型项⽬我不是很推荐,这样做项⽬的耦合度太⾼,⼀旦出现停⽌维护,⽽新的问题不断增加,没⼈处理就⿇烦了。
在glide和fresco还未出来的时候,当时最⽕的莫过于universalImageLoader和picasso了,当时觉得universalImageLoader配置相对picasso⿇烦,虽然提供了各种配置,但是没有实践过,根本不知道如何配置,还不如都采⽤默认配置,就选择了picasso作为图⽚加载框架,⽤了近⼀年的时间,没有太⼤的问题,且使⽤简单,或许是因为之前的项⽬太过于简单,周期也并不是很长,还有使⽤eclipse开发,⼀个很⼤的问题⼀直都没有暴露出来,换上了最新的Android Studio可以清晰的看到各种性能相关的监控,如
cpu还有内存监控,终于知道了之前做的项⽬都那么的卡顿的罪魁祸⾸,picasso加载稍微⼤⼀点的图⽚就特别耗内存,通常⼀个listView或者顶部滑动⼴告栏都含有多张图⽚,这使得做出的页⾯只要含图⽚较多就异常卡顿(之前的时候还把它归结为测试机不好),知道这⼀点后我就有点想把picasso给替换掉,但这⼀次我不能那么粗⼼。
测试了picasso,glide,universalImageLoader,fresco这四个框架,测试内容⼤概有以下⼏项,内存测试,⼤图⽚测试,⼩图⽚测试,本地图⽚,⽹络图⽚当然还结合官⽅⽂档体验其特⾊功能,内存测试中,glide,universalImageLoader,fresco表现都⾮常优秀,picasso这⼀点上实在是太糟糕了,⼩图⽚差别也不是很⼤,稍微⼤点图⽚内存消耗就要⽐其他⾼出⼏倍,这⼀点上证明了我的猜想,picasso不能再⽤了,下⾯⼀项项分析其他框架,在⾼于2M左右⼤图测试中fresco的表现则和picasso ⼀样直接神马都不显⽰,项⽬中要实现⼤图预览功能,这点上是不⾏的,接着看universalImageLoader和glide在这⼏项测试中成绩都很好,到底该如何选择呢?
因为我项⽬之前⽤的picasso,glide从⽤法上⼏乎就是另⼀个picasso,从picasso转移到glide相对改动较少,还有⼀点就是这个项⽬是google在维护,我也能给它更多的信任,相⽐较universalImageLoader,glide可以⽀持gif和短视频,后期也需要⽤到,这⾥不得不谈⼀下glide优秀的缓存机制了,glide图⽚缓存默认使⽤RGB565相当于ARGB8888可以节省不少的空间,⽀持与activity,fragment,application⽣命周期的联动,更智能管理图⽚请求当然还有其他的扩展更多可以
看?glide介绍?当然,glide的⽅法数量⽐universalImageLoader多了1000多个,遇到64k问题的会⽐较关注这个。
刚才只是掠过fresco,其实我对他的期待还是蛮⼤的,因为刚出来还有居多不稳定的地⽅,⾥⾯存在着⼤量吸引着我的功能,⽀持webps格式(和jpg⼀样都是有损压缩格式,webps相同质量图⽚更节省空间),⽀持渐进式jpeg,可以轻松的定制image 的各种属性,⽀持多图请求和图⽚复⽤,并⽀持⼿势缩放和旋转等等,更多介绍?fresco,当然,实际⽤的时候并没有那么好,很多功能都有待完善。
还有⼀点细节的地⽅要注意的,最好不要直接拿来⽤,⾄少经过⾃⼰简单的封装,⽽不是直接在项⽬中使⽤,⼀个简单的例⼦,后期图⽚过多,可能需要另外配置⼀台机器单独存放图⽚,主机地址做成可配置,可不要因为⼀个简单的需求⼜要加班了更多。
⼀、Glide3.0以来的新特性
1.动态的GIF图⽚加载:
Glide.with(context).load(...).asBitmap() //显⽰gif静态图⽚
Glide.with(context).load(...).asGif() //显⽰gif动态图⽚
2.本地视频快照:
Glide现在还可以把视频解码为⼀张图⽚:
Glide.with(context).load(“视频路径“)
(经过我的测试,只能把⼿机本地的mp4视频解析为⼀张图⽚,把mp4⽂件放在raw⽂件中,不能解析)
3.对缩略图的⽀持:
//加载yourView1/10尺⼨的缩略图,然后加载全图
Glide.with(yourFragment).load(yourUrl).thumbnail(0.1f).into(yourView)
4.⽣命周期集成
同时将Activity/Fragment作为with()参数的好处是:图⽚加载会和Activity/Fragment的⽣命周期保持⼀致,
请求会在onStop的时候⾃动暂停,
在onStart的时候重新启动,gif的动画也会在onStop的时候停⽌,以免在后台消耗电量。
5.转码
Glide的.toBytes()和.transcode()⽅法允许在后台获取、解码和转换⼀个图⽚,你可以将⼀张图⽚转换成更多有⽤的图⽚格式,⽐如,上传⼀张250*250的图⽚
Glide.with(context)
.load(“/user/profile/photo/path”)
.asBitmap()
.toBytes()
.centerCrop()
.into(new SimpleTarget<byte[]>(250, 250) {
@Override
public void onResourceReady(byte[] data, GlideAnimation anim) {
// Post your bytes to a background thread and upload them here.
}
});
6.动画:3.x加⼊了cross fades和View的属性动画的⽀持
⽐如
(.animate(ViewPropertyAnimation.Animator))
7. ⽹络模块可以选择OkHttp或者Volley的⽀持
You can now choose to use either OkHttp, or Volley, or Glide's HttpUrlConnection default as your network stack.
Volley和OkHttp可以在gradle⽂件当中添加依赖,注册相应的ModelLoaderFactory
⼆、图⽚的缓存和缓存的时效机制
1.图⽚缓存的键值
图⽚缓存的键值主要⽤于DiskCacheStrategy.RESULT,Glide当中的键值主要包含三个部分:
通过Id()⽅法返回的String数据作为键值。⼀般的DataFetchers会简单返回数据模型data model的toString()结果,如果是URL/File会返回相应的路径
图⽚的尺⼨,主要是通过override(width,height)或者通过Target's getSize()⽅法确定的尺⼨信息
包含⼀个可选的签名所有的这些东西会通过⼀种散列算法⽣成⼀个独有、安全的⽂件名,通过此⽂件名将图⽚缓存在disk中
2.缓存失效
因为Glide当中图⽚缓存key的⽣成是通过⼀个散列算法来实现的,所以很难⼿动去确定哪些⽂件可以从缓存当中进⾏删除
2.1 当内容(url,file path)改变的时候,改变相应的标识符就可以了,Glide当中也提供了signature()⽅法,将⼀个附加的数据加⼊到缓存key当中
多媒体存储数据,可⽤MediaStoreSignature类作为标识符,会将⽂件的修改时间、mimeType等信息作为cacheKey的⼀部分
⽂件,使⽤StringSignature
Urls ,使⽤StringSignature
Glide.with(yourFragment)
.load(yourFileDataModel)
.signature(new StringSignature(yourVersionMetadata))
.into(yourImageView);
Glide.with(fragment)
.load(mediaStoreUri)
.signature(new MediaStoreSignature(mimeType, dateModified, orientation))
.into(view);
⾃定义标识符:
public class IntegerVersionSignature implements Key {
private int currentVersion;
public IntegerVersionSignature(int currentVersion) {
this.currentVersion = currentVersion;
}
@Override
public boolean equals(Object o) {
if (o instanceof IntegerVersionSignature) {
IntegerVersionSignature other = (IntegerVersionSignature) o;
return currentVersion = other.currentVersion;
}
return false;
}
@Override
public int hashCode() {
return currentVersion;
}
@Override
public void updateDiskCacheKey(MessageDigest md) {
messageDigest.update(ByteBuffer.allocate(Integer.SIZE)
.putInt(signature).array());
}
}
2.2、不缓存可以通过diskCacheStrategy(DiskCacheStrategy.NONE.)实现
三、配置GlideModules
可以通过GlideModule接⼝来配置Glide的配置⽂件,并且像ModelLoaders⼀样注册相关组件。
包含⼀个GlideMode :
第⼀步、To use and register a GlideModule, first implement the interface with your configuration and components: public class MyGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// Apply options to the builder here.
}
@Override
public void registerComponents(Context context, Glide glide) {
// register ModelLoaders here.
}
}
第⼆步、然后将上⾯的实现了加⼊到proguard.cfg当中:
-keepnames class * package.MyGlideModule
第三步、在l⽂件中添加meta-data,以便Glide能够到你的Module
<meta-data
android:name="com.bumptech.glide.samples.flickr.FlickrGlideModule"
android:value="GlideModule" />
四、Library项⽬
⼀个Library项⽬可能会定义⼀个或者多个GlideModules,如果⼀个Library项⽬添加⼀个Module到Libr
ary项⽬的manifest当中,依赖于此Library的应⽤就会⾃动加载依赖库(Library项⽬)当中的Module。
当然,如果manifest的合并不正确,那么Library⾥⾯Module就必须⼿动地在应⽤当中添加进去。
五、GlideModules冲突
虽然Glide允许⼀个应⽤当中存在多个GlideModules,Glide并不会按照⼀个特殊的顺序去调⽤已注册的GlideModules,如果⼀个应⽤的多个依赖⼯程当中有多个相同的Modules,就有可能会产⽣冲突。
如果⼀个冲突是不可避免的,应⽤应该默认去定义⼀个⾃⼰的Module,⽤来⼿动地处理这个冲突,在进⾏Manifest合并的时候,可以⽤下⾯的标签排除冲突的module。
<meta-data android:name=”package.MyGlideModule” tools:node=”remove”/>
六、通过GlideBuilder配置全局配置⽂件
Glide允许开发者配置⾃定义的全局操作应⽤于所有的请求,这个部分可以通过GlideModule接⼝中的applyOptions⽅法的GlideBuilder参数实现:
1.DiskCache
1.1、硬盘缓存是在⼀个后台线程当中,通过⼀个DiskCache.Factory接⼝进⾏缓存的。
开发者能够通过GlideBuilder的setDiskCache(DiskCache.Factory df)⽅法设置存储的位置和⼤⼩
通过传⼊DiskCacheAdapter来完全禁⽤缓存
⾃定义⼀个DiskCache来完全禁⽤缓存,
Glide默认是⽤InternalCacheDiskCacheFactory类来创建硬盘缓存的,这个类会在应⽤的内部缓存⽬录下⾯创建⼀个最⼤容量250MB的缓存⽂件夹,使⽤这个缓存⽬录⽽不⽤sd卡,意味着除了本应⽤之外,其他应⽤是不能访问缓存的图⽚⽂件的。1.2.设置disk缓存的⼤⼩ : InternalCacheDiskCacheFactory
builder.setDiskCache(new InternalCacheDiskCacheFactory(context, yourSizeInBytes));
1.3.设置缓存的路径
可以通过实现DiskCache.Factory,然后使⽤DiskLruCacheWrapper创建⼀个新的缓存⽬录,⽐如,可以通过如下⽅式在外存当中创建缓存⽬录:
builder .setDiskCache(new DiskCache.Factory() {
@Override
public DiskCache build() {
// Careful: the external cache directory doesn't enforce permissions
File cacheLocation = new ExternalCacheDir(), "cache_dir_name");
cacheLocation.mkdirs();
(cacheLocation, yourSizeInBytes);
}
});
2.内存当中的缓存和POOLS
GlideBuilder当中,允许开发者去设置内存当中图⽚缓存区的⼤⼩,主要涉及到的类包括MemoryCache和BitmapPool
2.1 ⼤⼩的设置
默认内存缓存的⼤⼩是⽤过MemorySizeCalculator来实现的,这个类会根据设备屏幕的⼤⼩,计算出⼀个合适的size,开发者可以获取到相关的默认设置信息:
MemorySizeCalculator calculator = new MemorySizeCalculator(context);
int defaultMemoryCacheSize = MemoryCacheSize();
int defaultBitmapPoolSize = BitmapPoolSize();
如果在应⽤当中想要调整内存缓存的⼤⼩,开发者可以通过如下⽅式:
<(context).setMemoryCategory(MemoryCategory.HIGH);
2.2 Memory Cache
Glide内存缓存的⽬的是减少I/O,提⾼效率
可以通过GlideBuidler的setMemoryCache(MemoryCache memoryCache)去设置缓存的⼤⼩,开发者可以通过LruResourceCache类去设置缓存区的⼤⼩
builder.setMemoryCache(new LruResourceCache(yourSizeInBytes));
2.3 Bitmap Pool
可以通过GlideBuilder的setBitmapPool()⽅法设置池⼦的⼤⼩,LruBitmapPool是Glide的默认实现,使⽤如下:
builder.setBitmapPool(new LruBitmapPool(sizeInBytes));
.图⽚格式
GlideBuilder允许开发者设置⼀个全局的默认图⽚格式,
在默认情况下,Glide使⽤RGB_565格式加载图⽚,如果想要使⽤⾼质量的图⽚,可以通过如下⽅式设置系统的图⽚格式:builder.setDecodeFormat(DecodeFormat.ALWAYS_ARGB_8888);
七、⾃定义显⽰控件
除了可以将图⽚、视频快照和GIFS显⽰在View上⾯之外,开发者也可以在⾃定义的Target上⾯显⽰这些媒体⽂件
1.SimpleTarget
重点内容
如果你想简单地加载⼀个Bitmap,你可以通过以下简单的⽅式⽽不是直接地显⽰给⽤户,可能是显⽰⼀个notification,或者上传⼀个头像,Glide都能很好地实现
mkdirs方法
SimpleTarget提供了对Target的简单实现,并且让你专注于对加载结果的处理
为了使⽤SimpleTarget,开发者需要提供⼀个宽和⾼的像素值,⽤来加载你的资源⽂件,并且你需要去实现
onResourceReady(R resource,GlideAnimation<? super R> glideAnimation)
int myWidth = 512;
int myHeight = 384;
Glide.with(yourApplicationContext))
.load(youUrl)
.asBitmap()
.into(new SimpleTarget<Bitmap>(myWidth, myHeight) {
@Override
public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
// Do something with bitmap here.
}
};
说明:
通常你去加载资源的时候,是将他们加载到⼀个view当中,当fragment或者activity失去焦点或者distroyed的时候,Glide会⾃动停⽌加载相关资源,确保资源不会被浪费
在⼤多数SimpleTarget的实现当中,如果需要资源的加载不受组件⽣命周期的影响,Glide.width(context)当中的context是application context⽽不是fragment或者activity

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。