Glide介绍与使用

1. Glide如何绑定生命周期

2. Glide如何实现图片缓存的

3. Glide如何实现图片压缩的

4. Glide与Picasso相比为什么选择使用Glide

5. 高清巨图的加载

Glide的自定义模块功能

Glide3 和 Glide4的区别


Glide.with(this).load(url).into(imageView);
  1. with() : Glide.with()方法用于创建一个加载图片的实例。with()方法可以接收Context、Activity或者Fragment类型的参数。with()方法中传入的实例会决定Glide加载图片的生命周期,如果传入的是Activity或者Fragment的实例,那么当这个Activity或Fragment被销毁的时候,图片加载也会停止。如果传入的是ApplicationContext,那么只有当应用程序被杀掉的时候,图片加载才会停止。
  2. load() : 这个方法用于指定待加载的图片资源。Glide支持加载各种各样的图片资源,包括网络图片、本地图片、应用资源、二进制流、Uri对象等等。因此load()方法也有很多个方法重载.
  3. into() : 这个方法就很简单了,我们希望让图片显示在哪个ImageView上,把这个ImageView的实例传进去就可以了。当然,into()方法不仅仅是只能接收ImageView类型的参数,还支持很多更丰富的用法,不过那个属于高级技巧

1. Glide如何绑定生命周期

Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程

with()方法就是用于绑定生命周期的,with()方法的重载种类非常多,既可以传入Activity,也可以传入Fragment或者是Context。调用RequestManagerRetriever的静态get()方法得到一个RequestManagerRetriever对象,这个静态get()方法就是一个单例实现,然后再调用RequestManagerRetriever的实例get()方法,去获取RequestManager对象。

RequestManagerRetriever的实例get()方法,传入什么Context参数,Activity参数,Fragment参数等等,实际上只有两种情况而已,即传入Application类型的参数,和传入非Application类型的参数。

  • Application参数:如果传入的是Application对象,那么这里就会调用带有Context参数的get()方法重载,调用getApplicationManager()方法来获取一个RequestManager对象。其实这是最简单的一种情况,因为Application对象的生命周期即应用程序的生命周期,因此Glide并不需要做什么特殊的处理,它自动就是和应用程序的生命周期是同步的,如果应用程序关闭的话,Glide的加载也会同时终止。
  • 非Application参数:不管传入的是Activity、FragmentActivity、v4包下的Fragment、还是app包下的Fragment,最终的流程都是一样的,那就是会向当前的Activity当中添加一个隐藏的Fragment。因为Glide需要知道加载的生命周期。很简单的一个道理,如果你在某个Activity上正在加载着一张图片,结果图片还没加载出来,Activity就被用户关掉了,那么图片还应该继续加载吗?当然不应该。可是Glide并没有办法知道Activity的生命周期,于是Glide就使用了添加隐藏Fragment的这种小技巧,因为Fragment的生命周期和Activity是同步的,如果Activity被销毁了,Fragment是可以监听到的,这样Glide就可以捕获这个事件并停止图片加载了。
  • 如果我们是在非主线程当中使用的Glide,那么不管你是传入的Activity还是Fragment,都会被强制当成Application来处理。

其实就是为了得到一个RequestManager对象而已,然后Glide会根据我们传入with()方法的参数来确定图片加载的生命周期


2. Glide如何实现图片缓存的

Android图片加载框架最全解析(三),深入探究Glide的缓存机制

Glide将缓存分为两个模块,一个是内存缓存,一个是硬盘缓存.

  • 内存缓存:是防止应用重复将图片读取到内存中
  • 硬盘缓存:防止应用重复从网络或其他地方重复下载和读取数据

缓存Key

决定缓存Key的条件非常多,即使你用override()方法改变了一下图片的width或者height,也会生成一个完全不同的缓存Key。

//这个字符串也就是我们要加载的图片的唯一标识,比如说如果是一张网络上的图片的话,那么这个id就是这张图片的url地址
final String id = fetcher.getId();
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
        loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
        transcoder, loadProvider.getSourceEncoder());

内存缓存

默认情况下,Glide自动就是开启内存缓存的。也就是说,当我们使用Glide加载了一张图片之后,这张图片就会被缓存到内存当中,只要在它还没从内存中被清除之前,下次使用Glide再加载这张图片都会直接从内存当中读取,而不用重新从网络或硬盘上读取了,这样无疑就可以大幅度提升图片的加载效率。比方说你在一个RecyclerView当中反复上下滑动,RecyclerView中只要是Glide加载过的图片都可以直接从内存当中迅速读取并展示出来,从而大大提升了用户体验。

//禁用内存缓存功能
Glide.with(this)
     .load(url)
     .skipMemoryCache(true)
     .into(imageView);

