一篇文章搞定《图片框架Glide》

阅读: 评论:0

一篇文章搞定《图片框架Glide》

一篇文章搞定《图片框架Glide》

一篇文章搞定《图片框架Glide》

  • 前言
  • 主流程
  • 三大流程with、into、load捋顺
    • With
    • Load
    • Into
  • 其他常见问题详解
    • 与其他图片框架有什么区别
    • Glide怎么监听的生命周期
    • 为什么Glide不能在子线程中with
    • 如果在一个页面中使用Glide加载了一张图片,图片正在获取中,如果突然关闭页面,这个页面会造成内存泄漏吗?简单说一下内存泄漏的场景。
    • Glide如何监听网络变化的
    • Glide如何监测内存
    • Glide加载了一个300x300的图片后,修改ImagerView尺寸为100x100那么是压缩再加载吗?
    • 怎么去设计一个图片框架呢
  • 总结

前言

别走!别走!本篇文章一定不会因为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 {...@NonNullpublic static RequestManager with(@NonNull Context context) {return getRetriever(context).get(context);}@NonNullpublic static RequestManager with(@NonNull Activity activity) {return getRetriever(activity).get(activity);}@NonNullpublic static RequestManager with(@NonNull FragmentActivity activity) {return getRetriever(activity).get(activity);}@NonNullpublic static RequestManager with(@NonNull Fragment fragment) {return Activity()).get(fragment);}@SuppressWarnings("deprecation")@Deprecated@NonNullpublic static RequestManager with(@NonNull android.app.Fragment fragment) {return Activity()).get(fragment);}@NonNullpublic static RequestManager with(@NonNull View view) {return Context()).get(view);}
}

都是一些重载的with方法,可以通过参数看到他代表着传入不同组件的生命周期。
最终都是通过getRetriever去返回我们的RequestManager
那就往下看看getRetriever都做了什么

private static RequestManagerRetriever getRetriever(@Nullable Context context) {(context).getRequestManagerRetriever();
}
public RequestManagerRetriever getRequestManagerRetriever() {return requestManagerRetriever;
}

这其实就是创建Glide并获取RequestManager
往下看一下他的创建过程

public static Glide get(@NonNull Context context) {if (glide == null) {GeneratedAppGlideModule annotationGeneratedModule =ApplicationContext());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 = ApplicationContext();// 调用GlideBuilder.build方法创建GlideGlide glide = builder.build(applicationContext);// 注册内存管理的回调,因为Glide实现了ComponentCallbacks2接口isterComponentCallbacks(glide);// 保存glide实例到静态变量中Glide.glide = glide;
}   

可以看到Glide最终是通过Build一个工厂模式去创建的对象,当然创建对象使用工厂模式还是比较常见的。
看看Build中都默认配置了那些内容

