Glide使用特点

来源:互联网 发布:单片机音乐程序 编辑:程序博客网 时间:2024/05/18 01:05

指定图片大小

实际上,使用Glide在绝大多数情况下我们都是不需要指定图片大小的。

我们需要先了解一个概念,就是我们平时在加载图片的时候很容易会造成内存浪费。什么叫内存浪费呢?比如说一张图片的尺寸是1000*1000像素,但是我们界面上的ImageView可能只有200*200像素,这个时候如果你不对图片进行任何压缩就直接读取到内存中,这就属于内存浪费了,因为程序中根本就用不到这么高像素的图片。

而使用Glide,我们就完全不用担心图片内存浪费,甚至是内存溢出的问题。因为Glide从来都不会直接将图片的完整尺寸全部加载到内存中,而是用多少加载多少。Glide会自动判断ImageView的大小,然后只将这么大的图片像素加载到内存当中,帮助我们节省内存开支。

也正是因为Glide是如此的智能,所以刚才在开始的时候我就说了,在绝大多数情况下我们都是不需要指定图片大小的,因为Glide会自动根据ImageView的大小来决定图片的大小

不过,如果你真的有这样的需求,必须给图片指定一个固定的大小,Glide仍然是支持这个功能的。修改Glide加载部分的代码,如下所示:

Glide.with(this)
.load(url)
.placeholder(R.drawable.loading)
.error(R.drawable.error)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.override(100, 100)
.into(imageView);

这里使用override()方法指定了一个图片的尺寸,也就是说,Glide现在只会将图片加载成100*100像素的尺寸,而不会管你的ImageView的大小是多少了。

Glide缓存简介

Glide的缓存设计可以说是非常先进的,考虑的场景也很周全。在缓存这一功能上,Glide又将它分成了两个模块,一个是内存缓存,一个是硬盘缓存。

这两个缓存模块的作用各不相同,内存缓存的主要作用是防止应用重复将图片数据读取到内存当中,而硬盘缓存的主要作用是防止应用重复从网络或其他地方重复下载和读取数据。

内存缓存和硬盘缓存的相互结合才构成了Glide极佳的图片缓存效果,那么接下来我们就分别来分析一下这两种缓存的使用方法以及它们的实现原理。

内存缓存
既然是缓存功能,就必然会有用于进行缓存的Key。那么Glide的缓存Key是怎么生成的呢?生成缓存Key的代码在Engine类的load()方法中
Glide主要使用EngineKey来代表了一个key,决定缓存Key的条件非常多,即使你用override()方法改变了一下图片的width或者height,也会生成一个完全不同的缓存Key

有了缓存Key,接下来就可以开始进行缓存了,那么我们先从内存缓存看起。

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

既然已经默认开启了这个功能,还有什么可讲的用法呢?只有一点,如果你有什么特殊的原因需要禁用内存缓存功能,Glide对此提供了接口:

Glide.with(this)
.load(url)
.skipMemoryCache(true)
.into(imageView);

接下来就让我们就通过阅读源码来分析一下Glide的内存缓存功能是如何实现的

其实说到内存缓存的实现,非常容易就让人想到LruCache算法(Least Recently Used),也叫近期最少使用算法。它的主要算法原理就是把最近使用的对象用强引用存储在LinkedHashMap中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除

这里不止使用LRU算法,而且还使用了弱引用缓存,来进行内存缓存,当两者都没有的时候就从磁盘或者网络中读取数据

总的来说,Glide内存缓存的实现原理是:正在使用中的图片使用弱引用来进行缓存,不在使用中的图片使用LruCache来进行缓存的功能。

硬盘缓存

首先硬盘缓存的意义是什么?
本地缓存中,即使手机在没有网络的情况下依然能够加载出以前浏览过的新闻。使用的缓存技术就是DiskLruCache了

接下来我们开始学习硬盘缓存方面的内容

调用diskCacheStrategy()方法并传入DiskCacheStrategy.NONE,就可以禁用掉Glide的硬盘缓存功能了。

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

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

