Glide详解

原文:https://github.com/bumptech/glide/wiki

参考:http://blog.csdn.net/fandong12388/article/details/46372255

Glide作者的GitHub地址:https://github.com/bumptech/glide
jar下载地址:https://github.com/bumptech/glide/releases

Glide和Picasso对比介绍:http://blog.csdn.net/cym492224103/article/details/45055341

Glide 简单示例:http://blog.csdn.net/zhangphil/article/details/45535693

一、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当中的键值主要包含三个部分:

  1. 通过DataFetcher.getId()方法返回的String数据作为键值。一般的DataFetchers会简单返回数据模型data model的toString()结果,如果是URL/File会返回相应的路径

  2. 图片的尺寸,主要是通过override(width,height)或者通过Target’s getSize()方法确定的尺寸信息

  3. 包含一个可选的签名所有的这些东西会通过一种散列算法生成一个独有、安全的文件名,通过此文件名将图片缓存在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一样注册相关组件。
1.包含一个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 * com.mypackage.MyGlideModule

第三步、在AndroidManifest.xml文件中添加meta-data,以便Glide能够找到你的Module

<meta-data
 android:name="com.example.xx.glide"
    android:value="MyGlideModule " />

四、Library项目

一个Library项目可能会定义一个或者多个GlideModules,如果一个Library项目添加一个Module到Library项目的manifest当中,依赖于此Library的应用就会自动加载依赖库(Library项目)当中的Module。

当然,如果manifest的合并不正确,那么Library里面Module就必须手动地在应用当中添加进去

五、GlideModules冲突

虽然Glide允许一个应用当中存在多个GlideModules,Glide并不会按照一个特殊的顺序去调用已注册的GlideModules,如果一个应用的多个依赖工程当中有多个相同的Modules,就有可能会产生冲突。

如果一个冲突是不可避免的,应用应该默认去定义一个自己的Module,用来手动地处理这个冲突,在进行Manifest合并的时候,可以用下面的标签排除冲突的module

<meta-data android:name="com.example.xx.glide" 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 File(context.getExternalCacheDir(), "cache_dir_name");
            cacheLocation.mkdirs();
            return DiskLruCacheWrapper.get(cacheLocation, yourSizeInBytes);
        }
    });

2.内存当中的缓存和POOLS

GlideBuilder当中,允许开发者去设置内存当中图片缓存区的大小,主要涉及到的类包括MemoryCache和BitmapPool

2.1 大小的设置

默认内存缓存的大小是用过MemorySizeCalculator来实现的,这个类会根据设备屏幕的大小,计算出一个合适的size,开发者可以获取到相关的默认设置信息:

MemorySizeCalculator calculator = new MemorySizeCalculator(context);
int defaultMemoryCacheSize = calculator.getMemoryCacheSize();
int defaultBitmapPoolSize = calculator.getBitmapPoolSize();

如果在应用当中想要调整内存缓存的大小,开发者可以通过如下方式:

Glide.get(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));

3.图片格式

GlideBuilder允许开发者设置一个全局的默认图片格式,
在默认情况下,Glide使用RGB_565格式加载图片,如果想要使用高质量的图片,可以通过如下方式设置系统的图片格式:

builder.setDecodeFormat(DecodeFormat.ALWAYS_ARGB_8888);

七、自定义显示控件

除了可以将图片、视频快照和GIFS显示在View上面之外,开发者也可以在自定义的Target上面显示这些媒体文件

1.SimpleTarget

重点内容

  • 如果你想简单地加载一个Bitmap,你可以通过以下简单的方式而不是直接地显示给用户,可能是显示一个notification,或者上传一个头像,Glide都能很好地实现
  • 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
  • 另外,由于一些long running operations可能会导致内存泄露,如果你打算使用一个这样的操作,可以考虑使用一个静态的内部类而不是一个动态的内部类。

2.ViewTarget

