0%

Glide源码解析

概述

Glide是一个快速高效的Android图片加载库,提供了易用的API,高性能、可扩展的图片解码管道(decode pipeline),以及自动的资源池技术。

Glide 支持拉取,解码和展示视频快照,图片,和GIF动画,开发者可以将 Glide 网络请求从 HttpUrlConnection 替换成如 Google Volley 和 Square OkHttp 这样的工具库。Glide 提供了多种图片格式的缓存,可以感知Activity/Fragment的生命周期。能够复用和主动回收Bitmap,以及带有高效的缓存策略,减小了内存的开销。

本文基于 Glide 4.11.0 版本,参考网上一些解析,结合源码对 Glide 的机制做一个分析。

使用

具体用法见Glide Github

基本用法

使用Glide加载图片非常简单:

1
Glide.with(fragment).load(myUrl).into(imageView);

取消加载同样很简单:

1
Glide.with(fragment).clear(imageView);

及时取消不必要的加载不是必须的操作,因为当 Glide.with() 中传入的 Activity 或 Fragment 实例销毁时,Glide 会自动取消加载并回收资源。

定制请求

Glide 提供了许多可应用于单一请求的选项,包括变换、过渡、缓存选项等。

1
2
3
4
RequestOptions sharedOptions = new RequestOptions().placeholder(placeholder).fitCenter();

Glide.with(fragment).load(myUrl).apply(sharedOptions).into(imageView1);
Glide.with(fragment).load(myUrl).apply(sharedOptions).into(imageView2);

ListView/RecyclerView

在 ListView 或 RecyclerView 中加载图片的代码和在单独的 View 中加载完全一样。Glide 已经自动处理了 View 的复用和请求的取消:

1
2
3
4
5
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
String url = urls.get(position);
Glide.with(fragment).load(url).into(holder.imageView);
}

对 View 调用 clear() 或 into(view) 后在此之前的加载操作会被取消,并且在方法调用完成后,Glide 不会改变 view 的内容。如果忘记调用 clear(),而又没有开启新的加载操作,那么就会出现这种情况:已经为一个 view 设置好了一个 Drawable,但该 view 在之前的位置上使用 Glide 进行过加载图片的操作,Glide 加载完毕后可能会将这个 view 改回成原来的内容。

Target

可以将图片异步加载到自定义 Target 中:

1
2
3
4
5
6
7
8
9
Glide.with(context).load(url).into(new CustomTarget<Drawable>() {
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
}

@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
}
});

调用逻辑解析

先看两个接口:

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 interface Target<R> extends LifecycleListener {
void onLoadStarted(@Nullable Drawable placeholder);
void onLoadFailed(@Nullable Drawable errorDrawable);
void onResourceReady(@NonNull R resource, @Nullable Transition<? super R> transition);
void onLoadCleared(@Nullable Drawable placeholder);

void getSize(@NonNull SizeReadyCallback cb);

void removeCallback(@NonNull SizeReadyCallback cb);

void setRequest(@Nullable Request request);
Request getRequest();
}

public interface Request {
void begin();
void clear();
void pause();

boolean isRunning();
boolean isComplete();
boolean isCleared();

boolean isAnyResourceSet();
boolean isEquivalentTo(Request other);
}

with(context)

返回一个RequestManager对象,RequestManager是一个用于管理和启动对Glide的请求的类。可以感知activity, fragment等生命周期事件来自动停止,启动和重新启动请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Glide.java
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}

// 一系列重载方法

private static RequestManagerRetriever getRetriever(@Nullable Context context) {
Preconditions.checkNotNull(context, "...");
// Glide单例
return Glide.get(context).getRequestManagerRetriever();
}