@NonNullGlide build(@NonNull Context context) {// 创建请求图片线程池sourceExecutorif (sourceExecutor == null) {sourceExecutor =   wSourceExecutor();}// 创建硬盘缓存线程池diskCacheExecutorif (diskCacheExecutor == null) {diskCacheExecutor =   wDiskCacheExecutor();}// 创建动画线程池animationExecutorif (animationExecutor == null) {animationExecutor =   wAnimationExecutor();}if (memorySizeCalculator == null) {memorySizeCalculator = new   MemorySizeCalculator.Builder(context).build();}if (connectivityMonitorFactory == null) {connectivityMonitorFactory = new   DefaultConnectivityMonitorFactory();}if (bitmapPool == null) {// 依据设备的屏幕密度和尺寸设置各种pool的sizeint size =   BitmapPoolSize();if (size > 0) {// 创建图片线程池LruBitmapPool,缓存所有被释放的bitmap// 缓存策略在API大于19时,为SizeConfigStrategy,小于为AttributeStrategy。// 其中SizeConfigStrategy是以bitmap的size和config为key,value为bitmap的HashMapbitmapPool = new LruBitmapPool(size);} else {bitmapPool = new BitmapPoolAdapter();}}// 创建对象数组缓存池LruArrayPool,默认4Mif (arrayPool == null) {arrayPool = new   ArrayPoolSiz  eInBytes());}// 创建LruResourceCache,内存缓存if (memoryCache == null) {memoryCache = new   MemoryCa  cheSize());}if (diskCacheFactory == null) {diskCacheFactory = new   InternalCacheDiskCacheFactory(context);}// 创建任务和资源管理引擎(线程池,内存缓存和硬盘缓存对象)if (engine == null) {engine =new Engine(memoryCache,diskCacheFactory,diskCacheExecutor,wUnlimitedSourceExecutor(  ),wAnimationExecutor(),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)中有什么

 @NonNullpublic static RequestManager with(@NonNull Context context) {return getRetriever(context).get(context);}
@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {//判断是主线程还是子线程if (Util.isOnBackgroundThread()) {return ApplicationContext());} else {assertNotDestroyed(activity);isterSelf(activity);FragmentManager fm = SupportFragmentManager();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 = questManager = requestManager;//注意,这里传入的是anscodeClass = transcodeClass;this.defaultRequestOptions = DefaultRequestOptions();t = ansitionOptions = DefaultTransitionOptions(transcodeClass);questOptions = defaultRequestOptions;this.glideContext = GlideContext();
}

最后对model进行一个赋值,方便后面请求图片使用。并且更新他设置了URL的状态为true。

public RequestBuilder<TranscodeType> load(@Nullable String string) {return loadGeneric(string);}
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {del = 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 ApplicationContext());} else {//情况二:主线程的情况 利用一个空的Fragment进行生命周期管理// 判断Activity是否销毁assertNotDestroyed(activity);isterSelf(activity);//获取FragmentManagerFragmentManager fm = SupportFragmentManager();//创建RequestManagerreturn supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));}
}

情况一:子线程的情况
通过下面的源码可知,没有做生命周期的监听。
只是利用全局级别的context去创建RequestManager而已。(你可以认为就只是来取一个全局的Context)就去通过去初始化Glide配置了。