如果你想加载一张图片到一个view当中,但是又想改变或者监听Glide默认的部分设置,就可以通过重写ViewTarget或者他的子类来实现
如果你想Gidle加载图片的时候可以自定义图片的大小,或者想要设置一个自定义的显示动画,就可以通过ViewTarget来实现,可以通过一个静态的ViewTarget或者动态的内部类来实现相关的功能

Glide.with(yourFragment)
    .load(yourUrl) .into(new ViewTarget<YourViewClass, GlideDrawable>(yourViewObject) { @Override public void onResourceReady(GlideDrawable resource, GlideAnimation anim) { YourViewClass myView = this.view;
            // 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默认实现,可以在他们的子类方法当中使用super.xx()即可,例如:
Glide.with(yourFragment)
    .load(yourUrl)
    .asBitmap()
    .into(new BitmapImageViewTarget(yourImageView)) {
        @Override
        public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
            super.onResourceReady(bitmap, anim);
            Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {  
                @Override
                public void onGenerated(Palette palette) {
                    // Here's your generated palette
                }
            });
        }
    });

八、使用Glide下载自定义尺寸的图片

Glide的ModelLoader接口为开发者提供了装载图片的view的尺寸,并且允许开发者使用这些尺寸信息去选择合适的URL去下载图片。选用适当的尺寸可以节省宽带和设备的空间开销,提高app的性能
2014年googleI/o大会发表了一篇文章,阐述了他们如何使用ModelLoader接口去适配图片的尺寸,见下面的连接:https://github.com/google/iosched/blob/master/doc/IMAGES.md

1、通过http/https下载图片,可以通过继承BaseGlideUtlLoader来实现:

public interface MyDataModel {
    public String buildUrl(int width, int height);
} 
public class MyUrlLoader extends BaseGlideUrlLoader<MyDataModel> {
    @Override
    protected String getUrl(MyDataModel model, int width, int height) {
        // Construct the url for the correct size here.
        return model.buildUrl(width, height);
    }
}

2、可以使用你自定义的ModelLoader去加载图片了

Glide.with(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) {
        glide.register(MyDataModel.class, InputStream.class, 
            new MyUrlLoader.Factory());
    }
}

这样你就可以跳过.using()了

Glide.with(yourFragment)
    .load(yourModel) .into(yourView);

九、集成库

1.什么是集成库

Glide包含一些小的、可选的集成库,目前Glide集成库当中包含了访问网络操作的Volley和OkHttp

2.为什么要包含集成库

这些集成库,和Glide的ModelLoader系统允许开发者使用一致地框架去进行网络相关的操作

3.如何将一个库集成到Glide当中,

将一个库集成到Glide当中需要两步操作,

  1. 包含正确的dependency,
  2. 确保创建了该集成库的GlideModule,比如,

将Volley集成到Glide当中

第一步、添加依赖

dependencies { compile 'com.github.bumptech.glide:volley-integration:1.2.2' compile 'com.mcxiaoke.volley:library:1.0.5' }

第二步、创建Volley集成库的GlideModule

<meta-data
 android:name="com.bumptech.glide.integration.volley.VolleyGlideModule"
    android:value="GlideModule" />

然后改变混淆文件:

-keep class com.bumptech.glide.integration.volley.VolleyGlideModule
#or
-keep public class * implements com.bumptech.glide.module.GlideModule

将OkHttp集成到Glide当中

第一步、添加依赖

dependencies { compile 'com.github.bumptech.glide:okhttp-integration:1.2.2' compile 'com.squareup.okhttp:okhttp:2.0.0' }

第二步、创建OkHttp集成库的GlideModule

<meta-data
    android:name="com.bumptech.glide.integration.okhttp.OkHttpGlideModule"
android:value="GlideModule" />

-keep class com.bumptech.glide.integration.okhttp.OkHttpGlideModule
#or
-keep public class * implements com.bumptech.glide.module.GlideModule

十、在后台线程当中进行加载和缓存

为了保证Glide在后台线程当中加载资源文件更加容易,Glide除了Glide.with(fragment).load(url).into(view)之外还提供了