public static Glide get(@NonNull Context context) {
if (glide == null) {
GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}

with方法的内容都是获取一个 RequestManager 对象并返回。这些with方法关键的不同在于传入的参数不一致,可以是Context、Activity、Fragment等等。Glide在加载图片的时候会绑定 with(context) 方法中传入的 context 的生命周期,这样避免消耗多余的资源,也避免了在Activity销毁之后加载图片从而导致的空指针问题。

RequestManagerRetriever.get 方法在后面生命周期解析的时候再看。

注意上面的 getAnnotationGeneratedGlideModules 方法,它用来处理开发者通过 @GlideModule 注解自定义的 AppGlideModule 类。在编译期间如果存在通过 @GlideModule 注解修饰的类,那么 Glide 会通过注解处理器生成一个继承自 GeneratedAppGlideModule 的类,里面有自定义的一些配置或者 ModelLoader 等,这样通过 Glide.get() 获取到的 Glide 实例便会自动应用这些配置。

load(url)

返回一个RequestBuilder对象,RequestBuilder类是一个处理通用资源类型的设置选项和启动负载的通用类。

1
2
3
4
5
6
7
8
9
10
11
12
// RequestManager.java
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}

public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}

public <ResourceType> RequestBuilder<ResourceType> as(@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}

asDrawable() 调用返回了一个 RequestBuilder<Drawable> 对象,接下来看看 RequestBuilder.load 方法:

1
2
3
4
5
6
7
8
9
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}

private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}

在调用load()后可以进行一些设置,调用的是 RequestBuilder 父类 BaseRequestOptions 中的方法。如下所示:

1
Glide.with(context).load(url).placeholder(R.drawable.place_image).error(R.drawable.error_image).into(imageView);

into(imageView)

RequestBuilder.into

Glide中的前两步是创建了一个 RequestBuilder, 这个Request可以理解为对图片加载的配置请求,而并没有去执行。在Glide的最后一步into方法中,这个请求才会真正执行。

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
// RequestBuilder.java
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);

BaseRequestOptions<?> requestOptions = this;
// 根据 View 的 scaleType 创建新的 requestOptions
if (!requestOptions.isTransformationSet() && requestOptions.isTransformationAllowed() && view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default: // Do nothing.
}
}
return into(glideContext.buildImageViewTarget(view, transcodeClass), null, requestOptions, Executors.mainThreadExecutor());
}

在处理了 scale type 后,该方法又调用了glideContext.buildImageViewTarget(view, transcodeClass)方法创建了一个 Target 对象,然后传递给了into方法。接下来看一看这个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// GlideContext.java
// transcodeClass参数在上述解析中,可以看到传入的是Drawable.class
public <X> ViewTarget<ImageView, X> buildImageViewTarget(@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}

public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view, @NonNull Class<Z> clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException("Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}

这个方法的的本质是:通过对图片来源类型的判断,创建并返回与图片来源对应的ImageViewTarget。接下来继续看into方法:

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
private <Y extends Target<TranscodeType>> Y into(@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener, BaseRequestOptions<?> options, Executor callbackExecutor) {
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
// 创建 Request
Request request = buildRequest(target, targetListener, options, callbackExecutor);

Request previous = target.getRequest();
if (request.isEquivalentTo(previous) && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
if (!Preconditions.checkNotNull(previous).isRunning()) {
// 使用上一个请求而不是新请求来进行优化
previous.begin();
}
return target;
}

// 清空并终止target的request的执行
requestManager.clear(target);
// 设置新的request
target.setRequest(request);
requestManager.track(target, request);

return target;
}

Glide处理 ListView 或者 Recyclerview 中的直接正常使用加载图片即可,因为 Glide 处理了复用,看一下Glide内部是如何处理的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public Request getRequest() {
Object tag = getTag();
Request request = null;
if (tag != null) {
if (tag instanceof Request) {
request = (Request) tag;
} else {
throw new IllegalArgumentException("You must not call setTag() on a view Glide is targeting");
}
}
return request;
}

public void setRequest(@Nullable Request request) {
setTag(request);
}

可以看到,target.getRequest() 和 target.setRequest(Request request) 本质上还是通过 setTag 和 getTag 来做的处理。

RequestManager.track

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
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}

