Glide图片库使用

1.Glide是什么?

Glide是Google在2014的IO大会发布一款图片处理框架,是目前android领域比较成熟的一款,也是Google官方推荐的图片处理框架,主要支持网络图片、二进制流、drawable资源、本地图片显示,还支持本地视频显示。

2.Glide基本功能使用

  1. 在app级别下面配置gradle
1
2
3
dependencies {
compile 'com.github.bumptech.glide:glide:3.7.0'
}
  1. 使用起来
    通过Glide类进行一个链式调用,下面代码显示了一张网络图片。
  • with支持传入一下对象,我们在使用过程中尽量要传入activity、fragment因为glide会依赖它们的生命周期,如果onPaush时候,Glide就会暂停加载,重新onResume之后,又会继续加载。
    image

  • load() 支持网络图片、二进制流、drawable资源、本地图片的传入。

  • crossFade 这是开启显示淡入淡出的动画

  • override 如果获取的网络图片过大,我们通过它进行一个大小的裁剪,传入width和height参数进行宽高裁剪。

  • diskCacheStrategy 磁盘缓存的设置,默认Glide会开启的。

DiskCacheStrategy.NONE 什么都不缓存
DiskCacheStrategy.SOURCE 只缓存全尺寸图
DiskCacheStrategy.RESULT 只缓存最终的加载图
DiskCacheStrategy.ALL 缓存所有版本图(默认行为)

Glide 不仅缓存了全尺寸的图,还会根据 ImageView 大小所生成的图也会缓存起来。比如,请求一个 800x600 的图加载到一个 400x300 的 ImageView 中,Glide默认会将这原图还有加载到 ImageView 中的 400x300 的图也会缓存起来。

  • error 这里的设置是当加载图片出现错误时,显示的图片。
  • placeholder 图片加载完成之前显示的占位图。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12


    String url = "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2912495429,3557331556&fm=27&gp=0.jpg";
    Glide
    .with(this)
    .load(url)
    .crossFade()
    .override(200, 200)
    .diskCacheStrategy(DiskCacheStrategy.ALL)
    .error(R.mipmap.active_user_default_icon)
    .placeholder(R.mipmap.ic_album_image_source_pick)
    .into(img);

获取Glide请求完成的Bitmap
通过前面这种方式我们能很轻松的显示一张,图片到ImgeView上。但是我们有时候想获取加载完成后的Bitmap怎么办呢,可以通过SimpleTarget来获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
//SimpleTarget 获取bitmap,定义siez
SimpleTarget<Bitmap> mSimpleTarget = new SimpleTarget<Bitmap>(200, 200) {
@Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
img.setImageBitmap(resource);
}
};

//获取Bitmap的加载方法。
Glide.with(getApplicationContext())
.load(url)
.asBitmap()
.into(mSimpleTarget);

我们定义了一个SimpleTarget类,里面有一个onResourceReady方法的回调,会返回加载完成的Bitmap对象,into传入这个SimpleTarget对象。

自定义控件使用Glide显示Image
有时候我们自定义的控件也需要显示Imaeg,但是这个控件不是Image类或者子类,那么前面这种方法就会不使用了,我们可以通过ViewTarget来实现这个逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class MyImageView extends FrameLayout {
private ImageView mImageView;

public MyImageView(@NonNull Context context) {
super(context);
}

public MyImageView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}

public MyImageView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
protected void onFinishInflate() {
super.onFinishInflate();
mImageView = new ImageView(getContext());
addView(mImageView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}

public void setImage(Drawable drawable) {
mImageView.setImageDrawable(drawable);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
MyImageView img3;
//ViewTarget自定义View也支持获取Bitmap
ViewTarget viewTarget = new ViewTarget<MyImageView, GlideDrawable>(img3) {
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
img3.setImage(resource);
}
};

Glide.with(getApplicationContext())
.load(url)
.into(viewTarget);

通过定义一个ViewTarget传入了二个泛型类,一个是MyImageView,一个是GlideDrawable,传入了img3。通过onResourceReady的回调我们可以对自定义控件进行图片显示了。

3.Glide原理了解?

Glide的with、load、into虽然只有三个方法调用但是内部的逻辑是很复杂的,成吨的代码这里我只做一个大概了解。首先with方法会构造一个单例的RequestManager对象。

with方法

1
2
3
4
public static RequestManager with(FragmentActivity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}

1
2
3
public static RequestManagerRetriever get() {
return INSTANCE;
}

可以看出来我们一个程序生命周期内只有一个RequestManager对象,工程师在设计的时候很考虑到性能的问题。

load方法

1
2
3
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}

接着通过RequestManage对象的load方法返回一个DrawableTypeRequest类,它是一个DrawableRequestBuilder的一个子类,通过类名可以看出来主要作用是主要是构建请求中的一些参数用的,比如我们之前写的 override、error、placeholder、diskCacheStrategy这些辅助方法的设置。

into方法
最后的一个方法也是最复杂的一个,是真正请求网络请求的地方。

1
2
3
public Target<GlideDrawable> into(ImageView view) {
return super.into(view);
}
1
2
3
4
5
public Target<TranscodeType> into(ImageView view) {
Util.assertMainThread();
...
return into(glide.buildImageViewTarget(view, transcodeClass));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
@SuppressWarnings("unchecked")
public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
if (GlideDrawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new GlideDrawableImageViewTarget(view);
} else if (Bitmap.class.equals(clazz)) {
return (Target<Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException("Unhandled class: " + clazz
+ ", try .as*(Class).transcode(ResourceTranscoder)");
}
}

into接收一个ViewTeger,

into的调用

1
2
3
4
5
6
7
8
9
10

public <Y extends Target<TranscodeType>> Y into(Y target) {
...
Request request = buildRequest(target);
target.setRequest(request);
lifecycle.addListener(target);
requestTracker.runRequest(request);

return target;
}
1
2
3
4
5
6
7
8
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}

runRequest方法来控制请求的队列,如果当前视图状态是Paused状态就会把request添加到等待请求的队列,如果不是则直接执行。里面会有一个HttpUrlFetcher来执行前面封装好的网络请求

4.Glide是如何缓存的?

glide缓存主要分为

  1. 内存缓存

    内存缓存主要是防止同样的图片重复读取到内存中来,解约JVM的内存空间

glide的内存缓存机制是使用LruCache算法实现,首先需要会需要一个key,这个key是通过url+构建请求时候的参数决定的。keyFactory.buildKey的代码就是生成Key的逻辑。glide内存缓存会调用二个方法来获取缓存。

  • loadFromCache
    使用LruCache

  • loadFromActiveResources
    使用弱引用

内存缓存的逻辑就是,先会在loadFromCache中去取缓存,这个是一个LruCaChe算啊实现的,如果取到了值就会把它存入一个弱引用中。这样的防止被LruCache算法回收掉,如果LruCache没有取到就会去loadFromActiveResources方法里面取。

如果都没有那就会开启子线程去网络请求图片。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();

final String id = fetcher.getId();
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());

EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}

EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}

EngineJob current = jobs.get(key);
if (current != null) {
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}

EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(runnable);

if (Log.isLoggable(TG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
  1. 磁盘缓存

磁盘缓存主要是防止同一张网络图片,重复从网络中读取和下载,磁盘缓存也是使用LruCache算法实现的。

磁盘缓存有一个逻辑,当需要去加载一张图片的时候,Glide默认不会显示原始图片,而是会对图片进行压缩转换。经过这些转换操作之后才会把,图片显示出来,磁盘缓存默认就是缓存转换后的图片。