downloadOnly(int width, int height) downloadOnly(Y target)// Y extends Target<File> into(int width, int height)

1.downloadOnly

Glide的downloadOnly()允许开发者将Image的二进制文件下载到硬盘缓存当中,以便在后续使用,

  • 在UI线程当中异步下载,在异步线程当中则是使用width和height
  • 在异步线程当中同步调用下载,在同步线程当中,
    downloadOnly使用一个target作为参数

1、 在后台线程当中下载图片,可以通过如下的方式:

FutureTarget<File> future = Glide.with(applicationContext)
    .load(yourUrl)
    .downloadOnly(500, 500);
File cacheFile = future.get();

当future返回的时候,image的二进制文件信息就存入了disk缓存了,值得注意的是downloadOnly API只是保证图片个bytes数据在disk当中是有效的。

2、下载完毕之后如果想要进行显示,可以通过如下方式进行调用:

Glide.with(yourFragment)
    .load(yourUrl)
    .diskCacheStrategy(DiskCacheStrategy.ALL)
    .into(yourView);
Glide.with(yourFragment)
    .load(yourUrl)
    .diskCacheStrategy(DiskCacheStrategy.ALL)
    .into(yourView);

通过DiskCacheStrategy.ALL或者DiskCacheStrategy.SOURCE,可以保证程序会去读取缓存文件

2. 如果想要在后台线程当中获取某个URL对应的Bitmap

不通过downloadOnly,可以使用into(),会返回一个FutureTarget对象,比如,想要得到一个URL对应的500*500的centerCrop裁剪图片,可以通过如下方式实现:

Bitmap myBitmap = Glide.with(applicationContext)
    .load(yourUrl)
    .asBitmap()
    .centerCrop()
    .into(500, 500)
    .get()

注意:上面的调用只能在异步线程当中,如果在main Thread当中调用.get(),会阻塞主线

十一、转换器

1.默认的转换器

Glide两个默认的转换器,fitCenter和CenterCrop,其他的转换器详见https://github.com/wasabeef/glide-transformations,可以将图片转为各种形状,例如圆形,圆角型等等

用法:

Glide.with(yourFragment)
    .load(yourUrl)
    .fitCenter()
    .into(yourView);
Glide.with(yourFragment)
    .load(yourUrl)
    .centerCrop()
    .into(yourView);
// For Bitmaps:
Glide.with(yourFragment)
    .load(yourUrl)
    .asBitmap()
    .centerCrop()
    .into(yourView);
// For gifs:
Glide.with(yourFragment)
    .load(yourUrl)
    .asGif()
    .fitCenter()
    .into(yourView);

甚至可以在两幅图片进行类型转换的时候进行transformed

Glide.with(yourFragment)
    .load(yourUrl)
    .asBitmap()
    .toBytes()
    .centerCrop()
    .into(new SimpleTarget<byte[]>(...) { ... });

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 "com.example.myapp.MyTransformation";
    }
}

第二步、在Glide方法链当中用.transform(…)替换fitCenter()/centerCrop()

Glide.with(yourFragment)
    .load(yourUrl)
    .transform(new MyTransformation(context))
    .into(yourView);
// For Bitmaps:
Glide.with(yourFragment)
    .load(yourUrl)
    .asBitmap()
    .transform(new MyTransformation(context))
    .into(yourView);
// For Gifs:
Glide.with(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来转换一个image

4.1 不要手动地去释放一个转换的bitmap资源,也不要将transform()之后的Bitmap重新放置到BitmapPool当中去

protected Bitmap transform(BitmapPool bitmapPool, Bitmap original, int width, int height) {
    Bitmap result = bitmapPool.get(width, height, Bitmap.Config.ARGB_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.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    }
    // Create a Canvas backed by the result Bitmap.
    Canvas canvas = new Canvas(result);
    Paint paint = new Paint();
    paint.setAlpha(128);
    // Draw the original Bitmap onto the result Bitmap with a transformation.
    canvas.drawBitmap(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;
}

你可能感兴趣的:(Glide)