public final class TargetTracker implements LifecycleListener {
private final Set<Target<?>> targets = Collections.newSetFromMap(new WeakHashMap<Target<?>, Boolean>());

public void track(@NonNull Target<?> target) {
targets.add(target);
}
}

public class RequestTracker {
private final Set<Request> requests = Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
// 防止Activity/Fragment没有启动时,clear的request被回收,见 https://github.com/bumptech/glide/issues/346
private final List<Request> pendingRequests = new ArrayList<>();

public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
request.clear();
pendingRequests.add(request);
}
}
}

上面 Request 是 SingleRequest 类型:

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
// SingleRequest.java
public void begin() {
synchronized (requestLock) {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
if (model == null) {
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
// 通常是通过诸如notifyDataSetChanged之类的操作,它会向相同的Target或View启动相同的请求,
// 简单地使用上次获取的资源和大小,这意味着要重新加载的用户需要在开始新的加载之前显式清除View或Target
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}

status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}

if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE) && canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
}
}

方法的逻辑大致如下:

  • 获取图片尺寸,如果长宽已经确定,则 onSizeReady(overrideWidth, overrideHeight);如果未确定,先获取长宽,再 onSizeReady(overrideWidth, overrideHeight)
  • 图片开始加载,首先提供占位图

主要的逻辑在 onSizeReady() 方法中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// SingleRequest.java
public void onSizeReady(int width, int height) {
synchronized (requestLock) {
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
// 真正执行加载工作的代码
loadStatus = engine.load(glideContext, model, requestOptions.getSignature(),
this.width, this.height, requestOptions.getResourceClass(), transcodeClass,
priority, requestOptions.getDiskCacheStrategy(), requestOptions.getTransformations(),
requestOptions.isTransformationRequired(), requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(), requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(), requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(), this, callbackExecutor);
}
}

Engine.load

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Engine.java
public <R> LoadStatus load(GlideContext glideContext, Object model, Key signature,
int width, int height, Class<?> resourceClass, Class<R> transcodeClass, Priority priority,
DiskCacheStrategy diskCacheStrategy, Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired, boolean isScaleOnlyOrNoTransform, Options options,
boolean isMemoryCacheable, boolean useUnlimitedSourceExecutorPool, boolean useAnimationPool,
boolean onlyRetrieveFromCache, ResourceCallback cb, Executor callbackExecutor) {
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options);

EngineResource<?> memoryResource;
synchronized (this) {
// 从内存缓存中获取
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
if (memoryResource == null) {
return waitForExistingOrStartNewJob(glideContext, model, signature, width, height, resourceClass,
transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired,
isScaleOnlyOrNoTransform, options, isMemoryCacheable, useUnlimitedSourceExecutorPool,
useAnimationPool, onlyRetrieveFromCache, cb, callbackExecutor, key, startTime);
}
}
// 避免在持有引擎锁的同时回调,这样做更容易死锁。
cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
return null;
}

首先尝试从内存中获取缓存,获取到了则直接返回使用,否则调用 waitForExistingOrStartNewJob 方法获取磁盘缓存或从数据源获取资源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private <R> LoadStatus waitForExistingOrStartNewJob(/*...*/) {
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb, callbackExecutor);
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob = engineJobFactory.build(key, isMemoryCacheable, useUnlimitedSourceExecutorPool,
useAnimationPool, onlyRetrieveFromCache);
DecodeJob<R> decodeJob = decodeJobFactory.build(glideContext, model, key, signature, width, height,
resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired,
isScaleOnlyOrNoTransform, onlyRetrieveFromCache, options, engineJob);

jobs.put(key, engineJob);
engineJob.addCallback(cb, callbackExecutor);
engineJob.start(decodeJob);
return new LoadStatus(cb, engineJob);
}

生命周期控制