内存缓存的实现,可以分为两种:

  • LruCache算法(Least Recently Used),也叫近期最少使用算法。它的主要算法原理就是把最近使用的对象用强引用存储在LinkedHashMap中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。
  • 另一个使用的就是弱引用HashMap
  1. 首先判断是否缓存
  2. 接下来用key从LruCache算法中取值,LruCache算法中读取不到,从弱应用中读取,如果读取不到的话,才会开启线程执行后面的图片加载逻辑。
  3. 从LruCache算法中获取到缓存图片之后将它从缓存中移除,添加到弱引用中,使用弱引用来缓存正在使用中的图片,可以保护这些图片不会被LruCache算法回收掉。

在类中,用一个变量来记录图片被应用的次数,调用变量加1,当变量大于0的时候,说明图片正在使用中,就放到弱引用缓存中,如果变量等于0,说明已经不再被使用了,这里首先会将缓存图片从弱引用缓存中移除,然后再将它put到LruCache算法当中。这样也就实现了正在使用中的图片使用弱引用来进行缓存,不在使用中的图片使用LruCache来进行缓存的功能。

定义:弱引用,与强引用(我们常见的引用方式)相对;特点是:GC在回收时会忽略掉弱引用对象(忽略掉这种引用关系),即:就算弱引用指向了某个对象,但只要该对象没有被强引用指向,该对象也会被GC检查时回收掉。

硬盘缓存

Glide默认情况下在硬盘缓存的就是转换过后的图片,我们通过调用diskCacheStrategy()方法则可以改变这一默认行为。

//调用diskCacheStrategy()方法并传入DiskCacheStrategy.NONE,就可以禁用掉Glide的硬盘缓存功能了
Glide.with(this)
     .load(url)
     .diskCacheStrategy(DiskCacheStrategy.NONE)
     .into(imageView);

这个diskCacheStrategy()方法基本上就是Glide硬盘缓存功能的一切,它可以接收四种参数:

  • DiskCacheStrategy.NONE: 表示不缓存任何内容
  • DiskCacheStrategy.SOURCE: 表示只缓存原始图片
  • DiskCacheStrategy.RESULT: 表示只缓存转换过后的图片(默认选项)
  • DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片

首先,和内存缓存类似,硬盘缓存的实现也是使用的LruCache算法,而且Google还提供了一个现成的工具类DiskLruCache。Glide是使用的自己编写的DiskLruCache工具类,但是基本的实现原理都是差不多的。

默认情况下Glide会优先从硬盘缓存当中读取,只有硬盘缓存中不存在要读取的图片时,才会去读取原始图片。

获取RESULT图片和SOURCE的参数不同,因为如果我们是缓存的原始图片,其实并不需要这么多的参数,因为不用对图片做任何的变化。(基本上可以说就是由id(也就是图片url)来决定的缓存Key)


3. Glide如何实现图片压缩的

BitmapFactory.Options其实是一个辅助类,这个类提供了创建Bitmap类的接口,真正完成图像处理的类其实是Bitmap,但是由于这个类的构造函数是私有的,因此是无法在其他的类外进行实例化对象的,因此BitmapFactory.Options充当了这个辅助类,对外暴露接口,这样,我们就可以真正的调用Bitmap中的方法了.

参数配置

  1. inperferredConfig
  • ALPHA_8: 每个像素用占8位,存储的是图片的透明值,占1个字节
  • RGB_565:每个像素用占16位,分别为5-R,6-G,5-B通道,占2个字节
  • ARGB-4444:每个像素占16位,即每个通道用4位表示,占2个字节
  • ARGB_8888:每个像素占32位,每个通道用8位表示,占4个字节(默认)
  1. inJustDecodeBounds(boolean) inJustDecodeBounds=true的时候,那么代表对现在的这张图片进行非完全解码,其实说白了就是不给这个图片资源分配任何的内存,只是获取这个图片的基本信息(比如说:长度和宽度),不分配内存的原因想必大家都知道了,就是防止图片过大的问题,如果图片过大,那么我们获取到图片长度和宽度后,需要对图片进行一个压缩的操作
  2. inSampleSize(int)
    采样率,官方文档建议inSampleSize取值最好为2的指数。缩放比例就是1/(inSampleSize的2次方).
/**
 * 缩放资源id为resId的图片
 * @param res
 * @param resId 资源id
 * @param reqWidth 缩放后的宽度
 * @param reqHeight 缩放后的高度
 * @return
 */
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
    BitmapFactory.Options options = new BitmapFactory.Options();
    //只加载图片的宽高参数,并不会讲图片真正加载到内存中,节省内存
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId,options);

    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
    options.inJustDecodeBounds=false;
    options.inPreferredConfig = Bitmap.Config.RGB_565;    // 默认是Bitmap.Config.ARGB_8888
  
    return BitmapFactory.decodeResource(res, resId,options);
}

