2023年6月21日发(作者:)

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图⽚加载:(context).load(...).asBitmap() //显⽰gif静态图⽚(context).load(...).asGif() //显⽰gif动态图⽚2.本地视频快照:Glide现在还可以把视频解码为⼀张图⽚: (context).load(“视频路径“)(经过我的测试,只能把⼿机本地的mp4视频解析为⼀张图⽚,把mp4⽂件放在raw⽂件中,不能解析)3.对缩略图的⽀持: //加载yourView1/10尺⼨的缩略图,然后加载全图(yourFragment).load(yourUrl).thumbnail(0.1f).into(yourView)4.⽣命周期集成同时将Activity/Fragment作为with()参数的好处是:图⽚加载会和Activity/Fragment的⽣命周期保持⼀致,请求会在onStop的时候⾃动暂停,在onStart的时候重新启动,gif的动画也会在onStop的时候停⽌,以免在后台消耗电量。5.转码Glide的.toBytes()和.transcode()⽅法允许在后台获取、解码和转换⼀个图⽚,你可以将⼀张图⽚转换成更多有⽤的图⽚格式,⽐如,上传⼀张250*250的图⽚ (context) .load(“/user/profile/photo/path”) .asBitmap() .toBytes() .centerCrop() .into(new SimpleTarget(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(or))7. ⽹络模块可以选择OkHttp或者Volley的⽀持You can now choose to use either OkHttp, or Volley, or Glide's HttpUrlConnection default as your network 和OkHttp可以在gradle⽂件当中添加依赖,注册相应的ModelLoaderFactory

⼆、图⽚的缓存和缓存的时效机制1.图⽚缓存的键值图⽚缓存的键值主要⽤于,Glide当中的键值主要包含三个部分:通过()⽅法返回的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的⼀部分⽂件,使⽤StringSignatureUrls ,使⽤StringSignature (yourFragment) .load(yourFileDataModel) .signature(new StringSignature(yourVersionMetadata)) .into(yourImageView); (fragment) .load(mediaStoreUri) .signature(new MediaStoreSignature(mimeType, dateModified, orientation)) .into(view);⾃定义标识符: public class IntegerVersionSignature implements Key { private int currentVersion; public IntegerVersionSignature(int currentVersion) { tVersion = currentVersion; } @Override public boolean equals(Object o) { if (o instanceof IntegerVersionSignature) { IntegerVersionSignature other = (IntegerVersionSignature) o; return currentVersion = tVersion; } return false; } @Override public int hashCode() { return currentVersion; } @Override public void updateDiskCacheKey(MessageDigest md) { (te().putInt(signature).array()); }}2.2、不缓存可以通过diskCacheStrategy(.)实现

三、配置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. }}第⼆步、然后将上⾯的实现了加⼊到当中: -keepnames class * eModule第三步、在⽂件中添加meta-data,以便Glide能够找到你的Module 四、Library项⽬⼀个Library项⽬可能会定义⼀个或者多个GlideModules,如果⼀个Library项⽬添加⼀个Module到Library项⽬的manifest当中,依赖于此Library的应⽤就会⾃动加载依赖库(Library项⽬)当中的Module。当然,如果manifest的合并不正确,那么Library⾥⾯Module就必须⼿动地在应⽤当中添加进去。