Glide加载图片时,通过在所在的 Activity/Fragment 中添加一个空的 Fragment:RequestManagerFragment/SupportRequestManagerFragment,然后通过其生命周期的回调去控制Glide图片的加载动作。其代码如下:

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
public class RequestManagerFragment extends Fragment {
private final ActivityFragmentLifecycle lifecycle;
@Nullable private RequestManager requestManager;

public RequestManagerFragment() {
this(new ActivityFragmentLifecycle());
}

RequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {
this.lifecycle = lifecycle;
}

// ...

ActivityFragmentLifecycle getGlideLifecycle() {
return lifecycle;
}

@Override
public void onStart() {
super.onStart();
lifecycle.onStart();
}

@Override
public void onStop() {
super.onStop();
lifecycle.onStop();
}

@Override
public void onDestroy() {
super.onDestroy();
lifecycle.onDestroy();
}
}

可以看到,当 Fragment 回调生命周期时,会调用 ActivityFragmentLifecycle 中对应的方法,因此我们看看 ActivityFragmentLifecycle 中的逻辑:

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
class ActivityFragmentLifecycle implements Lifecycle {
private final Set<LifecycleListener> lifecycleListeners = Collections.newSetFromMap(new WeakHashMap<LifecycleListener, Boolean>());
private boolean isStarted;
private boolean isDestroyed;

@Override
public void addListener(@NonNull LifecycleListener listener) {
lifecycleListeners.add(listener);
// 当LifecycleListener一注册则先设置其当前状态
if (isDestroyed) {
listener.onDestroy();
} else if (isStarted) {
listener.onStart();
} else {
listener.onStop();
}
}

@Override
public void removeListener(@NonNull LifecycleListener listener) {
lifecycleListeners.remove(listener);
}

void onStart() {
isStarted = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStart();
}
}

void onStop() {
isStarted = false;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStop();
}
}

void onDestroy() {
isDestroyed = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onDestroy();
}
}
}

再看看 LifecycleListener 监听是何时添加的:

1
2
3
4
5
6
7
8
9
// RequestManager
RequestManager(/*...*/) {
if (Util.isOnBackgroundThread()) {
// 在主线程调用lifecycle.addListener(this)
mainHandler.post(addSelfToLifecycle);
} else {
lifecycle.addListener(this);
}
}

可知在 RequestManager 构造方法中,会将自己注册到 RequestManagerFragment 的生命周期监听中。而 RequestManager 实例的创建,从 Glide.with 方法中可知,会调用 RequestManagerRetriever.get 方法来获取一个 RequestManager 实例:

1
2
3
4
5
6
7
8
9
10
11
12
private final RequestManagerFactory factory;

public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
// 非主线程,传入的Context是Application实例
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}

当 context 是 Application 时,创建的 RequestManager 如下:

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
public RequestManager get(@NonNull Context context) {
// ...
return getApplicationManager(context);
}

private RequestManager getApplicationManager(@NonNull Context context) {
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
Glide glide = Glide.get(context.getApplicationContext());
applicationManager = factory.build(glide, new ApplicationLifecycle(),
new EmptyRequestManagerTreeNode(), context.getApplicationContext());
}
}
}
return applicationManager;
}

class ApplicationLifecycle implements Lifecycle {
@Override
public void addListener(@NonNull LifecycleListener listener) {
listener.onStart();
}

@Override
public void removeListener(@NonNull LifecycleListener listener) {
// Do nothing.
}
}

由上可知,当 Glide 传入的 Context 是 Application 时,RequestManager 中的 lifecycle 是 ApplicationLifecycle 实例,没有生命周期的感知功能。当 Context 是 activity 实例时,则会通过 fragmentGet 方法创建一个用来感知生命周期变化的 Fragment:

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
private RequestManager fragmentGet(@NonNull Context context, @NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint, boolean isParentVisible) {
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
Glide glide = Glide.get(context);
requestManager = factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}

private RequestManagerFragment getRequestManagerFragment(@NonNull final android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint, boolean isParentVisible) {
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = new RequestManagerFragment();
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
// 如果可见,则直接回调onStart
current.getGlideLifecycle().onStart();
}
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}

