本文基于glide4.10.0分析,首先根据简单的调用梳理流程,主要以流程图的形式展示,然后基于流程图,着重分析glide的内存管理.这样方便在后期遇到问题能及时找到地方和原因.
首先是流程图:
内存分析
内存类的创建
- 当我们调用
Glide.with(context)时,其内部通过单例的方式返回Glide实例
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;
}
2,然后通过逐层调用,最后通过GlideBuilder.build方法创建Glide实例对象
initializeGlide(..){
...
Glide glide = builder.build(applicationContext);
...
}
3,进入Build方法,查看具体内存管理的实现类.
- 我们都知道是基于三级内存管理策略,这里主要看实现逻辑
- 内存缓存类
LruResourceCache,具体给类可阅读网上的文章
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
- 磁盘缓存处理类
InternalCacheDiskCacheFactory,其继承DiskLruCacheFactory
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
public final class InternalCacheDiskCacheFactory extends DiskLruCacheFactory
这里我们找到了,在Glide中内存管理中,主要负责缓存管理的两个类.在流程分析中最终数据的加载都是通过LoadData和相应的Fetcher实现,而其是在构造方法添加的,这里重点了解其都由那些类,这样后面分析加载过程直接查看具体的类即可.
4,Loader的添加
register.
...
.append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
.append(File.class, InputStream.class, new FileLoader.StreamFactory())
.append(File.class, File.class, new FileDecoder()).
...
内存缓存的读取和写入
读取
1,Glide.into(imageView)根据流程会调用Engine.load方法,这里我们查看该方法
- 首先会生成资源key,发现其根据我们
load传入的参数(model),以及图标的高度,类等生成key - 缓存的调用通过方法
loadFromMemory方法获取
public <R> LoadStatus load(...){
EngineKey key = keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
EngineResource<?> memoryResource;
synchronized (this) {
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
}
}
2,loadFromMemory负责从内存缓存中读取数据,其内部有两级缓存,弱引用缓存和LruResourceCache缓存(其变量名为memory)
- loadFromActiveResources获取弱引用缓存,弱引用缓存通过HashMap实现
private EngineResource<?> loadFromActiveResources(Key key) {
EngineResource<?> active = activeResources.get(key);
if (active != null) {
active.acquire();
}
return active;
}
//ActiveResource
final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
synchronized EngineResource<?> get(Key key) {
ResourceWeakReference activeRef = activeEngineResources.get(key);
if (activeRef == null) {
return null;
}
EngineResource<?> active = activeRef.get();
if (active == null) {
cleanupActiveReference(activeRef);
}
return active;
}
- loadFromCache 获取LruResourceCache缓存
!. 其通过构造方法传入engine,变量为cache
//GlideBuild.build
engine =
new Engine(
memoryCache,
diskCacheFactory,
diskCacheExecutor,
sourceExecutor,
GlideExecutor.newUnlimitedSourceExecutor(),
animationExecutor,
isActiveResourceRetentionAllowed);
}
Engine(
MemoryCache cache,
...) {
this.cache = cache;
}
!!. 当从LruResourceCache获取缓存后,会将其删除,
private EngineResource<?> loadFromCache(Key key) {
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.activate(key, cached);
}
return cached;
}
private EngineResource<?> getEngineResourceFromCache(Key key) {
Resource<?> cached = cache.remove(key);
}
!!!. 然后添加到弱引用HashMap缓存中
//ActiveResource
synchronized void activate(Key key, EngineResource<?> resource) {
ResourceWeakReference toPut =
new ResourceWeakReference(
key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
if (removed != null) {
removed.reset();
}
}
写入
写入分析从获得返回数据开始分析,查看流程图我们直到其通知回调,最终会调用EngineJob.onResourceReady方法,我们分析写入内存缓存就从这里开始,省略前面的
- notifyCallbacksOfResult 方法内部会调用RequestBuilder.into传入的Executors.mainThreadExecutor()
- Executors.mainThreadExecutor传递线路
//RequestBuilder
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor());
Request request = buildRequest(target, targetListener, options, callbackExecutor);
//request最终通过obtainQuest生成
public static <R> SingleRequest<R> obtain(...
Executor callbackExecutor) {
}
//SingleReqeust.onSizeReady调用engine.load
public void onSizeReady(int width, int height) {
loadStatus =
engine.load(...
this(cb),
callbackExecutor);
}
//Engine.load创建engineJob,添加callbackExecutor,即Executors.mainThreadExecutor
//engineJob.addCallback(cb, callbackExecutor);
- 同时发现,添加的cb为SingleRequest,这个在数据返回的回调会用到
- Executors.mainThreadExecutor内部利用handler.post发送消息,这样我们发现其如何切换会主线程了
public static Executor mainThreadExecutor() {
return MAIN_THREAD_EXECUTOR;
}
private static final Executor MAIN_THREAD_EXECUTOR =
new Executor() {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
public void execute(@NonNull Runnable command) {
handler.post(command);
}
};
- 回到EngineJob的notifyCallbacksOfResult方法,其在最后回执行entry.executro方法,之所以上面说执行的Executors.mainThreadExecutor,这里继续说明,
- 倒叙最终是否是Executors.mainThreadExecutor
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb));
}
copy = cbs.copy();
synchronized void addCallback(final ResourceCallback cb, Executor callbackExecutor) {
stateVerifier.throwIfRecycled();
cbs.add(cb, callbackExecutor);
if (hasResource) {
// Acquire early so that the resource isn't recycled while the Runnable below is still sitting
// in the executors queue.
incrementPendingCallbacks(1);
callbackExecutor.execute(new CallResourceReady(cb));
} else if (hasLoadFailed) {
incrementPendingCallbacks(1);
callbackExecutor.execute(new CallLoadFailed(cb));
} else {
Preconditions.checkArgument(!isCancelled, "Cannot add callbacks to a cancelled EngineJob");
}
}
- 首先是copy,然后我们找到cps,最后发现addCallBack最终添加到了cbs,在上面查看Executors.mainThreadExecutor的传递路线,最终就是通过addCallBack添加的,这样就明白了
- 这样notifyCallbacksOfResult方法最后的执行,就回到主线程,这样我们看CallResourceReady,其应该实现了Runnable接口,看起run方法
- run方法里面回调用callCallbackOnResourceReady(cb)
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb));
}
private class CallResourceReady implements
@Override
public void run() {
synchronized (cb.getLock()) {
synchronized (EngineJob.this) {
if (cbs.contains(cb)) {
// Acquire for this particular callback.
engineResource.acquire();
callCallbackOnResourceReady(cb);
removeCallback(cb);
}
decrementPendingCallbacks();
}
}
}
- callCallbackOnResourceReady(cb)回调cb即SingleRequest的onResourceReady
- target即DrawableImageTarget这样
- 最终设置给了iamgeView
target.onResourceReady(result, animation);
@Override
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
if (transition == null || !transition.transition(resource, this)) {
setResourceInternal(resource);
}
...
}
private void setResourceInternal(@Nullable Z resource) {
// Order matters here. Set the resource first to make sure that the Drawable has a valid and
// non-null Callback before starting it.
setResource(resource);
maybeUpdateAnimatable(resource);
}
@Override
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
上面1,2主要分析了Glide如何将返回的数据设置给iamgeView,发现其是通过handlers实现的,同时我们分析也可以知道,如果设置缓存应该在前面 3,再次回到notifyCallbacksOfResult,分析内存的添加,其调用下面的方法
engineJobListener.onEngineJobComplete(this, localKey, localResource);
- engineJobListener实际是Engine.
engineJobFactory =
new EngineJobFactory(
diskCacheExecutor,
sourceExecutor,
sourceUnlimitedExecutor,
animationExecutor,
/*engineJobListener=*/ this,
/*resourceListener=*/ this);
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
<R> EngineJob<R> build(
Key key,
boolean isMemoryCacheable,
boolean useUnlimitedSourceGeneratorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache) {
EngineJob<R> result = Preconditions.checkNotNull((EngineJob<R>) pool.acquire());
}
final Pools.Pool<EngineJob<?>> pool =
FactoryPools.threadSafe(
JOB_POOL_SIZE,
new FactoryPools.Factory<EngineJob<?>>() {
@Override
public EngineJob<?> create() {
return new EngineJob<>(
diskCacheExecutor,
sourceExecutor,
sourceUnlimitedExecutor,
animationExecutor,
engineJobListener,
resourceListener,
pool);
}
});
- 这样我们进入Engine的onEngineJobComplete
public synchronized void onEngineJobComplete(
EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
// A null resource indicates that the load failed, usually due to an exception.
if (resource != null && resource.isMemoryCacheable()) {
activeResources.activate(key, resource);
}
jobs.removeIfCurrent(key, engineJob);
}
4,内存添加方法的最终调用
- 上面的步骤最终调用 activeResources.activate,其内部回添加弱引用缓存
synchronized void activate(Key key, EngineResource<?> resource) {
ResourceWeakReference toPut =
new ResourceWeakReference(
key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
}
- 但是没有发现LruResourceCache缓存的添加,我们知道notifyCallbacksOfResult内部最终调用CallResourceReady的run方法
- 前面没有找到,我们从后面寻找,发现其最后回调用decrementPendingCallbacks
- decrementPendingCallbacks内部回调用EngineReource.release
- release方法内部调用listener.onResourceReleased,那我们周到listener即可
- listener为接口ResourceListener的方法,那我们寻找其实现,我们发现Engine实现了给接口
- 但是我们好奇其是在哪里设置进去的呢,在notifyCallbacksOfResul里面找到下面一句代码
engineResource = engineResourceFactory.build(resource, isCacheable, key, resourceListener);
- resourceListener 是在EngineJob构造方法设置进去的,那我们返回上面EngineJob构建,发现跟engineJobListener的设置一样,在创建engineJobFactory设置进去
engineJobFactory =
new EngineJobFactory(
diskCacheExecutor,
sourceExecutor,
sourceUnlimitedExecutor,
animationExecutor,
/*engineJobListener=*/ this,
/*resourceListener=*/ this);
5,接下来,进入 EngineResource.ResourceListener的onResourceReleased方法,即Engine
- 这里我们发现了cache的put方法,到此,内存的添加就都完成了
public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
activeResources.deactivate(cacheKey);
if (resource.isMemoryCacheable()) {
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}
我们总结一下内存的写入
- 首先我们分析了Glide如何把返回的数据设置给imageView,其是通过handler.post一个消息,切换回主线程,然后通过回调SingleRequest的方法,然后调用Target.onResourceReady方法,最终调用了ImageView的setImageDrawable方法
- 然后我们分析了其如何往两个内存里写入数据
- 首先在创建EngineJob的实例是会传入两个监听engineJobListener和resourceListener,其就是Engine本身
- 然后在任务完成回调EngineJob的onResourceReady,接着调用notifyCallbacksOfResult,在这个方法里,调用engineJobListener(Engine).onEngineJobComplete设置弱引用Hashmap缓存,同时,在notifyCallbacksOfResult方法,会创建EngineResource,并将resourceListener传入,即Engine
- 然后在执行Handler.post的消息时,当设置完资源给iamgeView时,最终会调用engineresource的release方法
- release方法,最终会调用resourceListener.onResourceReleased,即engine的方法
- 然后在engine重写的这个方法里设置LruResourceCache缓存