return ApplicationContext());@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获取RequestManagerreturn get((FragmentActivity) context);} else if (context instanceof Activity) {//通过Activity获取RequestManagerreturn get((Activity) context);} else if (context instanceof ContextWrapper&& ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {//通过ContextWrapper获取RequestManagerreturn get(((ContextWrapper) context).getBaseContext());}}
//通过全局的Content创建RequestManagerreturn getApplicationManager(context);
}@NonNull
private RequestManager getApplicationManager(@NonNull Context context) {if (applicationManager == null) {synchronized (this) {if (applicationManager == null) {//创建Glide 利用全局的ContextGlide glide = (ApplicationContext());applicationManager =factory.build(glide,new ApplicationLifecycle(),new EmptyRequestManagerTreeNode(),ApplicationContext());}}}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 = RequestManager();if (requestManager == null) {//初始化Glide配置Glide glide = (context);//通过工厂方法创建requestManager,这个requestManager工厂方法可以在外部配置,//创建requestManager的时候就会注册相关Activity生命周期的回调requestManager =factory.build(glide, GlideLifecycle(), RequestManagerTreeNode(), context);if (isParentVisible) {Start();}//设置RequestManagercurrent.setRequestManager(requestManager);}return requestManager;
}

来看看SupportRequestManagerFragment
它是一个绑定在Activity上不显示的Fragment用于监听Activity的状态
SupportRequestManagerFragment.java

 @NonNullprivate SupportRequestManagerFragment getSupportRequestManagerFragment(@NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {//通过tag找到FragmentSupportRequestManagerFragment current =(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);if (current == null) {//如果缓存了那么取出缓存的current = (fm);if (current == null) {//没有缓存需要创建一个,创建ActivityFragmentLifecycle,监听生命周期current = new SupportRequestManagerFragment();//这是隐藏Fragment用于监听current.setParentFragmentHint(parentHint);if (isParentVisible) {//调用生命周期onStart()GlideLifecycle().onStart();}pendingSupportRequestManagerFragments.put(fm, current);fm.beginTransaction().add(current, FRAGMENT_TAG)mitAllowingStateLoss();//删除FragmentManagerhandler.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 || Context() == null) {return;}FragmentManager rootFragmentManager = getRootFragmentManager(parentFragmentHint);if (rootFragmentManager == null) {return;}//进行注册,通过parentFragmentHint获取的Context(), rootFragmentManager);
}@Nullable
private static FragmentManager getRootFragmentManager(@NonNull Fragment fragment) {while (ParentFragment() != null) {fragment = ParentFragment();}FragmentManager();
}

可以看到这里获取了我们创建的空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;}@Overridepublic void onStart() {Start();Start();}@Overridepublic void onStop() {Stop();Stop();}ActivityFragmentLifecycle getGlideLifecycle() {return lifecycle;}......
}

可以看到我们利用这个注册过的Fragment的生命周期的感知,进行回调。这里的lifecycle可不是jetpack里面的Lifecycle哦。就是他自定义的回调事件。那么有回调事件就一定有注册、监听的地方。
这个注册监听的位置,是在我们利用工厂模式创建RequestManager去做的。
在我们的RequestManagerRetriever类中也就是我们源码中通过getRetriever(activity)时获取的。
之后通过get获取RequestManager。不知道大家还有没有印象了。
RequestManagerRetriever.java

        //创建Glide实例Glide glide = (context);requestManager =factory.build(glide, GlideLifecycle(), RequestManagerTreeNode(), context);//factory接口public interface RequestManagerFactory {@NonNullRequestManager build(@NonNull Glide glide,@NonNull Lifecycle lifecycle,@NonNull RequestManagerTreeNode requestManagerTreeNode,@NonNull Context context);}//默认的RequestManager工厂private static final RequestManagerFactory DEFAULT_FACTORY =new RequestManagerFactory() {@NonNull@Overridepublic 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 = Node = questTracker = t = context;......if (Util.isOnBackgroundThread()) {Util.postOnUiThread(addSelfToLifecycle);} else {//lifecycle的生命周期回调加入到RequestManager中lifecycle.addListener(this);}......}public synchronized void onStart() {resumeRequests();// targetTracker维持着Traker列表,每个Traker属于Glide内部需要监听生命周期的逻辑Start();}public synchronized void onStop() {pauseRequests();Stop();}
}

可以看到是在利用工厂模式构建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 ApplicationContext());} else {//情况二:主线程的情况 利用一个空的Fragment进行生命周期管理// 判断Activity是否销毁assertNotDestroyed(activity);isterSelf(activity);//获取FragmentManagerFragmentManager fm = SupportFragmentManager();//创建RequestManagerreturn 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 MainLooper());private final ConnectivityMonitor connectivityMonitor;...RequestManager(Glide glide,Lifecycle lifecycle,RequestManagerTreeNode treeNode,RequestTracker requestTracker,ConnectivityMonitorFactory factory,Context context) {this.glide = glide;this.lifecycle = Node = questTracker = t = context;connectivityMonitor =factory.ApplicationContext(),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@Overridepublic 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() {@Overridepublic 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);}ConnectivityChanged(isConnected);}}};

ConnectivityMonitor.ConnectivityListener是在RequestManager中传入,有网络重新连接后重启请求:
RequestManager.java

 private static class RequestManagerConnectivityListener implements ConnectivityMonitor.ConnectivityListener {private final RequestTracker requestTracker;RequestManagerConnectivityListener(@NonNull RequestTracker requestTracker) {questTracker = requestTracker;}@Overridepublic void onConnectivityChanged(boolean isConnected) {if (isConnected) {startRequests();}}}

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 = ApplicationContext();...isterComponentCallbacks(glide);Glide.glide = glide;}@Overridepublic void onTrimMemory(int level) {trimMemory(level);}public void trimMemory(int level) {Util.assertMainThread();imMemory(level);imMemory(level);imMemory(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回收和复用&#le)

总结

总结三方框架真的让人心力交瘁,但是很有意义(仅限于总结完的那一霎那)。

本文发布于:2024-02-04 07:59:10,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170702533653734.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

下一篇:h5 先加载小图
标签:一篇文章   框架   图片   Glide
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23