至此,完成了Glide与Fragment生命周期的绑定。当 Fragment 中生命周期发生变化时,则会回调 RequestManager 中的方法:

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
// RequestManager
@Override
public synchronized void onStart() {
resumeRequests();
targetTracker.onStart();
}

@Override
public synchronized void onStop() {
pauseRequests();
targetTracker.onStop();
}

@Override
public synchronized void onDestroy() {
targetTracker.onDestroy();
for (Target<?> target : targetTracker.getAll()) {
clear(target);
}
targetTracker.clear();
requestTracker.clearRequests();
lifecycle.removeListener(this);
lifecycle.removeListener(connectivityMonitor);
mainHandler.removeCallbacks(addSelfToLifecycle);
glide.unregisterRequestManager(this);
}

缓存

一般来说,Glide 存在着多级缓存:Active Resources(另一个 View 正在展示这张图片) -> Memory cache(LruCache) -> Disk cache(DiskLruCache) -> Network

cache_process

自定义加载

Glide 自定义加载流程如下:

  1. 定义一个 Model 类用于包装需要加载的数据;
  2. 定义一个 Key 实现类,用于实现 Model 中的数据的签名用于区分缓存,可以直接让 Model 来实现 Key 中的接口;
  3. 定义一个 DataFetcher 的实现类,用于告诉 Glide 资源该如何加载,并把加载结果回调出去;
  4. 定义一个 ModelLoader 的实现类用于包装 DataFetcher;
  5. 定义一个 ModelLoaderFactory 的实现类用于生成 ModelLoader 实例;
  6. 将自定义加载配置到 AppGlideModule 中。

代码实例:

定义Model并实现Key接口

1
2
3
4
5
class MyModel(val path: String) : Key {
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
messageDigest.update(path.toByteArray())
}
}

实现DataFetcher用来加载数据

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
class MyModelFetcher(private val model: MyModel) : DataFetcher<InputStream> {
private var inputStream: InputStream? = null
private var isCancelled = false

override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in InputStream>) {
try {
if (!isCancelled) {
inputStream = FileInputStream(model.path)
}
callback.onDataReady(inputStream)
} catch (e: Exception) {
callback.onLoadFailed(e)
}
}

override fun cleanup() {
try {
inputStream?.close()
} catch (e: IOException) {
e.printStackTrace()
}
}

override fun cancel() {
isCancelled = true
}

override fun getDataClass(): Class<InputStream> {
return InputStream::class.java
}

override fun getDataSource(): DataSource {
return DataSource.LOCAL
}
}

定义ModelLoader和ModelLoaderFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MyModelLoader : ModelLoader<MyModel, InputStream> {
override fun buildLoadData(model: MyModel, width: Int, height: Int, options: Options): ModelLoader.LoadData<InputStream> {
return ModelLoader.LoadData(model, MyModelFetcher(model))
}

override fun handles(model: MyModel): Boolean = true

class MyLoaderFactory: ModelLoaderFactory<MyModel, InputStream> {
override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<MyModel, InputStream> {
return MyModelLoader()
}

override fun teardown() {
}
}
}

定义AppGlideModule

1
2
3
4
5
6
7
@GlideModule
class MyGlideModule : AppGlideModule() {
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
super.registerComponents(context, glide, registry)
registry.append(MyModel::class.java, InputStream::class.java, MyModelLoader.MyLoaderFactory())
}
}

总结

Glide加载图片的流程:

加载流程

开发一个图片加载框架必要的需求:

  • 异步加载:最少两个线程池(读磁盘/网络请求)
  • 切换到主线程:Handler
  • 缓存:LruCache、DiskLruCache
  • 防止OOM:软引用,弱引用,LruCache,Bitmap像素存储位置等
  • 内存泄露:生命周期管理
  • 列表滑动加载的问题:RecycleView加载图片错乱可以设置Tag(url)

参考: https://juejin.im/post/6844903986412126216