但是有一个概念大家需要了解,就是当我们使用Glide去加载一张图片的时候,Glide默认并不会将原始图片展示出来,而是会对图片进行压缩和转换(我们会在后面学习这方面的内容)。总之就是经过种种一系列操作之后得到的图片,就叫转换过后的图片。而Glide默认情况下在硬盘缓存的就是转换过后的图片

接下来还是老套路,我们来分析一下,Glide的硬盘缓存功能是如何实现的

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

Glide的回调与监听

Glide的into方法可以传入的参数当然不仅仅只是ImageView,还可以传入Target继承体系的对象,最简单的使用方法如下:

SimpleTarget<Bitmap> simpleTarget = new SimpleTarget<Bitmap>() {    @Override    public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) {        imageView.setImageBitmap(resource);    }};public void loadImage(View view) {    String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";    Glide.with(this)         .load(url)         .asBitmap()         .into(simpleTarget);}

可以看到,这里我们将SimpleTarget的泛型指定成Bitmap,然后在加载图片的时候调用了asBitmap()方法强制指定这是一张静态图,这样就能在onResourceReady()方法中获取到这张图的Bitmap对象了

Target的设计是为了可以让图片不仅仅只是显示在ImageView上,而是可以设置在任意View上
这些虽说都是自定义Target最基本的用法,但掌握了这些用法之后,你就能应对各种各样复杂的逻辑了

preload()方法

Glide加载图片虽说非常智能,它会自动判断该图片是否已经有缓存了,如果有的话就直接从缓存中读取,没有的话再从网络去下载。但是如果我希望提前对图片进行一个预加载,等真正需要加载图片的时候就直接从缓存中读取,不想再等待慢长的网络加载时间了,这该怎么办呢?

需要注意的是,我们如果使用了preload()方法,最好要将diskCacheStrategy的缓存策略指定成DiskCacheStrategy.SOURCE。因为preload()方法默认是预加载的原始图片大小,而into()方法则默认会根据ImageView控件的大小来动态决定加载图片的大小。因此,如果不将diskCacheStrategy的缓存策略指定成DiskCacheStrategy.SOURCE的话,很容易会造成我们在预加载完成之后再使用into()方法加载图片,却仍然还是要从网络上去请求图片这种现象

downloadOnly()方法

一直以来,我们使用Glide都是为了将图片显示到界面上。虽然我们知道Glide会在图片的加载过程中对图片进行缓存,但如果我现在就是想要去访问图片的缓存文件该怎么办呢?这就需要用到downloadOnly()方法了。

和preload()方法类似,downloadOnly()方法也是可以替换into()方法的,不过downloadOnly()方法的用法明显要比preload()方法复杂不少。顾名思义,downloadOnly()方法表示只会下载图片,而不会对图片进行加载。当图片下载完成之后,我们可以得到图片的存储路径,以便后续进行操作。

那么首先我们还是先来看下基本用法。downloadOnly()方法是定义在DrawableTypeRequest类当中的,它有两个方法重载,一个接收图片的宽度和高度,另一个接收一个泛型对象,如下所示:

downloadOnly(int width, int height)
downloadOnly(Y target)
这两个方法各自有各自的应用场景,其中downloadOnly(int width, int height)是用于在子线程中下载图片的,而downloadOnly(Y target)是用于在主线程中下载图片的。

那么我们先来看downloadOnly(int width, int height)的用法。当调用了downloadOnly(int width, int height)方法后会立即返回一个FutureTarget对象,然后Glide会在后台开始下载图片文件。接下来我们调用FutureTarget的get()方法就可以去获取下载好的图片文件了,如果此时图片还没有下载完,那么get()方法就会阻塞住,一直等到图片下载完成才会有值返回。

public void downloadImage(View view) {    new Thread(new Runnable() {        @Override        public void run() {            try {                String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";                final Context context = getApplicationContext();                FutureTarget<File> target = Glide.with(context)                                                 .load(url)                                                 .downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);                final File imageFile = target.get();                runOnUiThread(new Runnable() {                    @Override                    public void run() {                        Toast.makeText(context, imageFile.getPath(), Toast.LENGTH_LONG).show();                    }                });            } catch (Exception e) {                e.printStackTrace();            }        }    }).start();}

之后我们可以使用如下代码去加载这张图片,图片就会立即显示出来,而不用再去网络上请求了

public void loadImage(View view) {    String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";    Glide.with(this)            .load(url)            .diskCacheStrategy(DiskCacheStrategy.SOURCE)            .into(imageView);}

listener()方法

其实listener()方法的作用非常普遍,它可以用来监听Glide加载图片的状态。举个例子,比如说我们刚才使用了preload()方法来对图片进行预加载,但是我怎样确定预加载有没有完成呢?还有如果Glide加载图片失败了,我该怎样调试错误的原因呢?答案都在listener()方法当中。

首先来看下listener()方法的基本用法吧,不同于刚才几个方法都是要替换into()方法的,listener()是结合into()方法一起使用的,当然也可以结合preload()方法一起使用。最基本的用法如下所示

public void loadImage(View view) {    String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";    Glide.with(this)            .load(url)            .listener(new RequestListener<String, GlideDrawable>() {                @Override                public boolean onException(Exception e, String model, Target<GlideDrawable> target,                    boolean isFirstResource) {                    return false;                }                @Override                public boolean onResourceReady(GlideDrawable resource, String model,                    Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {                    return false;                }            })            .into(imageView);}

没错,listener()方法就是这么简单。不过还有一点需要处理,onResourceReady()方法和onException()方法都有一个布尔值的返回值,返回false就表示这个事件没有被处理,还会继续向下传递,返回true就表示这个事件已经被处理掉了,从而不会再继续向下传递。举个简单点的例子,如果我们在RequestListener的onResourceReady()方法中返回了true,那么就不会再回调Target的onResourceReady()方法了

Glide强大的图片变换功能

稍微对Android有点了解的人应该都知道ImageView有scaleType这个属性,但是可能大多数人却不知道,如果在没有指定scaleType属性的情况下,ImageView默认的scaleType是什么?
动手才是检验真理的唯一标准,想知道答案,自己动手试一下就知道了。

imageView = (ImageView) findViewById(R.id.image_view);        Log.d(TAG, "imageView scaleType is " + imageView.getScaleType());

我们可以得知,在没有明确指定的情况下,ImageView默认的scaleType是FIT_CENTER。
我们可以使用基于Glide的图片变换框架来实现各种效果,例如模糊,黑白等

   //glide图片变换框架    compile 'jp.wasabeef:glide-transformations:2.0.2'

自定义模块的基本用法

Glide的用法是非常非常简单的,大多数情况下,我们想要实现的图片加载效果只需要一行代码就能解决了。但是Glide过于简洁的API也造成了一个问题,就是如果我们想要更改Glide的某些默认配置项应该怎么操作呢?很难想象如何将更改Glide配置项的操作串联到一行经典的Glide图片加载语句中当中吧?没错,这个时候就需要用到自定义模块功能了。

自定义模块功能可以将更改Glide配置,替换Glide组件等操作独立出来,使得我们能轻松地对Glide的各种配置进行自定义,并且又和Glide的图片加载逻辑没有任何交集,这也是一种低耦合编程方式的体现。那么接下来我们就学习一下自定义模块的基本用法
具体使用如下:
http://blog.csdn.net/guolin_blog/article/details/78179422

更简单的组件替换

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'  }

当然了,这些库背后的工作原理和我们刚才自己手动实现替换HTTP组件的原理是一模一样的。而学会了手动替换组件的原理我们就能更加轻松地扩展更多丰富的功能,因此掌握这一技能还是非常重要的

扩展目标:实现带进度的Glide图片加载功能

首先来确立一下功能扩展的目标。虽说Glide本身就已经十分强大了,但是有一个功能却长期以来都不支持,那就是监听下载进度功能。

我们都知道,使用Glide来加载一张网络上的图片是非常简单的,但是让人头疼的是,我们却无从得知当前图片的下载进度。如果这张图片很小的话,那么问题也不大,反正很快就会被加载出来。但如果这是一张比较大的GIF图,用户耐心等了很久结果图片还没显示出来,这个时候你就会觉得下载进度功能是十分有必要的了。

好的,那么我们今天的目标就是对Glide进行功能扩展,使其支持监听图片下载进度的功能。

详见郭神讲解:
http://blog.csdn.net/guolin_blog/article/details/78357251