内容简介:Glide4.0之后,GlideModule推荐使用注解(@GlideModule)形式实现
1.1、 Example
Glide.with(imageView.context) .load(roundRectUrl) .apply(RequestOptions().priority(Priority.HIGH)) .apply(RequestOptions().diskCacheStrategy(DiskCacheStrategy.DATA)) .apply(RequestOptions.centerCropTransform()) .apply( RequestOptions.bitmapTransform( RoundedCornersTransformation( radius, 0, RoundedCornersTransformation.CornerType.ALL ) ) ) .into(imageView) 复制代码
1.2、自定义AppGlideModule
Glide4.0之后,GlideModule推荐使用注解(@GlideModule)形式实现
@GlideModule class GlideConfigModule : AppGlideModule() { override fun applyOptions(context: Context, builder: GlideBuilder) { //设置内存大小 builder.setMemoryCache(LruResourceCache(1024 * 1024 * 100)) // 100M //设置图片缓存大小 builder.setBitmapPool(LruBitmapPool(1024 * 1024 * 50)) /** * 设置磁盘缓存大小 */ // 内部缓存目录 data/data/packageName/DiskCacheName builder.setDiskCache( InternalCacheDiskCacheFactory(context, "GlideDemo", 1024 * 1024 * 100) ) // 外部磁盘SD卡 /*builder.setDiskCache( ExternalPreferredCacheDiskCacheFactory(context, "GlideDemo", 1024 * 1024 * 10) )*/ } override fun registerComponents(context: Context, glide: Glide, registry: Registry) { // 替换网络请求组件,使用OkHttp val builder = OkHttpClient.Builder() builder.addInterceptor(ProgressInterceptor) val okHttpClient = builder.build() registry.replace(GlideUrl::class.java, InputStream::class.java, OkHttpUrlLoader.Factory(okHttpClient)) } /** * 禁用清单解析 */ override fun isManifestParsingEnabled(): Boolean { return false } } 复制代码
二、前言
2.1 一个好的图片加载框架必备的功能:
当阅读Glide源码之前,我们应该考虑一个问题,假设让我们自己去实现一个图片加载框架,会需要哪些功能?
1、网络组件,下载图片 HttpUrlConnection OkHttp
2、请求队列,线程池 优先级处理,请求的终止
3、缓存: 内存缓存,本地文件缓存,服务器缓存等等
4、内存资源回收机制: 先进先出,或者生命周期
5、更加基础的组件就是各种图片资源的转码解码了
2.2 Android图片加载框架对比
假设你去面试,面试官问你使用什么图片框架,你说你用Glide。面试官并不是想让你说Glide,而是你选择Glide图片加载框架的原因和理由。它与其他图片加载框架的优缺点。
这里引用前辈的文章,就不在单独再写一篇了: Android图片加载框架Fresco,Glide,Picasso对比分析
三、Glide流程分析
3.1 流程图
这是从网上找的流程图:
你心中会想:这么简单??
我回答你,当然不是这么简单,但这确实是最主要最底层的架构,所有的图片加载框架都一样,区别在于在这个基础上延伸的各种小细节,比如多少级缓存、缓存的位置(是缓存在应用的内存中,还是缓存在系统的共享匿名内存中)、生命周期管理等等。
3.2 Glide使用三步曲源码分析
这里就需要去阅读我的上一篇文章了:Glide源码分析One
3.3 类关系图
OK,当你看完了3.2后,再看Glide的类关系图你会相当的清晰,这个图也是我从网上找的:
接下来就是一些细节问题了
四、生命周期管理
生命周期管理是在: 第一步Glide.with()中创建的,具体逻辑在RequestManagerRetriever.get()中实现:
4.1 RequestManagerRetriever.get():
public RequestManager get(@NonNull Context context) { 、、、 if (Util.isOnMainThread() && !(context instanceof Application)) { if (context instanceof FragmentActivity) { return get((FragmentActivity) context); } else if (context instanceof Activity) { return get((Activity) context); } else if (context instanceof ContextWrapper) { return get(((ContextWrapper) context).getBaseContext()); } } return getApplicationManager(context); } 复制代码
get()方法重载了很多,参数对应与Glide.with()方法的参数,分别有Context、Activity、FragmentActivity、Fragment、android.support.v4.app.Fragment、View.
参数很重要,参数很重要,参数很重要!!!接下来给出解释: 1、RequestManagerRetriever.get(Activity): 参数为Activity的get()方法public RequestManager get(@NonNull Activity activity) { if (Util.isOnBackgroundThread()) { return get(activity.getApplicationContext()); } else { assertNotDestroyed(activity); android.app.FragmentManager fm = activity.getFragmentManager(); // FragmentManager直接来自Activity return fragmentGet( activity, fm, /*parentHint=*/ null, isActivityVisible(activity)); } } 复制代码
2、RequestManagerRetriever.get(Fragment): 参数为Fragment的get()方法
public RequestManager get(@NonNull Fragment fragment) { if (Util.isOnBackgroundThread()) { return get(fragment.getActivity().getApplicationContext()); } else { FragmentManager fm = fragment.getChildFragmentManager(); // FragmentManager来自Fragment return supportFragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible()); } } 复制代码
3、RequestManagerRetriever.get(View): 参数为View的get()方法
public RequestManager get(@NonNull View view) { ... Activity activity = findActivity(view.getContext()); 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(activity); } // Standard Fragments. android.app.Fragment fragment = findFragment(view, activity); if (fragment == null) { return get(activity); } return get(fragment); } 复制代码
通过View去寻找其所在的Fragment,如果该View在Fragment中,直接走参数为Fragment的get(Fragment)方法;如果在Activity中,则直接走参数为Activity的get(Activity)方法
重载的所有get()方法,最终都只有三种情况:
1)、 getApplicationManager()
2)、 fragmentGet()
3)、 supportFragmentGet()
- 1、 getApplicationManager() 当参数为ApplicationContext或者运行在非主线程时,会执行到getApplicationManager()方法:
private RequestManager getApplicationManager(@NonNull Context context) { // Either an application context or we are on a background thread. if (applicationManager == null) { synchronized (this) { if (applicationManager == null) { // Normally pause/resume is taken care of by the fragment we add to the fragment or // activity. However, in this case since the manager attached to the application will not // receive lifecycle events, we must force the manager to start resumed using // ApplicationLifecycle. // TODO(b/27524013): Factor out this Glide.get() call. Glide glide = Glide.get(context.getApplicationContext()); applicationManager = factory.build( glide, new ApplicationLifecycle(), new EmptyRequestManagerTreeNode(), context.getApplicationContext()); } } } return applicationManager; } 复制代码
看该方法的注释,Request(也就是缓存)的生命周期是跟随Application
- 2、fragmentGet(FragmentManager)
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); // build RequestManager时,将RequestManagerFragment的生命周期(current.getGlideLifecycle())传入 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) { // 通过FragmentManager寻找是否已经添加过RequestManagerFragment,避免重复添加 RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG); if (current == null) { current = pendingRequestManagerFragments.get(fm); if (current == null) { current = new RequestManagerFragment(); // 新建一个空布局的Fragment current.setParentFragmentHint(parentHint); if (isParentVisible) { current.getGlideLifecycle().onStart(); } // 在原有的界面上添加一个空布局的Fragment,用于生命周期管理 pendingRequestManagerFragments.put(fm, current); fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss(); handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget(); } } return current; } 复制代码
Glide的请求的生命周期管理是通过向页面添加一个空布局的Fragment来实现的,上面说参数很重要,这里给大家解惑。
1、当参数为Activity时,空布局的Fragment直接添加在Activity上,此时的Request的生命周期跟随Activity;
2、当参数为Fragment时,空布局的Fragment直接添加在Fragment上,此时的Request的生命周期跟随父Fragment;
3、单参数为View时,这时会通过View去找寻该View是在Fragment上还是在Activity中,分别走以上两种情况;
4.2 RequestManager
RequestManager( Glide glide, Lifecycle lifecycle, RequestManagerTreeNode treeNode, RequestTracker requestTracker, ConnectivityMonitorFactory factory, Context context) { ··· // If we are the application level request manager, we may be created on a background thread. // In that case we cannot risk synchronously pausing or resuming requests, so we hack around the // issue by delaying adding ourselves as a lifecycle listener by posting to the main thread. // This should be entirely safe. if (Util.isOnBackgroundThread()) { mainHandler.post(addSelfToLifecycle); } else { lifecycle.addListener(this); } ··· } private final Runnable addSelfToLifecycle = new Runnable() { @Override public void run() { lifecycle.addListener(RequestManager.this); } }; 复制代码1、mainHandler.post(addSelfToLifecycle);
当前线程为后台进程、或者是application等级的主线程时,通过mainHandler切回主线程,因为得刷新UI,主线程安全
2、lifecycle.addListener(this);直接监听上一步生成的布局为空的fragment的生命周期
五、优先级Priority
5.1 Priority 类
public enum Priority { IMMEDIATE, HIGH, NORMAL, LOW, } 复制代码
5.2 DecodeJob
前面分析Glide执行流程时发现,DecodeJob是真正干活的类,其实现了Runnable接口,但是它还实现了Comparable<DecodeJob<?>>接口.Engine实例化DecodeJob时,会将优先级属性传入DecodeJob中,具体使用在其实现的比较方法中:
/** * @return a negative integer, zero, or a positive integer as * this object is less than, equal to, or greater than the specified **/ @Override public int compareTo(@NonNull DecodeJob<?> other) { int result = getPriority() - other.getPriority(); if (result == 0) { result = order - other.order; } return result; } 复制代码ThreadPoolExecutor:
execute()
PriorityBlockingQueue: // 线程池的任务队列,选择高优先级的任务优先执行
offer()
siftUpComparable()
同时实现Runnable和Comparable<DecodeJob<?>>接口,线程池自动实现优先级的自动排序。六、缓存
6.1 LRU缓存算法
在Glide初始化时,会指定一个默认的缓存算法,在GlideBuilder.build()方法中
Glide build(@NonNull Context context) { ··· if (memoryCache == null) { memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize()); } ··· } 复制代码
使用了LRU(Least Recently Used)算法,核心就是最近最少使用。在算法的内部维护了一个LinkHashMap的链表(双向链表),通过put数据的时候判断是否内存已经满了,如果满了,则将最近最少使用的数据给剔除掉,从而达到内存不会爆满的状态。
更重要的一点是,当我们通过get()方法获取数据的时候,这个获取的数据会从队列中跑到队列尾来,从而很好的满足我们LruCache的算法设计思想。
LinkedHashMap.get():
public V get(Object key) { Node<K,V> e; if ((e = getNode(hash(key), key)) == null) return null; if (accessOrder) afterNodeAccess(e); return e.value; } void afterNodeAccess(Node<K,V> e) { // move node to last LinkedHashMapEntry<K,V> last; if (accessOrder && (last = tail) != e) { LinkedHashMapEntry<K,V> p = (LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after; p.after = null; if (b == null) head = a; else b.after = a; if (a != null) a.before = b; else last = b; if (last == null) head = p; else { p.before = last; last.after = p; } tail = p; ++modCount; } } 复制代码
6.2 Glide缓存逻辑
缓存逻辑关键类Engine
6.2.1 缓存Key
- Enginr.load():
public synchronized <R> LoadStatus load(...) { EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options); ... } 复制代码
model: Url、Uri、FilePath,图片来源地址
这里有个前提知识必须得知道:
DiskCacheStrategy.NONE 什么都不缓存
DiskCacheStrategy.DATA 只缓存原来的全分辨率的图像。DiskCacheStrategy.RESOURCE 只缓存最终的图像,即降低分辨率后的(或者是 转换后的)
DiskCacheStrategy.ALL 缓存所有版本的图像
DiskCacheStrategy.AUTOMATIC 让Glide根据图片资源智能地选择使用哪一种缓存策略 (默认选项)
width,height:从缓存的key中包含宽高能得出,同一张图片,假设设置的不是缓存原始图片,不同宽高将缓存不同的图片。
优点: 加载更快,少了图片宽高处理的过程。
缺点: 更费内存
6.2 缓存读取逻辑
Enginr.load():
public synchronized <R> LoadStatus load() { ... EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options); EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable); if (active != null) { cb.onResourceReady(active, DataSource.MEMORY_CACHE); if (VERBOSE_IS_LOGGABLE) { logWithTimeAndKey("Loaded resource from active resources", startTime, key); } return null; } EngineResource<?> cached = loadFromCache(key, isMemoryCacheable); if (cached != null) { cb.onResourceReady(cached, DataSource.MEMORY_CACHE); if (VERBOSE_IS_LOGGABLE) { logWithTimeAndKey("Loaded resource from cache", startTime, key); } return null; } EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache); if (current != null) { current.addCallback(cb, callbackExecutor); if (VERBOSE_IS_LOGGABLE) { logWithTimeAndKey("Added to existing load", startTime, key); } return new LoadStatus(cb, current); } // 生成网络请求逻辑 ... } @Nullable private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) { if (!isMemoryCacheable) { return null; } EngineResource<?> active = activeResources.get(key); if (active != null) { active.acquire(); // 引用计数器加1 } return active; } private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) { if (!isMemoryCacheable) { return null; } EngineResource<?> cached = getEngineResourceFromCache(key); // 将缓存数据从缓存队列中移出 if (cached != null) { cached.acquire(); // 引用计数器加1 activeResources.activate(key, cached); // 将数据放入正在活动的缓存列表中 } return cached; } 复制代码
EngineResource.acquire():
synchronized void acquire() { if (isRecycled) { throw new IllegalStateException("Cannot acquire a recycled resource"); } ++acquired; // 资源被引用的计数器 } 复制代码
6.3 缓存生成
其实就是在图片数据加载成功后,放入缓存列表。回调的最终方法在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.setResourceListener(key, this); if (resource.isCacheable()) { // 判断该资源是否需要缓存 activeResources.activate(key, resource); // 缓存 } } jobs.removeIfCurrent(key, engineJob); } 复制代码
6.4 缓存的移除
Engine.onResourceReleased():
public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) { activeResources.deactivate(cacheKey); // 从活动列表移除 if (resource.isCacheable()) { cache.put(cacheKey, resource); // 放入缓存列表 } else { resourceRecycler.recycle(resource); } } 复制代码
移除缓存的具体调用流程:
这里就得扯到前面的生命周期管理了,RequestManager中实现了LifecycleListener,接收Fragment或Activity的生命周期,这里主要分析onDestory()方法:
RequestManager.onDestory():
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); } 复制代码
RequestTracker.clearRequests() -> SingleRequest.clear() -> Engine.release() -> EngineResource.release()
void release() { // To avoid deadlock, always acquire the listener lock before our lock so that the locking // scheme is consistent (Engine -> EngineResource). Violating this order leads to deadlock // (b/123646037). synchronized (listener) { synchronized (this) { if (acquired <= 0) { throw new IllegalStateException("Cannot release a recycled or not yet acquired resource"); } if (--acquired == 0) { // 引用数值标准acquire减1 listener.onResourceReleased(key, this); } } } } 复制代码
参考了下面的优秀文章:
©爱穿衬衫的程序员
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 以太坊源码分析(36)ethdb源码分析
- [源码分析] kubelet源码分析(一)之 NewKubeletCommand
- libmodbus源码分析(3)从机(服务端)功能源码分析
- [源码分析] nfs-client-provisioner源码分析
- [源码分析] kubelet源码分析(三)之 Pod的创建
- Spring事务源码分析专题(一)JdbcTemplate使用及源码分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。