篇章目标要点
Glide是目前最为流行的图片加载框架,内部提供了缓存机制,本文系列目的是记录学习Glide缓存机制要点。缓存最主要的点就是读,写,控制,本文就是围绕这几个方面进行解读。目的是通过向源码学习形成自己的能力进度和思考。目前已经有很多的文章都有撰写相应的学习笔记,部分笔记存在的问题时,所摘取的代码片段未标记代码来源,所以对照源码阅读时,有时找不到位置,本文介绍方法时,会注明相应的类的路径。在第一篇文章中已经介绍了Gilde使用了哪些缓存框架实现的内存缓存和硬盘缓存,完成了下图0.缓存对象构建。第二篇文章中,完整的讲述了Glide加载网络图片的流程和原理(下图1.加载外部图片),加载的数据缓存的流程和原理(下图2.写入缓存),访问外部数据前使用缓存数据的流程(下图3.缓存读取)。本篇文章,重点阐述4.内存缓存空间限制,硬盘缓存空间限制,以及在View的生命周期发生变化时Glide内部的控制机制是怎么样的。
源码获取
可以在github下载Glide源码进行阅读,通过git工具下载
git clone https://github.com/bumptech/glide.git
内存缓存控制
1.Glide的build方法中会初始化一个BitmapPool进行图片的内存缓存,该对象的创建过程需要设定缓存空间,代码如下
if (bitmapPool == null) {
//设置Bitmap内存缓存大小,bitmapPool为内存缓存对象
int size = memorySizeCalculator.getBitmapPoolSize();
if (size > 0) {
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
2.其内存缓存空间大小是通过MemorySizeCalculator进行计算分配的,分配逻辑如下
/**
* 内存缓存+硬盘缓存的最大可用空间:低内存时为堆内存的0.33倍,否则为0.4倍
* 内存缓存:硬盘缓存的所占比例为Android.O以下为4:2,以上为1:2,低内存时为0:2
* @return
*/
private static int getMaxSize(
ActivityManager activityManager, float maxSizeMultiplier, float lowMemoryMaxSizeMultiplier) {
//获取堆内存大小,一般16M
final int memoryClassBytes = activityManager.getMemoryClass() * 1024 * 1024;
//RAM低于1GB为低内存设备
final boolean isLowMemoryDevice = isLowMemoryDevice(activityManager);
//低内存时为堆内存的0.33倍,否则为0.4倍
return Math.round(
memoryClassBytes * (isLowMemoryDevice ? lowMemoryMaxSizeMultiplier : maxSizeMultiplier));
}
3.对于自己使用的Android4.4.3设备分配的堆内存是16M,计算下来内存缓存空间上4.3M,硬盘缓存空间为2.1M.
硬盘缓存控制
硬盘缓存控制对象也是在Glide的Build方法中创建的,其缓存空间的分配逻辑同上述内存分配的代码
/**
* 设置硬盘缓存的大小
* 为memoryCache对象赋值为LruResourceCache类型,该类型继承LruCache,基于HashMap在内存中缓存图片
*/
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
View的生命周期感知
Glide需要在with()方法中传入context对象,顺着这条线我们看下其是如何实现生命周期感知的 1.Glide传入上下文参数,返回RequestManager对象
Glide.with(view.getContext()).load(getCurrentList().get(pos).url).placeholder(R.drawable.ic_launcher_foreground).into(imageView);
//返回RequestManager对象
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
2.无论上下文是Activity/ FragmentActivity /Fragment/View对象,从源码看内部都是相应的对象可见时才请求
//上下文对象为FragmentActivity时
@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
//非主线程调用时,不加载
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
frameWaiter.registerSelf(activity);
FragmentManager fm = activity.getSupportFragmentManager();
//主线程调用时,当Activity可见时请求数据
return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
//视图可见时执行请求
@NonNull
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,
boolean isParentVisible) {
SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
// This is a bit of hack, we're going to start the RequestManager, but not the
// corresponding Lifecycle. It's safe to start the RequestManager, but starting the
// Lifecycle might trigger memory leaks. See b/154405040
//视图可见时执行请求
if (isParentVisible) {
requestManager.onStart();
}
current.setRequestManager(requestManager);
}
return requestManager;
}
3.我们再看下传入的上下文为View对象时,上下文对象为View时,如该View是在一个Fragment中,那么该Fragment可见时执行请求;如其不在Fragment中,那么Activity可见时执行请求
/**
* 上下文对象为View时,如该View是在一个Fragment中,那么该Fragment可见时执行请求;
* 如其不在Fragment中,那么Activity可见时执行请求
* @param view
* @return
*/
@SuppressWarnings("deprecation")
@NonNull
public RequestManager get(@NonNull View view) {
if (Util.isOnBackgroundThread()) {
return get(view.getContext().getApplicationContext());
}
Preconditions.checkNotNull(view);
Preconditions.checkNotNull(
view.getContext(), "Unable to obtain a request manager for a view without a Context");
Activity activity = findActivity(view.getContext());
// The view might be somewhere else, like a service.
if (activity == null) {
return get(view.getContext().getApplicationContext());
}
// Support Fragments.
// Although the user might have non-support Fragments attached to FragmentActivity, searching
// for non-support Fragments is so expensive pre O and that should be rare enough that we
// prefer to just fall back to the Activity directly.
if (activity instanceof FragmentActivity) {
Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
return fragment != null ? get(fragment) : get((FragmentActivity) activity);
}
//确认View是否存在于某个Fragment中
// Standard Fragments.
android.app.Fragment fragment = findFragment(view, activity);
if (fragment == null) {
return get(activity);
}
return get(fragment);
}
3.返回的RequestManager对象实现了LifecycleListener,具有生命周期感知能力。以下摘取了生命周期关联的onStart()状态时恢复请求任务,onStop()状态时停止请求任务,页面销毁时清除任务。
public class RequestManager
implements ComponentCallbacks2, LifecycleListener, ModelTypes<RequestBuilder<Drawable>> {
……
//页面进入onStart()状态时恢复请求动作
/**
* Lifecycle callback that registers for connectivity events (if the
* android.permission.ACCESS_NETWORK_STATE permission is present) and restarts failed or paused
* requests.
*/
@Override
public synchronized void onStart() {
resumeRequests();
targetTracker.onStart();
}
//页面进入onStop()状态时停止请求动作
/**
* Lifecycle callback that unregisters for connectivity events (if the
* android.permission.ACCESS_NETWORK_STATE permission is present) and pauses in progress loads.
*/
@Override
public synchronized void onStop() {
pauseRequests();
targetTracker.onStop();
}
//页面销毁时清除任务
/**
* Lifecycle callback that cancels all in progress requests and clears and recycles resources for
* all completed requests.
*/
@Override
public synchronized void onDestroy() {
targetTracker.onDestroy();
for (Target<?> target : targetTracker.getAll()) {
clear(target);
}
targetTracker.clear();
requestTracker.clearRequests();
lifecycle.removeListener(this);
lifecycle.removeListener(connectivityMonitor);
Util.removeCallbacksOnUiThread(addSelfToLifecycle);
glide.unregisterRequestManager(this);
}
学习心得
了解了内存缓存和硬盘缓存的空间限制的逻辑代码,以及Glide会监听View所在的上下文的生命周期状态实现的生命周期感知。
|