/**
 *计算inSampleSize
 * @param options
 * @param reqWidth 缩放后的宽度
 * @param reqHeight 缩放后的高度
 * @return
 */
private 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 halfHeight = height / 2;
        final int halfWidth = width / 2;
        //计算inSampleSize直到缩放后的宽高都小于指定的宽高
        while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }

    }

    System.out.println(inSampleSize);
    return inSampleSize;
}

使用如下:

Bitmap bitmap = decodeSampledBitmapFromResource(getResources(), R.mipmap.ic_launcher, 400, 400);
img.setImageBitmap(bitmap);

4. Glide与Picasso相比为什么选择使用Glide

深入对比Glide 和 Picasso

  1. 库的大小和方法的数量
    对比两个.jar 库的大小,Glide 要比 Picasso 大很多,基本上是 Picasso 的3.5倍: Picasso:121kb,Glide:440kb
    Picasso 的方法 总共有849个,而 Glide 的有2678个:

  2. 使用方式
    Glide 有 Activity 和 Fragment 的生命周期。Picasso只有Context

  3. 缓存大小
    Glide缓存的是ImageView的图片大小,下次直接加载,不需要改变尺寸,但是设定两个不一样大小的 ImageView, 那么Glide 实际上是会缓存两份。
    Picasso 是下载图片然后缓存完整的大小到本地,每次加载都要resize。

  4. 内存使用
    Glide 默认是用的 RGB_565 的设定,PIcasso 则是用的 ARGB _8888的设定。就算把Glide设置成ARGB _8888也会小,因为 Glide 是加载已经改变大小后的图片

  5. 加载图片的时间
    第一次,Picasso 会比 Glide 快一点。猜测可能的原因还是因为之前讲到的缓存机制导致,因为Picasso 是直接把图加载到内存中,而 Glide 则需要改变图片大小再加载到内存中去。这个应该是会耗费一定的时间。但是,当再次加载图片从内存中的时候,Glide 则比 Picasso 要快。其原理还是因为缓存机制的区别。因为Picasso 从缓存中拿到的图片,还要先去 resize 后,然后设定给 imageView,但是 Glide 则不需要这样。

  6. 其他功能的对比

  • GIF 支持:Glide 支持 GIF。
  • 灵活性:Glide 提供了非常多的配置,你可以非常灵活的根据你的需求来客制化,从而缩减 Glide 库的大小等。

5. 高清巨图的加载

Android 高清加载巨图方案


高级技巧

高级技巧

项目的图片资源都是存放在七牛云上面的,而七牛云为了对图片资源进行保护,会在图片url地址的基础之上再加上一个token参数。也就是说,一张图片的url地址可能会是如下格式:

http://url.com/image.jpg?token=d9caa6e02c990b0a

而使用Glide加载这张图片的话,也就会使用这个url地址来组成缓存Key。

但是接下来问题就来了,token作为一个验证身份的参数并不是一成不变的,很有可能时时刻刻都在变化。而如果token变了,那么图片的url也就跟着变了,图片url变了,缓存Key也就跟着变了。结果就造成了,明明是同一张图片,就因为token不断在改变,导致Glide的缓存功能完全失效了。


Glide的自定义模块功能

Android图片加载框架最全解析(六),探究Glide的自定义模块功能

自定义模块功能可以将更改Glide配置,替换Glide组件等操作独立出来,使得我们能轻松地对Glide的各种配置进行自定义,并且又和Glide的图片加载逻辑没有任何交集,这也是一种低耦合编程方式的体现。

public class MyGlideModule implements GlideModule {
    //更改Glide的默认配置
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
    
    }
    
    //替换Glide组件
    @Override
    public void registerComponents(Context context, Glide glide) {
    }
}

不过,目前Glide还无法识别我们自定义的MyGlideModule,如果想要让它生效,还得在AndroidManifest.xml文件当中加入如下配置才行:

<manifest>

    ...

    <application>

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

        ...

    </application>
</manifest>  

更改Glide配置

  • setMemoryCache() : 用于配置Glide的内存缓存策略,默认配置是LruResourceCache。
  • setBitmapPool() : 用于配置Glide的Bitmap缓存池,默认配置是LruBitmapPool。
  • setDiskCache() : 用于配置Glide的硬盘缓存策略,默认配置是InternalCacheDiskCacheFactory。
  • setDiskCacheService() : 用于配置Glide读取缓存中图片的异步执行器,默认配置是FifoPriorityThreadPoolExecutor,也就是先入先出原则。
  • setResizeService() : 用于配置Glide读取非缓存中图片的异步执行器,默认配置也是FifoPriorityThreadPoolExecutor。
  • setDecodeFormat() : 用于配置Glide加载图片的解码模式,默认配置是RGB_565。

替换Glide组件