五、GlideModules冲突虽然Glide允许⼀个应⽤当中存在多个GlideModules,Glide并不会按照⼀个特殊的顺序去调⽤已注册的GlideModules,如果⼀个应⽤的多个依赖⼯程当中有多个相同的Modules,就有可能会产⽣冲突。如果⼀个冲突是不可避免的,应⽤应该默认去定义⼀个⾃⼰的Module,⽤来⼿动地处理这个冲突,在进⾏Manifest合并的时候,可以⽤下⾯的标签排除冲突的module。 六、通过GlideBuilder配置全局配置⽂件Glide允许开发者配置⾃定义的全局操作应⽤于所有的请求,这个部分可以通过GlideModule接⼝中的applyOptions⽅法的GlideBuilder参数实现 :che1.1、硬盘缓存是在⼀个后台线程当中,通过⼀个y接⼝进⾏缓存的。开发者能够通过GlideBuilder的setDiskCache(y df)⽅法设置存储的位置和⼤⼩通过传⼊DiskCacheAdapter来完全禁⽤缓存⾃定义⼀个DiskCache来完全禁⽤缓存,Glide默认是⽤InternalCacheDiskCacheFactory类来创建硬盘缓存的,这个类会在应⽤的内部缓存⽬录下⾯创建⼀个最⼤容量250MB的缓存⽂件夹,使⽤这个缓存⽬录⽽不⽤sd卡,意味着除了本应⽤之外,其他应⽤是不能访问缓存的图⽚⽂件的。1.2.设置disk缓存的⼤⼩ : InternalCacheDiskCacheFactory kCache(new InternalCacheDiskCacheFactory(context, yourSizeInBytes));1.3.设置缓存的路径可以通过实现y,然后使⽤DiskLruCacheWrapper创建⼀个新的缓存⽬录,⽐如,可以通过如下⽅式在外存当中创建缓存⽬录: builder .setDiskCache(new y() { @Override public DiskCache build() {

// Careful: the external cache directory doesn't enforce permissions File cacheLocation = new File(ernalCacheDir(), "cache_dir_name"); (); return (cacheLocation, yourSizeInBytes); } });2.内存当中的缓存和POOLSGlideBuilder当中,允许开发者去设置内存当中图⽚缓存区的⼤⼩,主要涉及到的类包括MemoryCache和BitmapPool2.1 ⼤⼩的设置默认内存缓存的⼤⼩是⽤过MemorySizeCalculator来实现的,这个类会根据设备屏幕的⼤⼩,计算出⼀个合适的size,开发者可以获取到相关的默认设置信息: MemorySizeCalculator calculator = new MemorySizeCalculator(context);int defaultMemoryCacheSize = oryCacheSize();int defaultBitmapPoolSize = mapPoolSize();如果在应⽤当中想要调整内存缓存的⼤⼩,开发者可以通过如下⽅式:(context).setMemoryCategory();2.2 Memory CacheGlide内存缓存的⽬的是减少I/O,提⾼效率可以通过GlideBuidler的setMemoryCache(MemoryCache memoryCache)去设置缓存的⼤⼩,开发者可以通过LruResourceCache类去设置缓存区的⼤⼩oryCache(new LruResourceCache(yourSizeInBytes));2.3 Bitmap Pool可以通过GlideBuilder的setBitmapPool()⽅法设置池⼦的⼤⼩,LruBitmapPool是Glide的默认实现,使⽤如下: mapPool(new LruBitmapPool(sizeInBytes));.图⽚格式GlideBuilder允许开发者设置⼀个全局的默认图⽚格式,在默认情况下,Glide使⽤RGB_565格式加载图⽚,如果想要使⽤⾼质量的图⽚,可以通过如下⽅式设置系统的图⽚格式:

odeFormat(_ARGB_8888);七、⾃定义显⽰控件除了可以将图⽚、视频快照和GIFS显⽰在View上⾯之外,开发者也可以在⾃定义的Target上⾯显⽰这些媒体⽂件Target重点内容如果你想简单地加载⼀个Bitmap,你可以通过以下简单的⽅式⽽不是直接地显⽰给⽤户,可能是显⽰⼀个notification,或者上传⼀个头像,Glide都能很好地实现SimpleTarget提供了对Target的简单实现,并且让你专注于对加载结果的处理为了使⽤SimpleTarget,开发者需要提供⼀个宽和⾼的像素值,⽤来加载你的资源⽂件,并且你需要去实现 onResourceReady(R resource,GlideAnimation glideAnimation)int myWidth = 512;int myHeight = 384;(yourApplicationContext)) .load(youUrl) .asBitmap() .into(new SimpleTarget(myWidth, myHeight) { @Override public void onResourceReady(Bitmap bitmap, GlideAnimation anim) { // Do something with bitmap here. } };说明:通常你去加载资源的时候,是将他们加载到⼀个view当中,当fragment或者activity失去焦点或者distroyed的时候,Glide会⾃动停⽌加载相关资源,确保资源不会被浪费在⼤多数SimpleTarget的实现当中,如果需要资源的加载不受组件⽣命周期的影响,(context)当中的context是application context⽽不是fragment或者activity另外,由于⼀些long running operations可能会导致内存泄露,如果你打算使⽤⼀个这样的操作,可以考虑使⽤⼀个静态的内部类⽽不是⼀个动态的内部类。rget如果你想加载⼀张图⽚到⼀个view当中,但是⼜想改变或者监听Glide默认的部分设置,就可以通过重写ViewTarget或者他的⼦类来实现如果你想Gidle加载图⽚的时候可以⾃定义图⽚的⼤⼩,或者想要设置⼀个⾃定义的显⽰动画,就可以通过ViewTarget来实现,可以通过⼀个静态的ViewTarget或者动态的内部类来实现相关的功能 (yourFragment) .load(yourUrl) .into(new ViewTarget(yourViewObject) { @Override public void onResourceReady(GlideDrawable resource, GlideAnimation anim) { YourViewClass myView = ; // Set your resource on myView and/or start your animation here. } });说明:加载⼀张静态的图⽚或者⼀张GIF动态图,可以在load后⾯加上asBitmap()/asGif().Load(url)会通过asXXX()替换ViewTarget当中的GlideDrawable参数,也可以通过实现LifecycleLisener,给target设置⼀个回调。3.覆盖默认的相关设置如果只是想使⽤Glide的默认配置,可以使⽤Glide当中ImageViewTargets的两个⼦类:GlideDrawableImageViewTarget 默认的实现,可以通过asGif()加载动态图⽚BitmapImageViewTarget 可以通过asBitmap()加载静态图⽚如果想要使⽤Glide默认实现,可以在他们的⼦类⽅法当中使⽤()即可,例如: (yourFragment) .load(yourUrl) .asBitmap() .into(new BitmapImageViewTarget(yourImageView)) { @Override public void onResourceReady(Bitmap bitmap, GlideAnimation anim) { urceReady(bitmap, anim); teAsync(bitmap, new eAsyncListener() {

@Override public void onGenerated(Palette palette) { // Here's your generated palette } }); } });⼋、使⽤Glide下载⾃定义尺⼨的图⽚Glide的ModelLoader接⼝为开发者提供了装载图⽚的view的尺⼨,并且允许开发者使⽤这些尺⼨信息去选择合适的URL去下载图⽚。选⽤适当的尺⼨可以节省宽带和设备的空间开销,提⾼app的性能1、通过http/https下载图⽚,可以通过继承BaseGlideUtlLoader来实现: public interface MyDataModel { public String buildUrl(int width, int height);}

public class MyUrlLoader extends BaseGlideUrlLoader { @Override protected String getUrl(MyDataModel model, int width, int height) { // Construct the url for the correct size here. return rl(width, height); }}2、可以使⽤你⾃定义的ModelLoader去加载图⽚了 (yourFragment) .using(new MyUrlLoader()) .load(yourModel) .into(yourView);如果你想避免每次加载图⽚都要使⽤.using(new MyUrlLoader()) ,可以实现是⼀个ModelLoaderFactory然后使⽤Glide将它注册到GlideModule当中 public class MyGlideModule implements GlideModule { ... @Override public void registerComponents(Context context, Glide glide) { er(, ,

new y()); }}这样你就可以跳过.using()了 (yourFragment) .load(yourModel) .into(yourView);九、集成库1.什么是集成库Glide包含⼀些⼩的、可选的集成库,⽬前Glide集成库当中包含了访问⽹络操作的Volley和OkHttp2.为什么要包含集成库这些集成库,和Glide的ModelLoader系统允许开发者使⽤⼀致地框架去进⾏⽹络相关的操作3.如何将⼀个库集成到Glide当中,将⼀个库集成到Glide当中需要两步操作,包含正确的dependency,确保创建了该集成库的GlideModule,⽐如,将Volley集成到Glide当中第⼀步、添加依赖 dependencies { compile ':volley-integration:1.2.2' compile ':library:1.0.5'}第⼆步、创建Volley集成库的GlideModule 然后改变混淆⽂件: -keep class GlideModule#or-keep public class * implements odule将OkHttp集成到Glide当中:第⼀步、添加依赖 dependencies { compile ':okhttp-integration:1.2.2' compile ':okhttp:2.0.0'}第⼆步、创建OkHttp集成库的GlideModule -keep class GlideModule#or-keep public class * implements odule⼗、在后台线程当中进⾏加载和缓存为了保证Glide在后台线程当中加载资源⽂件更加容易,Glide除了(fragment).load(url).into(view)之外还提供了 downloadOnly(int width, int height)downloadOnly(Y target)// Y extends Targetinto(int width, int height)adOnlyGlide的downloadOnly()允许开发者将Image的⼆进制⽂件下载到硬盘缓存当中,以便在后续使⽤,在UI线程当中异步下载,在异步线程当中则是使⽤width和height在异步线程当中同步调⽤下载,在同步线程当中,downloadOnly使⽤⼀个target作为参数(1)在后台线程当中下载图⽚,可以通过如下的⽅式: FutureTarget future = (applicationContext) .load(yourUrl) .downloadOnly(500, 500);File cacheFile = ();当future返回的时候,image的⼆进制⽂件信息就存⼊了disk缓存了,值得注意的是downloadOnly API只是保证图⽚个bytes数据在disk当中是有效的。(2)下载完毕之后如果想要进⾏显⽰,可以通过如下⽅式进⾏调⽤: (yourFragment) .load(yourUrl) .diskCacheStrategy() .into(yourView);通过或者,可以保证程序会去读取缓存⽂件2. 如果想要在后台线程当中获取某个URL对应的Bitmap不通过downloadOnly,可以使⽤into(),会返回⼀个FutureTarget对象,⽐如,想要得到⼀个URL对应的500*500的centerCrop裁剪图⽚,可以通过如下⽅式实现: Bitmap myBitmap = (applicationContext) .load(yourUrl) .asBitmap() .centerCrop() .into(500, 500) .get()注意:上⾯的调⽤只能在异步线程当中,如果在main Thread当中调⽤.get(),会阻塞主线⼗⼀、转换器1.默认的转换器⽤法:

(yourFragment) .load(yourUrl) .fitCenter() .into(yourView); (yourFragment) .load(yourUrl) .centerCrop() .into(yourView); // For Bitmaps:(yourFragment) .load(yourUrl) .asBitmap() .centerCrop() .into(yourView);// For gifs:(yourFragment) .load(yourUrl) .asGif() .fitCenter() .into(yourView);甚⾄可以在两幅图⽚进⾏类型转换的时候进⾏transformed (yourFragment) .load(yourUrl) .asBitmap() .toBytes() .centerCrop() .into(new SimpleTarget(...) { ... });2.⾃定义转换器⽅法:第⼀步、编写转换器类 ,继承BitmapTransformation:

private static class MyTransformation extends BitmapTransformation { public MyTransformation(Context context) { super(context); } @Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform,

int outWidth, int outHeight) { Bitmap myTransformedBitmap = ... // apply some transformation here. return myTransformedBitmap; } @Override public String getId() { // Return some id that uniquely identifies your transformation. return "sformation"; }}第⼆步、在Glide⽅法链当中⽤.transform(…)替换fitCenter()/centerCrop() (yourFragment) .load(yourUrl) .transform(new MyTransformation(context)) .into(yourView);// For Bitmaps:(yourFragment) .load(yourUrl) .asBitmap() .transform(new MyTransformation(context)) .into(yourView);// For Gifs:(yourFragment) .load(yourUrl) .asGif() .transform(new MyTransformation(context)) .into(yourView);3.⾃定义转换器的尺⼨在上⾯使⽤过程当中没有设置尺⼨值,那么转换器转换的图⽚尺⼨怎么确定呢,Glide实际上已经⾜够智能根据view的尺⼨来确定转换图⽚的尺⼨了如果需要⾃定义尺⼨,⽽不是⽤view和target当中的尺⼨,那么可以使⽤override(int,int)设置相关的宽和⾼4. Bitmap 再利⽤为了减少垃圾收集,可以通过BitmapPool接⼝去释放不需要的Bitmaps,当然也可以对⾥⾯的bitmap进⾏再利⽤。例如在⼀次转换中,从pool当中得到⼀个bitmap将Bitmap回设给Canvas使⽤Matrix、Paint在Canvas上⾯绘制原始的Bitmap,或者通过⼀个Shader来转换⼀个image4.1 不要⼿动地去释放⼀个转换的bitmap资源,也不要将transform()之后的Bitmap重新放置到BitmapPool当中去 protected Bitmap transform(BitmapPool bitmapPool, Bitmap original, int width, int height) { Bitmap result = (width, height, _8888); // If no matching Bitmap is in the pool, get will return null, so we should //allocate. if (result == null) { // Use ARGB_8888 since we're going to add alpha to the image. result = Bitmap(width, height, _8888); } // Create a Canvas backed by the result Bitmap. Canvas canvas = new Canvas(result); Paint paint = new Paint(); ha(128); // Draw the original Bitmap onto the result Bitmap with a transformation. tmap(original, 0, 0, paint); // Since we've replaced our original Bitmap, we return our new Bitmap here. Glide will // will take care of returning our original Bitmap to the BitmapPool for us.

return result;}

2023年6月21日发(作者:)

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图⽚加载:(context).load(...).asBitmap() //显⽰gif静态图⽚(context).load(...).asGif() //显⽰gif动态图⽚2.本地视频快照:Glide现在还可以把视频解码为⼀张图⽚: (context).load(“视频路径“)(经过我的测试,只能把⼿机本地的mp4视频解析为⼀张图⽚,把mp4⽂件放在raw⽂件中,不能解析)3.对缩略图的⽀持: //加载yourView1/10尺⼨的缩略图,然后加载全图(yourFragment).load(yourUrl).thumbnail(0.1f).into(yourView)4.⽣命周期集成同时将Activity/Fragment作为with()参数的好处是:图⽚加载会和Activity/Fragment的⽣命周期保持⼀致,请求会在onStop的时候⾃动暂停,在onStart的时候重新启动,gif的动画也会在onStop的时候停⽌,以免在后台消耗电量。5.转码Glide的.toBytes()和.transcode()⽅法允许在后台获取、解码和转换⼀个图⽚,你可以将⼀张图⽚转换成更多有⽤的图⽚格式,⽐如,上传⼀张250*250的图⽚ (context) .load(“/user/profile/photo/path”) .asBitmap() .toBytes() .centerCrop() .into(new SimpleTarget(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(or))7. ⽹络模块可以选择OkHttp或者Volley的⽀持You can now choose to use either OkHttp, or Volley, or Glide's HttpUrlConnection default as your network 和OkHttp可以在gradle⽂件当中添加依赖,注册相应的ModelLoaderFactory

⼆、图⽚的缓存和缓存的时效机制1.图⽚缓存的键值图⽚缓存的键值主要⽤于,Glide当中的键值主要包含三个部分:通过()⽅法返回的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的⼀部分⽂件,使⽤StringSignatureUrls ,使⽤StringSignature (yourFragment) .load(yourFileDataModel) .signature(new StringSignature(yourVersionMetadata)) .into(yourImageView); (fragment) .load(mediaStoreUri) .signature(new MediaStoreSignature(mimeType, dateModified, orientation)) .into(view);⾃定义标识符: public class IntegerVersionSignature implements Key { private int currentVersion; public IntegerVersionSignature(int currentVersion) { tVersion = currentVersion; } @Override public boolean equals(Object o) { if (o instanceof IntegerVersionSignature) { IntegerVersionSignature other = (IntegerVersionSignature) o; return currentVersion = tVersion; } return false; } @Override public int hashCode() { return currentVersion; } @Override public void updateDiskCacheKey(MessageDigest md) { (te().putInt(signature).array()); }}2.2、不缓存可以通过diskCacheStrategy(.)实现

三、配置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. }}第⼆步、然后将上⾯的实现了加⼊到当中: -keepnames class * eModule第三步、在⽂件中添加meta-data,以便Glide能够找到你的Module 四、Library项⽬⼀个Library项⽬可能会定义⼀个或者多个GlideModules,如果⼀个Library项⽬添加⼀个Module到Library项⽬的manifest当中,依赖于此Library的应⽤就会⾃动加载依赖库(Library项⽬)当中的Module。当然,如果manifest的合并不正确,那么Library⾥⾯Module就必须⼿动地在应⽤当中添加进去。

五、GlideModules冲突虽然Glide允许⼀个应⽤当中存在多个GlideModules,Glide并不会按照⼀个特殊的顺序去调⽤已注册的GlideModules,如果⼀个应⽤的多个依赖⼯程当中有多个相同的Modules,就有可能会产⽣冲突。如果⼀个冲突是不可避免的,应⽤应该默认去定义⼀个⾃⼰的Module,⽤来⼿动地处理这个冲突,在进⾏Manifest合并的时候,可以⽤下⾯的标签排除冲突的module。 六、通过GlideBuilder配置全局配置⽂件Glide允许开发者配置⾃定义的全局操作应⽤于所有的请求,这个部分可以通过GlideModule接⼝中的applyOptions⽅法的GlideBuilder参数实现 :che1.1、硬盘缓存是在⼀个后台线程当中,通过⼀个y接⼝进⾏缓存的。开发者能够通过GlideBuilder的setDiskCache(y df)⽅法设置存储的位置和⼤⼩通过传⼊DiskCacheAdapter来完全禁⽤缓存⾃定义⼀个DiskCache来完全禁⽤缓存,Glide默认是⽤InternalCacheDiskCacheFactory类来创建硬盘缓存的,这个类会在应⽤的内部缓存⽬录下⾯创建⼀个最⼤容量250MB的缓存⽂件夹,使⽤这个缓存⽬录⽽不⽤sd卡,意味着除了本应⽤之外,其他应⽤是不能访问缓存的图⽚⽂件的。1.2.设置disk缓存的⼤⼩ : InternalCacheDiskCacheFactory kCache(new InternalCacheDiskCacheFactory(context, yourSizeInBytes));1.3.设置缓存的路径可以通过实现y,然后使⽤DiskLruCacheWrapper创建⼀个新的缓存⽬录,⽐如,可以通过如下⽅式在外存当中创建缓存⽬录: builder .setDiskCache(new y() { @Override public DiskCache build() {

// Careful: the external cache directory doesn't enforce permissions File cacheLocation = new File(ernalCacheDir(), "cache_dir_name"); (); return (cacheLocation, yourSizeInBytes); } });2.内存当中的缓存和POOLSGlideBuilder当中,允许开发者去设置内存当中图⽚缓存区的⼤⼩,主要涉及到的类包括MemoryCache和BitmapPool2.1 ⼤⼩的设置默认内存缓存的⼤⼩是⽤过MemorySizeCalculator来实现的,这个类会根据设备屏幕的⼤⼩,计算出⼀个合适的size,开发者可以获取到相关的默认设置信息: MemorySizeCalculator calculator = new MemorySizeCalculator(context);int defaultMemoryCacheSize = oryCacheSize();int defaultBitmapPoolSize = mapPoolSize();如果在应⽤当中想要调整内存缓存的⼤⼩,开发者可以通过如下⽅式:(context).setMemoryCategory();2.2 Memory CacheGlide内存缓存的⽬的是减少I/O,提⾼效率可以通过GlideBuidler的setMemoryCache(MemoryCache memoryCache)去设置缓存的⼤⼩,开发者可以通过LruResourceCache类去设置缓存区的⼤⼩oryCache(new LruResourceCache(yourSizeInBytes));2.3 Bitmap Pool可以通过GlideBuilder的setBitmapPool()⽅法设置池⼦的⼤⼩,LruBitmapPool是Glide的默认实现,使⽤如下: mapPool(new LruBitmapPool(sizeInBytes));.图⽚格式GlideBuilder允许开发者设置⼀个全局的默认图⽚格式,在默认情况下,Glide使⽤RGB_565格式加载图⽚,如果想要使⽤⾼质量的图⽚,可以通过如下⽅式设置系统的图⽚格式:

odeFormat(_ARGB_8888);七、⾃定义显⽰控件除了可以将图⽚、视频快照和GIFS显⽰在View上⾯之外,开发者也可以在⾃定义的Target上⾯显⽰这些媒体⽂件Target重点内容如果你想简单地加载⼀个Bitmap,你可以通过以下简单的⽅式⽽不是直接地显⽰给⽤户,可能是显⽰⼀个notification,或者上传⼀个头像,Glide都能很好地实现SimpleTarget提供了对Target的简单实现,并且让你专注于对加载结果的处理为了使⽤SimpleTarget,开发者需要提供⼀个宽和⾼的像素值,⽤来加载你的资源⽂件,并且你需要去实现 onResourceReady(R resource,GlideAnimation glideAnimation)int myWidth = 512;int myHeight = 384;(yourApplicationContext)) .load(youUrl) .asBitmap() .into(new SimpleTarget(myWidth, myHeight) { @Override public void onResourceReady(Bitmap bitmap, GlideAnimation anim) { // Do something with bitmap here. } };说明:通常你去加载资源的时候,是将他们加载到⼀个view当中,当fragment或者activity失去焦点或者distroyed的时候,Glide会⾃动停⽌加载相关资源,确保资源不会被浪费在⼤多数SimpleTarget的实现当中,如果需要资源的加载不受组件⽣命周期的影响,(context)当中的context是application context⽽不是fragment或者activity另外,由于⼀些long running operations可能会导致内存泄露,如果你打算使⽤⼀个这样的操作,可以考虑使⽤⼀个静态的内部类⽽不是⼀个动态的内部类。rget如果你想加载⼀张图⽚到⼀个view当中,但是⼜想改变或者监听Glide默认的部分设置,就可以通过重写ViewTarget或者他的⼦类来实现如果你想Gidle加载图⽚的时候可以⾃定义图⽚的⼤⼩,或者想要设置⼀个⾃定义的显⽰动画,就可以通过ViewTarget来实现,可以通过⼀个静态的ViewTarget或者动态的内部类来实现相关的功能 (yourFragment) .load(yourUrl) .into(new ViewTarget(yourViewObject) { @Override public void onResourceReady(GlideDrawable resource, GlideAnimation anim) { YourViewClass myView = ; // Set your resource on myView and/or start your animation here. } });说明:加载⼀张静态的图⽚或者⼀张GIF动态图,可以在load后⾯加上asBitmap()/asGif().Load(url)会通过asXXX()替换ViewTarget当中的GlideDrawable参数,也可以通过实现LifecycleLisener,给target设置⼀个回调。3.覆盖默认的相关设置如果只是想使⽤Glide的默认配置,可以使⽤Glide当中ImageViewTargets的两个⼦类:GlideDrawableImageViewTarget 默认的实现,可以通过asGif()加载动态图⽚BitmapImageViewTarget 可以通过asBitmap()加载静态图⽚如果想要使⽤Glide默认实现,可以在他们的⼦类⽅法当中使⽤()即可,例如: (yourFragment) .load(yourUrl) .asBitmap() .into(new BitmapImageViewTarget(yourImageView)) { @Override public void onResourceReady(Bitmap bitmap, GlideAnimation anim) { urceReady(bitmap, anim); teAsync(bitmap, new eAsyncListener() {

@Override public void onGenerated(Palette palette) { // Here's your generated palette } }); } });⼋、使⽤Glide下载⾃定义尺⼨的图⽚Glide的ModelLoader接⼝为开发者提供了装载图⽚的view的尺⼨,并且允许开发者使⽤这些尺⼨信息去选择合适的URL去下载图⽚。选⽤适当的尺⼨可以节省宽带和设备的空间开销,提⾼app的性能1、通过http/https下载图⽚,可以通过继承BaseGlideUtlLoader来实现: public interface MyDataModel { public String buildUrl(int width, int height);}

public class MyUrlLoader extends BaseGlideUrlLoader { @Override protected String getUrl(MyDataModel model, int width, int height) { // Construct the url for the correct size here. return rl(width, height); }}2、可以使⽤你⾃定义的ModelLoader去加载图⽚了 (yourFragment) .using(new MyUrlLoader()) .load(yourModel) .into(yourView);如果你想避免每次加载图⽚都要使⽤.using(new MyUrlLoader()) ,可以实现是⼀个ModelLoaderFactory然后使⽤Glide将它注册到GlideModule当中 public class MyGlideModule implements GlideModule { ... @Override public void registerComponents(Context context, Glide glide) { er(, ,

new y()); }}这样你就可以跳过.using()了 (yourFragment) .load(yourModel) .into(yourView);九、集成库1.什么是集成库Glide包含⼀些⼩的、可选的集成库,⽬前Glide集成库当中包含了访问⽹络操作的Volley和OkHttp2.为什么要包含集成库这些集成库,和Glide的ModelLoader系统允许开发者使⽤⼀致地框架去进⾏⽹络相关的操作3.如何将⼀个库集成到Glide当中,将⼀个库集成到Glide当中需要两步操作,包含正确的dependency,确保创建了该集成库的GlideModule,⽐如,将Volley集成到Glide当中第⼀步、添加依赖 dependencies { compile ':volley-integration:1.2.2' compile ':library:1.0.5'}第⼆步、创建Volley集成库的GlideModule 然后改变混淆⽂件: -keep class GlideModule#or-keep public class * implements odule将OkHttp集成到Glide当中:第⼀步、添加依赖 dependencies { compile ':okhttp-integration:1.2.2' compile ':okhttp:2.0.0'}第⼆步、创建OkHttp集成库的GlideModule -keep class GlideModule#or-keep public class * implements odule⼗、在后台线程当中进⾏加载和缓存为了保证Glide在后台线程当中加载资源⽂件更加容易,Glide除了(fragment).load(url).into(view)之外还提供了 downloadOnly(int width, int height)downloadOnly(Y target)// Y extends Targetinto(int width, int height)adOnlyGlide的downloadOnly()允许开发者将Image的⼆进制⽂件下载到硬盘缓存当中,以便在后续使⽤,在UI线程当中异步下载,在异步线程当中则是使⽤width和height在异步线程当中同步调⽤下载,在同步线程当中,downloadOnly使⽤⼀个target作为参数(1)在后台线程当中下载图⽚,可以通过如下的⽅式: FutureTarget future = (applicationContext) .load(yourUrl) .downloadOnly(500, 500);File cacheFile = ();当future返回的时候,image的⼆进制⽂件信息就存⼊了disk缓存了,值得注意的是downloadOnly API只是保证图⽚个bytes数据在disk当中是有效的。(2)下载完毕之后如果想要进⾏显⽰,可以通过如下⽅式进⾏调⽤: (yourFragment) .load(yourUrl) .diskCacheStrategy() .into(yourView);通过或者,可以保证程序会去读取缓存⽂件2. 如果想要在后台线程当中获取某个URL对应的Bitmap不通过downloadOnly,可以使⽤into(),会返回⼀个FutureTarget对象,⽐如,想要得到⼀个URL对应的500*500的centerCrop裁剪图⽚,可以通过如下⽅式实现: Bitmap myBitmap = (applicationContext) .load(yourUrl) .asBitmap() .centerCrop() .into(500, 500) .get()注意:上⾯的调⽤只能在异步线程当中,如果在main Thread当中调⽤.get(),会阻塞主线⼗⼀、转换器1.默认的转换器⽤法:

(yourFragment) .load(yourUrl) .fitCenter() .into(yourView); (yourFragment) .load(yourUrl) .centerCrop() .into(yourView); // For Bitmaps:(yourFragment) .load(yourUrl) .asBitmap() .centerCrop() .into(yourView);// For gifs:(yourFragment) .load(yourUrl) .asGif() .fitCenter() .into(yourView);甚⾄可以在两幅图⽚进⾏类型转换的时候进⾏transformed (yourFragment) .load(yourUrl) .asBitmap() .toBytes() .centerCrop() .into(new SimpleTarget(...) { ... });2.⾃定义转换器⽅法:第⼀步、编写转换器类 ,继承BitmapTransformation:

private static class MyTransformation extends BitmapTransformation { public MyTransformation(Context context) { super(context); } @Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform,

int outWidth, int outHeight) { Bitmap myTransformedBitmap = ... // apply some transformation here. return myTransformedBitmap; } @Override public String getId() { // Return some id that uniquely identifies your transformation. return "sformation"; }}第⼆步、在Glide⽅法链当中⽤.transform(…)替换fitCenter()/centerCrop() (yourFragment) .load(yourUrl) .transform(new MyTransformation(context)) .into(yourView);// For Bitmaps:(yourFragment) .load(yourUrl) .asBitmap() .transform(new MyTransformation(context)) .into(yourView);// For Gifs:(yourFragment) .load(yourUrl) .asGif() .transform(new MyTransformation(context)) .into(yourView);3.⾃定义转换器的尺⼨在上⾯使⽤过程当中没有设置尺⼨值,那么转换器转换的图⽚尺⼨怎么确定呢,Glide实际上已经⾜够智能根据view的尺⼨来确定转换图⽚的尺⼨了如果需要⾃定义尺⼨,⽽不是⽤view和target当中的尺⼨,那么可以使⽤override(int,int)设置相关的宽和⾼4. Bitmap 再利⽤为了减少垃圾收集,可以通过BitmapPool接⼝去释放不需要的Bitmaps,当然也可以对⾥⾯的bitmap进⾏再利⽤。例如在⼀次转换中,从pool当中得到⼀个bitmap将Bitmap回设给Canvas使⽤Matrix、Paint在Canvas上⾯绘制原始的Bitmap,或者通过⼀个Shader来转换⼀个image4.1 不要⼿动地去释放⼀个转换的bitmap资源,也不要将transform()之后的Bitmap重新放置到BitmapPool当中去 protected Bitmap transform(BitmapPool bitmapPool, Bitmap original, int width, int height) { Bitmap result = (width, height, _8888); // If no matching Bitmap is in the pool, get will return null, so we should //allocate. if (result == null) { // Use ARGB_8888 since we're going to add alpha to the image. result = Bitmap(width, height, _8888); } // Create a Canvas backed by the result Bitmap. Canvas canvas = new Canvas(result); Paint paint = new Paint(); ha(128); // Draw the original Bitmap onto the result Bitmap with a transformation. tmap(original, 0, 0, paint); // Since we've replaced our original Bitmap, we return our new Bitmap here. Glide will // will take care of returning our original Bitmap to the BitmapPool for us.

return result;}