一篇文章搞定《图片框架Glide》
一篇文章搞定《图片框架Glide》
前言
别走!别走!本篇文章一定不会因为Glide的庞大让你失去兴趣!!!
Glide的源码,非常非常的庞大,很多人都被直接的劝退。
如果你要一点点挖,解析全部的源码,那估计一个月你都解析不了这个框架的。
(说实话这个库的开源作者们也不是都了解框架中的各个分支的)
那么怎么办啊?
借用郭霖郭老师的一句话,抽丝剥茧、点到即止!!!!!!!
那么Glide我们怎么去搞呢?
首先大家要明白一点,看开源库是要学习什么呢?
答:(以下是我认为的学习三方框架的优先级)
- 第一点:学习牛逼的主流程框架的搭建
- 第二点:学习他优于常人的核心功能的实现
- 第三点:学习他如何优雅的解决框架的问题
- 第四点:学习他代码的设计模式,并帮他想一些能优化的点
- 第五点:能将上面四个学习点,运用到你自己的项目中,解决一些棘手的问题
因此我们的主流程是一定要懂的,其次我们要对关键的一些问题进行剖析。
主流程
主流程:我是真没有必要去说。为什么呢?
答:因为我就是再说都没有下面这篇文章写的优美,真的会让人快速的理解。佩服佩服!!!
别给我点赞,给他点赞,好吧。
读本篇文章之前,兄弟们先把这篇文章读一遍吧,真的能让你很好理解主流程。
Glide源码难看懂?用这个角度让你事半功倍!
三大流程with、into、load捋顺
我们先从主线的角度上,把我们链式调用的三个函数搞定。
Glide.with(context).load(url).into(imageView);
With
首先是with函数,with函数给我们返回了RequestManager。
他负责初次创建实例化我们的Glide对象
并绑定我们的Glide的生命周期从而控制图片的加载暂停。
RequestManager requestManager = Glide.with(context)
我们Command点进去with看一下
Glide.java
public class Glide implements ComponentCallbacks2 {
...
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
@NonNull
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
@NonNull
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
@NonNull
public static RequestManager with(@NonNull Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
@SuppressWarnings("deprecation")
@Deprecated
@NonNull
public static RequestManager with(@NonNull android.app.Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
@NonNull
public static RequestManager with(@NonNull View view) {
return getRetriever(view.getContext()).get(view);
}
}
都是一些重载的with方法,可以通过参数看到他代表着传入不同组件的生命周期。
最终都是通过getRetriever去返回我们的RequestManager
那就往下看看getRetriever都做了什么
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
return Glide.get(context).getRequestManagerRetriever();
}
public RequestManagerRetriever getRequestManagerRetriever() {
return requestManagerRetriever;
}
这其实就是创建Glide并获取RequestManager
往下看一下他的创建过程
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;
}
其中就是通过checkAndInitializeGlide去创建的,我们先不去看支线,只去看主线。看看他是怎么创建的
@GuardedBy("Glide.class")
private static void checkAndInitializeGlide(
@NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
....
initializeGlide(context, generatedAppGlideModule);
}
@GuardedBy("Glide.class")
private static void initializeGlide(
@NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
initializeGlide(context, new GlideBuilder(), generatedAppGlideModule);
}
@GuardedBy("Glide.class")
@SuppressWarnings("deprecation")
private static void initializeGlide(
@NonNull Context context,
@NonNull GlideBuilder builder,
@Nullable GeneratedAppGlideModule annotationGeneratedModule) {
......
Context applicationContext = context.getApplicationContext();
// 调用GlideBuilder.build方法创建Glide
Glide glide = builder.build(applicationContext);
// 注册内存管理的回调,因为Glide实现了ComponentCallbacks2接口
applicationContext.registerComponentCallbacks(glide);
// 保存glide实例到静态变量中
Glide.glide = glide;
}
可以看到Glide最终是通过Build一个工厂模式去创建的对象,当然创建对象使用工厂模式还是比较常见的。
看看Build中都默认配置了那些内容
@NonNull
Glide build(@NonNull Context context) {
// 创建请求图片线程池sourceExecutor
if (sourceExecutor == null) {
sourceExecutor = GlideExecutor.newSourceExecutor();
}
// 创建硬盘缓存线程池diskCacheExecutor
if (diskCacheExecutor == null) {
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
}
// 创建动画线程池animationExecutor
if (animationExecutor == null) {
animationExecutor = GlideExecutor.newAnimationExecutor();
}
if (memorySizeCalculator == null) {
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
}
if (connectivityMonitorFactory == null) {
connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
}
if (bitmapPool == null) {
// 依据设备的屏幕密度和尺寸设置各种pool的size
int size = memorySizeCalculator.getBitmapPoolSize();
if (size > 0) {
// 创建图片线程池LruBitmapPool,缓存所有被释放的bitmap
// 缓存策略在API大于19时,为SizeConfigStrategy,小于为AttributeStrategy。
// 其中SizeConfigStrategy是以bitmap的size和config为key,value为bitmap的HashMap
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
// 创建对象数组缓存池LruArrayPool,默认4M
if (arrayPool == null) {
arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSiz eInBytes());
}
// 创建LruResourceCache,内存缓存
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCa cheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
// 创建任务和资源管理引擎(线程池,内存缓存和硬盘缓存对象)
if (engine == null) {
engine =
new Engine(
memoryCache,
diskCacheFactory,
diskCacheExecutor,
sourceExecutor,
GlideExecutor.newUnlimitedSourceExecutor( ),
GlideExecutor.newAnimationExecutor(),
isActiveResourceRetentionAllowed);
}
RequestManagerRetriever requestManagerRetriever =
new RequestManagerRetriever(requestManagerFactory);
return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptions.lock(),
defaultTransitionOptions);
}
可以看到在Glide的工厂模式中我们配置了,图片、缓存、动画使用到的线程池,还有关键的任务引擎。
到此呢我们就只是将我们的上面Glide.with(context)中刚进来提及到的。
很多的重载方法中的getRetriever(context)说完了,他最终返回的是RequestManagerRetriever对象。之后调用了RequestManagerRetriever中的get(context)
我们来看看RequestManagerRetriever的get(context)中有什么
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {
//判断是主线程还是子线程
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
frameWaiter.registerSelf(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
Glide也就是通过这个get方法去绑定了他的生命周期的。(先记住是这里绑定的生命周期的。后面会着重讲)
可以看到会利用我们传入的Activity、Fragment、context去获取我们的RequestManager。从而利用生命周期对图片加载进行管理。
这里算是他的支线,先不说那么细,后面会把生命周期问题单独拿出来说的。也是方便有读者来专门看生命周期的问题的。
Load
其次就是我们的Load函数,load函数比较简单,让我们来看看。
他负责的很少,主要是初始化RequestBuilder,并设置需要加载的URL
RequestBuilder<Drawable> requestBuilder = Glide.with(context).load(URL)
可以看到他返回给我们的是RequestBuilder对象。
我们Command进去看看
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);
}
load方法里面调用的asDrawable(),主要是初始化RequestBuilder
protected RequestBuilder(Glide glide, RequestManager requestManager,
Class<TranscodeType> transcodeClass, Context context) {
this.glide = glide;
this.requestManager = requestManager;
//注意,这里传入的是Drawable.classthis.transcodeClass = transcodeClass;
this.defaultRequestOptions = requestManager.getDefaultRequestOptions();
this.context = context;
this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
this.requestOptions = defaultRequestOptions;
this.glideContext = glide.getGlideContext();
}
最后对model进行一个赋值,方便后面请求图片使用。并且更新他设置了URL的状态为true。
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。但是RequestBuilder却有着大用处,因为into是基于他调用的。into是很复杂的一个加载图片的过程,包含着三级缓存的知识。
Into
into非常的庞大,我们只去说他的主流程,并且只讲到网络请求的部分。因为在4.0版本之后,Glide已经使用OkHttp来解决网络请求图片了。
上面我们说到,Load帮我们返回一个RequestBuilder,来调用into函数。
让我们来具体看一看他的内容吧。
Glide.with(this).load("url").into(imageView)
RequestBuilder.into(ImageView imageView)
写了一部分还是放弃了,因为into流程实在是太庞大了。我希望这篇文章是关注一些重点的问题。和想让人们从当前框架的学习的内容,而不是花一个月的时间来研究一个into流程。(ps:真的需要一个月的时间能看明白就不错了)
推荐一个博客,大家如果想去深入的话可以去研读一下。这里就不搞在这里了。太臃肿了,而且容易让读者放弃。
Glide源码详细解析
其他常见问题详解
一定要把上面的主流程看一遍,咱们再来看下面的这些核心问题!!!
开整!!!
与其他图片框架有什么区别
首先主流的框架有Glide、Picasso、Coil
咱们只说关键点!!!真别墨迹别的乱七八糟的。
只说你如果选择框架,你去选择的理由!!!
Coil
先说Coil,Coil是Android上的一个全新的图片加载框架,它的全名叫做coroutine image loader,即协程图片加载库。
他有什么特点
- 完全使用kotlin编写,使用了kotlin的协程,项目全是Kotlin的话,推荐这个图片框架。因为既然使用了Kotlin编写,那么一定适配了Kotlin的各种高阶、便捷的调用和使用。
- 它支持GIF和SVG,并且可以执行四个默认转换:模糊,圆形裁剪,灰度和圆角。
- 足够轻量,只有大概1500个核心方法。相比Glide和Picasso来说。
- 因为是新库,所以对于之前Glide和Picasso的性能是有着提升的,内部使用了OKHttp、Okio、LifeCycle等开源库。
总结:如果你是kotlin工程、或者新起工程、就直接用Coil的。(ps:后面会出一篇文章去看Coil)
Glide
老牌框架Glide
- 第一点肯定是要适配工程的,因为老牌框架,要看你工程中的使用情况。因为你不能想换新框架去改动整个项目啊。你想牵一发就动全身?
- 三级缓存杠杠的,有效的避免OOM的出现,当然Coil也是杠杠的,相比之下Picasso再加载大图的时候就会出现性能的问题。
- Glide可以加载Gif动图,Picasso不可以加载动图。
- Glide它会为每种大小的ImageView都缓存一次。Picasso只缓存一个全尺寸的。因此Glide加载更快,但是需要内存空间更大。
总结:老项目中Glide使用很多无法重写、Gif图加载、空间换性能。就选Glide
Picasso
没他奶奶屌用,他的功能,咱们完全可以包装Glide去实现。
- API使用简单,链式调用,一行代码就能使用,上手快
- 持异步加载,避免在主线程上执行图片加载操作。
- Picasso支持图片的转换功能,可以对图片进行常见的转换操作,如旋转、裁剪、缩放等。这样可以在不修改原始图片的情况下,对其进行各种处理,以满足不同的需求。
总结
- 新项目推荐Coil。
- 老项目Glide太多的话,还是去用Glide。
- 因为包体积的优化,项目中要统一使用一个框架。
Glide怎么监听的生命周期
我们上面说到Glide.with(context)会为我们创建一个
RequestManager requestManager = Glide.with(activity)
首先会通过getRetriever获取RequestManagerRetriever。之后调用其中的get方法。
@NonNull
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
因为上面也已经讲解了getRetriever的相关内容,我们直接看get(activity)方法
主要分为子线程和主线程的两个场景。
- 情况一:子线程的情况 如果是子线程就用Application级别的context,也就是不进行生命周期管理
- 情况二:主线程的情况 利用一个空的Fragment进行生命周期管理
@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {
//判断是主线程还是子线程
if (Util.isOnBackgroundThread()) {
//情况一:子线程的情况 如果是子线程就用Application级别的context,也就是不进行生命周期管理
return get(activity.getApplicationContext());
} else {
//情况二:主线程的情况 利用一个空的Fragment进行生命周期管理
// 判断Activity是否销毁
assertNotDestroyed(activity);
frameWaiter.registerSelf(activity);
//获取FragmentManager
FragmentManager fm = activity.getSupportFragmentManager();
//创建RequestManager
return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
情况一:子线程的情况
通过下面的源码可知,没有做生命周期的监听。
只是利用全局级别的context去创建RequestManager而已。(你可以认为就只是来取一个全局的Context)就去通过Glide.get去初始化Glide配置了。
return get(activity.getApplicationContext());
@NonNull
public RequestManager get(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
//如果是在主线程并且不是Application的Context
} else if (Util.isOnMainThread() && !(context instanceof Application)) {
if (context instanceof FragmentActivity) {
//通过FragmentActivity获取RequestManager
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
//通过Activity获取RequestManager
return get((Activity) context);
} else if (context instanceof ContextWrapper
&& ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
//通过ContextWrapper获取RequestManager
return get(((ContextWrapper) context).getBaseContext());
}
}
//通过全局的Content创建RequestManager
return getApplicationManager(context);
}
@NonNull
private RequestManager getApplicationManager(@NonNull Context context) {
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
//创建Glide 利用全局的Context
Glide glide = Glide.get(context.getApplicationContext());
applicationManager =
factory.build(
glide,
new ApplicationLifecycle(),
new EmptyRequestManagerTreeNode(),
context.getApplicationContext());
}
}
}
return applicationManager;
}
情况二:主线程的情况 利用一个空的Fragment进行生命周期管理
return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
@NonNull
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,
boolean isParentVisible) {
//利用创建的FragmentManager去获取SupportRequestManagerFragment(Fragment)
SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
//初始化Glide配置
Glide glide = Glide.get(context);
//通过工厂方法创建requestManager,这个requestManager工厂方法可以在外部配置,
//创建requestManager的时候就会注册相关Activity生命周期的回调
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
if (isParentVisible) {
requestManager.onStart();
}
//设置RequestManager
current.setRequestManager(requestManager);
}
return requestManager;
}
来看看SupportRequestManagerFragment
它是一个绑定在Activity上不显示的Fragment用于监听Activity的状态
SupportRequestManagerFragment.java
@NonNull
private SupportRequestManagerFragment getSupportRequestManagerFragment(
@NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
//通过tag找到Fragment
SupportRequestManagerFragment current =
(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
//如果缓存了那么取出缓存的
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
//没有缓存需要创建一个,创建ActivityFragmentLifecycle,监听生命周期
current = new SupportRequestManagerFragment();
//这是隐藏Fragment用于监听
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
//调用生命周期onStart()
current.getGlideLifecycle().onStart();
}
pendingSupportRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
//删除FragmentManager
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
}
这里是有小细节的,他缓存了当前的ActivityFragmentLifecycle,防止重复创建。没有才会创建的。能增加效率就增加效率。牛!!!
最后通过current.setParentFragmentHint(parentHint);
将我们创建好的设置给ActivityFragmentLifecycle。
我们往下看看current.setParentFragmentHint(parentHint);
void setParentFragmentHint(@Nullable Fragment parentFragmentHint) {
this.parentFragmentHint = parentFragmentHint;
if (parentFragmentHint == null || parentFragmentHint.getContext() == null) {
return;
}
FragmentManager rootFragmentManager = getRootFragmentManager(parentFragmentHint);
if (rootFragmentManager == null) {
return;
}
//进行注册,通过parentFragmentHint获取的rootFragmentManager
registerFragmentWithRoot(parentFragmentHint.getContext(), rootFragmentManager);
}
@Nullable
private static FragmentManager getRootFragmentManager(@NonNull Fragment fragment) {
while (fragment.getParentFragment() != null) {
fragment = fragment.getParentFragment();
}
return fragment.getFragmentManager();
}
可以看到这里获取了我们创建的空Fragment的FragmentManager。从而进行注册。
这里注册过程就结束了。那么他是怎么去监听生命周期的呢?
简化以下我们创建的空的Fragment(SupportRequestManagerFragment)的代码
public class SupportRequestManagerFragment extends Fragment {
private final ActivityFragmentLifecycle lifecycle;
public SupportRequestManagerFragment() {
this(new ActivityFragmentLifecycle());
}
@VisibleForTesting
@SuppressLint("ValidFragment")
public SupportRequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {
this.lifecycle = lifecycle;
}
@Override
public void onStart() {
super.onStart();
lifecycle.onStart();
}
@Override
public void onStop() {
super.onStop();
lifecycle.onStop();
}
ActivityFragmentLifecycle getGlideLifecycle() {
return lifecycle;
}
......
}
可以看到我们利用这个注册过的Fragment的生命周期的感知,进行回调。这里的lifecycle可不是jetpack里面的Lifecycle哦。就是他自定义的回调事件。那么有回调事件就一定有注册、监听的地方。
这个注册监听的位置,是在我们利用工厂模式创建RequestManager去做的。
在我们的RequestManagerRetriever类中也就是我们源码中通过getRetriever(activity)时获取的。
之后通过get获取RequestManager。不知道大家还有没有印象了。
RequestManagerRetriever.java
//创建Glide实例
Glide glide = Glide.get(context);
requestManager =factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
//factory接口
public interface RequestManagerFactory {
@NonNull
RequestManager build(
@NonNull Glide glide,
@NonNull Lifecycle lifecycle,
@NonNull RequestManagerTreeNode requestManagerTreeNode,
@NonNull Context context);
}
//默认的RequestManager工厂
private static final RequestManagerFactory DEFAULT_FACTORY =
new RequestManagerFactory() {
@NonNull
@Override
public RequestManager build(
@NonNull Glide glide,
@NonNull Lifecycle lifecycle,
@NonNull RequestManagerTreeNode requestManagerTreeNode,
@NonNull Context context) {
return new RequestManager(glide, lifecycle, requestManagerTreeNode, context);
}
};
public class RequestManager
implements ComponentCallbacks2, LifecycleListener,...{
RequestManager(
Glide glide,
Lifecycle lifecycle,
RequestManagerTreeNode treeNode,
RequestTracker requestTracker,
ConnectivityMonitorFactory factory,
Context context) {
this.glide = glide;
this.lifecycle = lifecycle;
this.treeNode = treeNode;
this.requestTracker = requestTracker;
this.context = context;
......
if (Util.isOnBackgroundThread()) {
Util.postOnUiThread(addSelfToLifecycle);
} else {
//lifecycle的生命周期回调加入到RequestManager中
lifecycle.addListener(this);
}
......
}
public synchronized void onStart() {
resumeRequests();
// targetTracker维持着Traker列表,每个Traker属于Glide内部需要监听生命周期的逻辑
targetTracker.onStart();
}
public synchronized void onStop() {
pauseRequests();
targetTracker.onStop();
}
}
可以看到是在利用工厂模式构建RequestManager中对lifecycle 进行了注册,之后在空的Fragment中进行了触发。
最后的监听也在RequestManager中的onStart和onStop中了。最后也是通过实现了Request接口的SingleRequest进行了图片的暂停(clear)和开始(begin),两个方法。有兴趣的可以去看一下,就在SingleRequest.java中。
为什么Glide不能在子线程中with
因为在子线程调用,不会产生一个空白的Fragment和Activity进行生命周期的绑定;
可以看源码来证明这一点:(ps:认真看了上面的生命周期的,绝对知道怎么回事)
- 情况一:子线程的情况 如果是子线程就用Application级别的context,也就是不进行生命周期管理
- 情况二:主线程的情况 利用一个空的Fragment进行生命周期管理
@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {
//判断是主线程还是子线程
if (Util.isOnBackgroundThread()) {
//情况一:子线程的情况 如果是子线程就用Application级别的context,也就是不进行生命周期管理
return get(activity.getApplicationContext());
} else {
//情况二:主线程的情况 利用一个空的Fragment进行生命周期管理
// 判断Activity是否销毁
assertNotDestroyed(activity);
frameWaiter.registerSelf(activity);
//获取FragmentManager
FragmentManager fm = activity.getSupportFragmentManager();
//创建RequestManager
return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
那这会怎么样呢?
因为没有了生命周期的监听,可能导致Activity销毁了,图片请求资源仍然存在,导致内存泄漏。
大家不要误解哦,这里的内存泄漏可不是说,图片或者引用的context,因为我们并没有引用当前的Activity和Fragment。那么这里的GCRoot是谁呢?
答:是子线程本身啊,兄弟们。 子线程本身就是GcRoot啊。(ps:之前说过活动线程是GcRoot的哦!!!)
如果在一个页面中使用Glide加载了一张图片,图片正在获取中,如果突然关闭页面,这个页面会造成内存泄漏吗?简单说一下内存泄漏的场景。
通过上面两个问题的分析,相比大家也知道答案了。
答案:
- 因为Glide 在加载资源的时候,如果是在 Activity、Fragment 这一类有生命周期的组件上进行的话,会创建一个透明的 RequestManagerFragment 加入到FragmentManager 之中,感知生命周期,当 Activity、Fragment 等组件进入不可见,或者已经销毁的时候,Glide 会停止加载资源。因此也不会造成内存泄漏。
- 但是在非生命周期的组件上进行时(Service、IntentService、BroadcastReceiver),会采用Application 的生命周期贯穿整个应用,所以 applicationManager 只有在应用程序关闭的时候终止加载。这样就会有内存泄漏的概率出现。
Glide如何监听网络变化的
从上面页面生命周期的分析部分知道,对于任务的控制都是通过RequestManager,还是到它里面去看,实现网络变化监听的就是ConnectivityMonitor:
RequestManager.java
public class RequestManager implements LifecycleListener,
ModelTypes<RequestBuilder<Drawable>> {
...
protected final Glide glide;
protected final Context context;
@Synthetic final Lifecycle lifecycle;
private final RequestTracker requestTracker;
private final RequestManagerTreeNode treeNode;
private final TargetTracker targetTracker = new TargetTracker();
private final Handler mainHandler = new Handler(Looper.getMainLooper());
private final ConnectivityMonitor connectivityMonitor;
...
RequestManager(
Glide glide,
Lifecycle lifecycle,
RequestManagerTreeNode treeNode,
RequestTracker requestTracker,
ConnectivityMonitorFactory factory,
Context context) {
this.glide = glide;
this.lifecycle = lifecycle;
this.treeNode = treeNode;
this.requestTracker = requestTracker;
this.context = context;
connectivityMonitor =
factory.build(
context.getApplicationContext(),
new RequestManagerConnectivityListener(requestTracker));
if (Util.isOnBackgroundThread()) {
mainHandler.post(addSelfToLifecycle);
} else {
lifecycle.addListener(this);
}
lifecycle.addListener(connectivityMonitor);
...
}
其实和生命周期是相似的。也是把它注册为ActivityFragmentLifecycle的观察者,ConnectivityMonitor通过ConnectivityMonitorFactory进行构造,提供了默认实现类DefaultConnectivityMonitorFactory
DefaultConnectivityMonitorFactory.java
public class DefaultConnectivityMonitorFactory implements ConnectivityMonitorFactory {
private static final String TAG = "ConnectivityMonitor";
private static final String NETWORK_PERMISSION = "android.permission.ACCESS_NETWORK_STATE";
@NonNull
@Override
public ConnectivityMonitor build(
@NonNull Context context,
@NonNull ConnectivityMonitor.ConnectivityListener listener) {
int permissionResult = ContextCompat.checkSelfPermission(context, NETWORK_PERMISSION);
boolean hasPermission = permissionResult == PackageManager.PERMISSION_GRANTED;
return hasPermission
? new DefaultConnectivityMonitor(context, listener) : new NullConnectivityMonitor();
}
}
接着就往下看DefaultConnectivityMonitor, 在onStart中registerReceiver监听手机网络状态变化的广播,然后在connectivityReceiver中调用isConnect进行网络状态确认,根据网络状态是否变化,如果有变化就回调监听ConnectivityMonitor.ConnectivityListener:
final class DefaultConnectivityMonitor implements ConnectivityMonitor {
private static final String TAG = "ConnectivityMonitor";
private final Context context;
@SuppressWarnings("WeakerAccess") @Synthetic final ConnectivityListener listener;
@SuppressWarnings("WeakerAccess") @Synthetic boolean isConnected;
private boolean isRegistered;
private final BroadcastReceiver connectivityReceiver = new BroadcastReceiver() {
@Override
public void onReceive(@NonNull Context context, Intent intent) {
boolean wasConnected = isConnected;
isConnected = isConnected(context);
if (wasConnected != isConnected) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "connectivity changed, isConnected: " + isConnected);
}
listener.onConnectivityChanged(isConnected);
}
}
};
ConnectivityMonitor.ConnectivityListener是在RequestManager中传入,有网络重新连接后重启请求:
RequestManager.java
private static class RequestManagerConnectivityListener implements ConnectivityMonitor
.ConnectivityListener {
private final RequestTracker requestTracker;
RequestManagerConnectivityListener(@NonNull RequestTracker requestTracker) {
this.requestTracker = requestTracker;
}
@Override
public void onConnectivityChanged(boolean isConnected) {
if (isConnected) {
requestTracker.restartRequests();
}
}
}
Glide如何监测内存
在Glide构造的时候会调用registerComponentCallbacks进行全局注册, 系统在内存紧张的时候回调onTrimMemory,然后根据系统内存紧张级别进行memoryCache/bitmapPool/arrayPool的回收
Glide.java
public static Glide get(@NonNull Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context);
}
}
}
return glide;
}
private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
Context applicationContext = context.getApplicationContext();
...
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}
@Override
public void onTrimMemory(int level) {
trimMemory(level);
}
public void trimMemory(int level) {
Util.assertMainThread();
memoryCache.trimMemory(level);
bitmapPool.trimMemory(level);
arrayPool.trimMemory(level);
}
Glide加载了一个300x300的图片后,修改ImagerView尺寸为100x100那么是压缩再加载吗?
No,不是。Glide它会为每个不同尺寸的Imageview缓存一张图片,也就是说不管你的这张图片有没有加载过,只要imageview的尺寸不一样,那么Glide就会重新加载一次,这时候,它会在加载的imageview之前从网络上重新下载,然后再缓存。
也就是说:如果一个页面的imageview是300 * 300像素,而另一个页面中的imageview是100 * 100像素,这时候想要让两个imageview像是同一张图片,那么Glide需要下载两次图片,并且缓存两张图片。
源码证明这个答案:
可以看到width, height是组成EngineKey的成员,也就是说宽高不同,Key就不同,那么就不可能在之前的缓存HashMap中获取到缓存。
因此需要重新请求,缓存重新设置尺寸的图片。
public <R> LoadStatus load() {
// 根据请求参数得到缓存的键
EngineKey key = keyFactory.buildKey(model, signature, width, height,
transformations,resourceClass, transcodeClass, options);
}
但是Picasso会不管imageview大小是什么,总是直接缓存整张图片。之后通过压缩等算法去调整。
怎么去设计一个图片框架呢
那就从后往前来吧!
1、网络请求:我直接上一个OkHttp
2、线程操作:异步加载+线程切换
3、编解码:支持多种格式图片的编解码
4、图片压缩:我直接抄一个鲁班压缩
5、多级缓存:我直接抄一个三级缓存
6、生命周期管理:通过对生命周期的管理,来管理我的图片框架的启动和停止
7、资源回收:Bitmap回收和复用(Bitmap.recycle)
总结
总结三方框架真的让人心力交瘁,但是很有意义(仅限于总结完的那一霎那)。
更多推荐
所有评论(0)