换Glide组件功能需要在自定义模块的registerComponents()方法中加入具体的替换逻辑。相比于更改Glide配置,替换Glide组件这个功能的难度就明显大了不少。Glide中的组件非常繁多,也非常复杂,但其实大多数情况下并不需要我们去做什么替换。不过,有一个组件却有着比较大的替换需求,那就是Glide的HTTP通讯组件。

默认情况下,Glide使用的是基于原生HttpURLConnection进行订制的HTTP通讯组件,但是现在大多数的Android开发者都更喜欢使用OkHttp,因此将Glide中的HTTP通讯组件修改成OkHttp的这个需求比较常见

更简单的组件替换

Glide官方给我们提供了非常简便的HTTP组件替换方式。并且除了支持OkHttp3之外,还支持OkHttp2和Volley。

我们只需要在gradle当中添加几行库的配置就行了。比如使用OkHttp3来作为HTTP通讯组件的配置如下:

dependencies {
    compile 'com.squareup.okhttp3:okhttp:3.9.0'
    compile 'com.github.bumptech.glide:okhttp3-integration:1.5.0@aar'
}

使用OkHttp2来作为HTTP通讯组件的配置如下:

dependencies {
    compile 'com.github.bumptech.glide:okhttp-integration:1.5.0@aar'
    compile 'com.squareup.okhttp:okhttp:2.7.5'
}

使用Volley来作为HTTP通讯组件的配置如下:

dependencies {
    compile 'com.github.bumptech.glide:volley-integration:1.5.0@aar'  
    compile 'com.mcxiaoke.volley:library:1.0.19'  
}

Glide3 和 Glide4的区别

Android图片加载框架最全解析(八),带你全面了解Glide 4的用法

配置:

RequestOptions options = new RequestOptions()
        .placeholder(R.drawable.ic_launcher_background)
        .error(R.drawable.error)
        .override(200, 100)
        .diskCacheStrategy(DiskCacheStrategy.NONE);
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);

缓存:

Glide自动就是开启内存缓存的:

RequestOptions options = new RequestOptions()
        .skipMemoryCache(true);//表示禁用掉Glide的内存缓存功能
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);

硬盘缓存:

RequestOptions options = new RequestOptions()
        .diskCacheStrategy(DiskCacheStrategy.NONE);//禁止Glide对图片进行硬盘缓存
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);

diskCacheStrategy()的五种参数:

  • DiskCacheStrategy.NONE: 表示不缓存任何内容。
  • DiskCacheStrategy.DATA: 表示只缓存原始图片。
  • DiskCacheStrategy.RESOURCE: 表示只缓存转换过后的图片。
  • DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。
  • DiskCacheStrategy.AUTOMATIC: 表示让Glide根据图片资源智能地选择使用哪一种缓存策略(默认选项)。

其中,DiskCacheStrategy.DATA对应Glide 3中的DiskCacheStrategy.SOURCE,DiskCacheStrategy.RESOURCE对应Glide 3中的DiskCacheStrategy.RESULT。而DiskCacheStrategy.AUTOMATIC是Glide 4中新增的一种缓存策略,并且在不指定diskCacheStrategy的情况下默认使用就是的这种缓存策略。

指定加载格式:

Glide.with(this)
     .asBitmap()
     .load("http://guolin.tech/test.gif")
     .into(imageView);

在Glide 3中的语法是先load()再asBitmap()的,而在Glide 4中是先asBitmap()再load()的。

图片变换:

dependencies {
    implementation 'jp.wasabeef:glide-transformations:3.0.1'
}
String url = "http://guolin.tech/book.png";
RequestOptions options = new RequestOptions()
        .transforms(new BlurTransformation(), new GrayscaleTransformation());//同时对图片进行模糊化和黑白化处理
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);

自定义模块:

@GlideModule
public class MyAppGlideModule extends AppGlideModule {

    @Override
    public void applyOptions(Context context, GlideBuilder builder) {

    }

    @Override
    public void registerComponents(Context context, Glide glide, Registry registry) {

    }

}

注意在MyAppGlideModule类在上面,我们加入了一个@GlideModule的注解,这是Gilde 4和Glide 3最大的一个不同之处。在Glide 3中,我们定义了自定义模块之后,还必须在AndroidManifest.xml文件中去注册它才能生效,而在Glide 4中是不需要的,因为@GlideModule这个注解已经能够让Glide识别到这个自定义模块了。

使用Generated API:

Generated API是Glide 4中全新引入的一个功能,它的工作原理是使用注解处理器 (Annotation Processor) 来生成出一个API,在Application模块中可使用该流式API一次性调用到RequestBuilder,RequestOptions和集成库中所有的选项。

这么解释有点拗口,简单点说,就是Glide 4仍然给我们提供了一套和Glide 3一模一样的流式API接口。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值