Android | Glide细枝篇

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://juejin.im/post/5f0ec887e51d45349917c614","title":""},"content":[{"type":"text","text":"《看完不忘系列》之Glide"}]},{"type":"text","text":" (树干篇)一文对"},{"type":"codeinline","content":[{"type":"text","text":"Glide"}]},{"type":"text","text":"加载图片的核心流程做了介绍,细枝篇作为补充,将对一些具体实现细节进行深入。本文篇幅略大,大家可以根据目录索引到感兴趣的章节阅读~"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"源码基于最新版本"},{"type":"codeinline","content":[{"type":"text","text":"4.11.0"}]},{"type":"text","text":",先上一张职责图预览下,一家人就要整整齐齐~"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/12/123641426bb04181171d0896a24e5d5e.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文约3200字,阅读大约10分钟。如个别大图模糊(官方会压缩),可前往"},{"type":"link","attrs":{"href":"https://imholiday.cn/","title":""},"content":[{"type":"text","text":"个人站点"}]},{"type":"text","text":"阅读"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Generated API"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通过创建一些类,继承相关接口,然后打上注解,由apt来处理这些类,从而实现接口扩展。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"全局配置"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注解"},{"type":"codeinline","content":[{"type":"text","text":"@GlideModule"}]},{"type":"text","text":"用来配置全局参数和注册定制的能力,在application里使用"},{"type":"codeinline","content":[{"type":"text","text":"AppGlideModule"}]},{"type":"text","text":",在library里使用"},{"type":"codeinline","content":[{"type":"text","text":"LibraryGlideModule"}]},{"type":"text","text":","}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@GlideModule\npublic class MyAppGlideModule extends AppGlideModule {\n @Override\n public boolean isManifestParsingEnabled() {\n return false;//新版本不需要解析manifest里的元数据(没用过老版本,不太懂,按文档返回false即可)\n }\n\n @Override\n public void applyOptions(Context context, GlideBuilder builder) {\n super.applyOptions(context, builder);\n //全局配置\n //builder.setBitmapPool(xxx);\n //builder.setDefaultRequestOptions(xxx);\n //...\n }\n\n @Override\n public void registerComponents(Context context, Glide glide, Registry registry) {\n super.registerComponents(context, glide, registry);\n //注册一些定制的能力,比如扩展新的图片来源ModelLoader\n //registry.register(xxx);\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如现在的"},{"type":"codeinline","content":[{"type":"text","text":"Glide"}]},{"type":"text","text":"的Bitmap默认配置是"},{"type":"codeinline","content":[{"type":"text","text":"ARGB_8888"}]},{"type":"text","text":",如果项目图片类型比较单一,不需要透明度通道和高色域,可以配置全局的"},{"type":"codeinline","content":[{"type":"text","text":"RGB_565"}]},{"type":"text","text":"减少一半内存。见"},{"type":"link","attrs":{"href":"https://muyangmin.github.io/glide-docs-cn/doc/configuration.html#%E9%BB%98%E8%AE%A4%E8%AF%B7%E6%B1%82%E9%80%89%E9%A1%B9","title":""},"content":[{"type":"text","text":"默认请求选项"}]},{"type":"text","text":","}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@GlideModule\npublic class MyAppGlideModule extends AppGlideModule {\n\n @Override\n public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {\n super.applyOptions(context, builder);\n builder.setDefaultRequestOptions(new RequestOptions()\n .format(DecodeFormat.PREFER_RGB_565));\n //注:由于png需要透明度通道,这类图依旧会采用8888\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"或者可以根据设备评分来衡量,普通机型配置"},{"type":"codeinline","content":[{"type":"text","text":"RGB_565"}]},{"type":"text","text":"(在需要透明度通道的场景局部使用"},{"type":"codeinline","content":[{"type":"text","text":"ARGB_8888"}]},{"type":"text","text":"),高端机型则可以直接配置"},{"type":"codeinline","content":[{"type":"text","text":"ARGB_8888"}]},{"type":"text","text":",纵享奢华体验。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a9/a9847b6e358b20d437b3f2ca8a583456.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"行为打包"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注解"},{"type":"codeinline","content":[{"type":"text","text":"@GlideExtension"}]},{"type":"text","text":"可以将一些通用行为打包起来,扩展一个接口方便业务层调用。比如电商App很多页面都有商品列表,这些商品图片的宽高如果是固定的,就可以包装起来,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@GlideExtension\npublic class MyAppExtension {\n private static final int GOODS_W = 300; //商品图宽度\n private static final int GOODS_H = 400; //商品图高度\n\n private MyAppExtension() { //私有化构造方法\n }\n\n @GlideOption\n public static BaseRequestOptions> goods(BaseRequestOptions> options) {\n return options\n .fitCenter()\n .override(GOODS_W, GOODS_H) //宽高\n .placeholder(R.mipmap.ic_launcher) //商品占位图\n .error(R.mipmap.ic_launcher); //商品图加载失败时\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"rebuild一下项目,生成类"},{"type":"codeinline","content":[{"type":"text","text":"build/generated/ap_generated_sources/debug/out/com/holiday/srccodestudy/glide/GlideOptions.java"}]},{"type":"text","text":",里面会多出一个方法,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"class GlideOptions extends RequestOptions implements Cloneable {\n public GlideOptions goods() {\n return (GlideOptions) MyAppExtension.goods(this);\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这时,就可以用goods来直接使用这一组打包好的行为了,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//要用GlideApp\nGlideApp.with(this).load(url).goods().into(img);"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Generated API"}]},{"type":"text","text":"比较适合短周期/小型项目,中大型项目往往不会直接裸使用"},{"type":"codeinline","content":[{"type":"text","text":"Glide"}]},{"type":"text","text":",会包一个中间层来进行隔离(禁止业务层用到"},{"type":"codeinline","content":[{"type":"text","text":"Glide"}]},{"type":"text","text":"的任何类),以便随时可以升级替换,这个中间层就可以根据需要来自行扩展。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"空Fragment取消请求"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Glide.with(context),当context是Activity时,每个页面都会被添加一个空fragment,由空fragment持有"},{"type":"codeinline","content":[{"type":"text","text":"页面级别RequestManager"}]},{"type":"text","text":"来管理请求,那退出页面时是如何取消请求的呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"with通过"},{"type":"codeinline","content":[{"type":"text","text":"RequestManagerRetriever"}]},{"type":"text","text":"获取"},{"type":"codeinline","content":[{"type":"text","text":"SupportRequestManagerFragment"}]},{"type":"text","text":","}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//SupportRequestManagerFragment.java\n//创建SupportRequestManagerFragment\npublic SupportRequestManagerFragment() {\n //创建Lifecycle\n this(new ActivityFragmentLifecycle());\n}\n\n//RequestManager.java\n//创建RequestManager,传入Lifecycle\nRequestManager(\n Glide glide,\n Lifecycle lifecycle,\n //...\n Context context) {\n //lifecycle添加RequestManager为观察者\n lifecycle.addListener(this);\n}\n\n//ActivityFragmentLifecycle.java\npublic void addListener(LifecycleListener listener) {\n //记录观察者们\n lifecycleListeners.add(listener);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"退出页面时,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//SupportRequestManagerFragment.java\npublic void onDestroy() {\n lifecycle.onDestroy();\n}\n\n//ActivityFragmentLifecycle.java\nvoid onDestroy() {\n for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {\n lifecycleListener.onDestroy();\n }\n}\n\n//RequestManager.java\npublic synchronized void onDestroy() {\n //各种取消、注销操作\n targetTracker.onDestroy();\n for (Target> target : targetTracker.getAll()) {\n clear(target);\n }\n targetTracker.clear();\n requestTracker.clearRequests();\n lifecycle.removeListener(this);\n lifecycle.removeListener(connectivityMonitor);\n mainHandler.removeCallbacks(addSelfToLifecycle);\n glide.unregisterRequestManager(this);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代码看起来有点绕,大致如下图,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/70/704b49a4432f5fd1f001ffdf7a72c658.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Cache缓存"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"内存"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"内存缓存有两级,一是处于活跃状态,正被view使用着的缓存,称"},{"type":"codeinline","content":[{"type":"text","text":"活跃资源"}]},{"type":"text","text":";二是没被view使用的,就叫他"},{"type":"codeinline","content":[{"type":"text","text":"非活跃资源"}]},{"type":"text","text":"吧,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"读取内存:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//Engine.java\npublic LoadStatus load(...){\n //获取内存缓存\n memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);\n}\n\nprivate EngineResource> loadFromMemory(\n EngineKey key, boolean isMemoryCacheable, long startTime) {\n\t//活跃资源,从ActiveResources的Map中获取\n //Map activeEngineResources,值是弱引用,会手动计数\n EngineResource> active = loadFromActiveResources(key);\n if (active != null) {\n return active;\n }\n\t//非活跃资源,从LruResourceCache获取,也有手动计数\n //返回后,说明这个缓存被view给用上了,非活跃资源则变成活跃\n EngineResource> cached = loadFromCache(key);\n if (cached != null) {\n return cached;\n }\n //内存没有缓存,load就会去请求\n return null;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"写入内存:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//Engine.java\npublic synchronized void onEngineJobComplete(\n EngineJob> engineJob, Key key, EngineResource> resource) {\n if (resource != null && resource.isMemoryCacheable()) {\n //简单理解,就是图片加载完成,这时写入活跃资源的\n activeResources.activate(key, resource);\n }\n}\n\npublic void onResourceReleased(Key cacheKey, EngineResource> resource) {\n //活跃资源已经没有被引用了,就移出\n activeResources.deactivate(cacheKey);\n if (resource.isMemoryCacheable()) {\n //转入非活跃资源\n cache.put(cacheKey, resource);\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如下图:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/64/64cac3a019594c23e23548956c29329e.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"磁盘"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看看缓存目录"},{"type":"codeinline","content":[{"type":"text","text":"/data/data/com.holiday.srccodestudy/cache/image_manager_disk_cache/"}]},{"type":"text","text":","}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3e/3e9d1874af1976767b4299bd97d9dfe3.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先看日志文件"},{"type":"codeinline","content":[{"type":"text","text":"journal"}]},{"type":"text","text":","}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"libcore.io.DiskLruCache //头部名字\n1 //磁盘缓存版本\n1 //App版本\n1 //每个entry(日志条目)存放的文件数,默认为1,即一个entry对应一个图片文件,比如下面就有4个entry,即4张图片\n\nDIRTY 64d4b00d8ce8b0942d53b3048d5cf6aaa7173acd321e17420891bbc35b98629f\nCLEAN 64d4b00d8ce8b0942d53b3048d5cf6aaa7173acd321e17420891bbc35b98629f 5246\nDIRTY 2c23e32bd9b208092b3cbee8db6f1aff5bc11cb0d4ebd092604ee53099beff37\nCLEAN 2c23e32bd9b208092b3cbee8db6f1aff5bc11cb0d4ebd092604ee53099beff37 404730\nREAD 64d4b00d8ce8b0942d53b3048d5cf6aaa7173acd321e17420891bbc35b98629f\nREAD 2c23e32bd9b208092b3cbee8db6f1aff5bc11cb0d4ebd092604ee53099beff37\nDIRTY b566e62aa0e2fb8cb219ad3aa7a0ade9a96521526501ccd775d70aa4f6489272\nCLEAN b566e62aa0e2fb8cb219ad3aa7a0ade9a96521526501ccd775d70aa4f6489272 9878\nREAD 2c23e32bd9b208092b3cbee8db6f1aff5bc11cb0d4ebd092604ee53099beff37\nREAD b566e62aa0e2fb8cb219ad3aa7a0ade9a96521526501ccd775d70aa4f6489272\nDIRTY 55f4af9c1020e3272ce8063c351aff3518f3a1c9508f38345eab27686e263a4c\nCLEAN 55f4af9c1020e3272ce8063c351aff3518f3a1c9508f38345eab27686e263a4c 69284\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下半部分是操作记录,行开头指操作行为,"},{"type":"codeinline","content":[{"type":"text","text":"DIRTY"}]},{"type":"text","text":"表示在编辑(处于脏数据状态,别读),"},{"type":"codeinline","content":[{"type":"text","text":"CLEAN"}]},{"type":"text","text":"(干净状态)表示写好了,可以读了,"},{"type":"codeinline","content":[{"type":"text","text":"READ"}]},{"type":"text","text":"表示被读入了,"},{"type":"codeinline","content":[{"type":"text","text":"REMOVE"}]},{"type":"text","text":"则表示被删除,中间很长的一串字符就是缓存键或文件名字,最后的数字是文件大小,如404730 B=395.2 KB,只有处于"},{"type":"codeinline","content":[{"type":"text","text":"CLEAN"}]},{"type":"text","text":"状态才会写大小。那么图中的文件名是什么意思,为啥key的后面还有"},{"type":"codeinline","content":[{"type":"text","text":".0"}]},{"type":"text","text":"后缀?因为一个"},{"type":"codeinline","content":[{"type":"text","text":"entry"}]},{"type":"text","text":"(日志条目)可以对应多个图片,"},{"type":"codeinline","content":[{"type":"text","text":".0"}]},{"type":"text","text":"代表"},{"type":"codeinline","content":[{"type":"text","text":"entry"}]},{"type":"text","text":"的第一张图片,如果有配置1对多,那就会有"},{"type":"codeinline","content":[{"type":"text","text":".1"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":".2"}]},{"type":"text","text":"这样的后缀。选一个"},{"type":"codeinline","content":[{"type":"text","text":".0"}]},{"type":"text","text":"文件点击右键,"},{"type":"codeinline","content":[{"type":"text","text":"Save as"}]},{"type":"text","text":"保存到电脑,改个jpg后缀,就能看图了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"来到"},{"type":"codeinline","content":[{"type":"text","text":"DiskLruCache"}]},{"type":"text","text":"类(看名字知道还是"},{"type":"codeinline","content":[{"type":"text","text":"最近最少使用算法"}]},{"type":"text","text":"),"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//DiskLruCache.java\n\n//有序Map,实现最近最少使用算法\nprivate final LinkedHashMap lruEntries =\n new LinkedHashMap(0, 0.75f, true);\n\n//读取磁盘缓存\npublic synchronized Value get(String key) throws IOException {\n //根据key找到entry\n Entry entry = lruEntries.get(key);\n if (entry == null) {\n return null;\n }\n //还不可以读,返回null\n if (!entry.readable) {\n return null;\n }\n //追加一行日志:READ\n journalWriter.append(READ);\n journalWriter.append(' ');\n journalWriter.append(key);\n journalWriter.append('\\n');\n //Value就是用来封装的实体\n return new Value(key, entry.sequenceNumber, entry.cleanFiles, entry.lengths);\n}\n\n//写入磁盘缓存(这里只是存进内存的Map,真正的写入在DiskLruCacheWrapper)\nprivate synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException {\n Entry entry = lruEntries.get(key);\n if (entry == null) {\n entry = new Entry(key);\n //存进LinkedHashMap\n lruEntries.put(key, entry);\n }\n Editor editor = new Editor(entry);\n entry.currentEditor = editor;\n //追加一行日志:DIRTY\n journalWriter.append(DIRTY);\n return editor;\n}\n\n//删除磁盘缓存\npublic synchronized boolean remove(String key) throws IOException {\n Entry entry = lruEntries.get(key);\n if (entry == null || entry.currentEditor != null) {\n return false;\n }\n //删除entry对应的图片文件\n for (int i = 0; i < valueCount; i++) {\n File file = entry.getCleanFile(i);\n size -= entry.lengths[i];\n entry.lengths[i] = 0;\n }\n //追加一行日志:REMOVE\n journalWriter.append(REMOVE);\n //从内存Map中移除\n lruEntries.remove(key);\n return true;\n}\n\n//当日志操作数和entry数都达到2000,就清空日志重写\nprivate boolean journalRebuildRequired() {\n final int redundantOpCompactThreshold = 2000;\n return redundantOpCount >= redundantOpCompactThreshold //\n && redundantOpCount >= lruEntries.size();\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那么读取和写入时机在哪呢?我们反向追踪一波"},{"type":"codeinline","content":[{"type":"text","text":"get"}]},{"type":"text","text":"方法,从"},{"type":"codeinline","content":[{"type":"text","text":"DiskLruCache"}]},{"type":"text","text":"到"},{"type":"codeinline","content":[{"type":"text","text":"DiskLruCacheWrapper"}]},{"type":"text","text":"的"},{"type":"codeinline","content":[{"type":"text","text":"get"}]},{"type":"text","text":",然后再追,发现有两个类调了"},{"type":"codeinline","content":[{"type":"text","text":"get"}]},{"type":"text","text":",分别是"},{"type":"codeinline","content":[{"type":"text","text":"DataCacheGenerator"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"ResourceCacheGenerator"}]},{"type":"text","text":",前者是原始图片的缓存,后者是经过"},{"type":"codeinline","content":[{"type":"text","text":"downsampled"}]},{"type":"text","text":"向下采样或"},{"type":"codeinline","content":[{"type":"text","text":"transformed"}]},{"type":"text","text":"转换过的图片,在"},{"type":"link","attrs":{"href":"https://muyangmin.github.io/glide-docs-cn/doc/caching.html#磁盘缓存策略disk-cache-strategy","title":""},"content":[{"type":"text","text":"磁盘缓存策略"}]},{"type":"text","text":"中提到:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目前支持的策略允许你阻止加载过程使用或写入磁盘缓存,选择性地仅缓存无修改的原生数据,或仅缓存变换过的缩略图,或是兼而有之。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"默认情况下,网络图片缓存的是原始数据,那我们继续跟"},{"type":"codeinline","content":[{"type":"text","text":"DataCacheGenerator"}]},{"type":"text","text":","}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//DataCacheGenerator.java\npublic boolean startNext() {\n while (modelLoaders == null || !hasNextModelLoader()) {\n sourceIdIndex++;\n if (sourceIdIndex >= cacheKeys.size()) {\n return false;\n }\n Key sourceId = cacheKeys.get(sourceIdIndex);\n Key originalKey = new DataCacheKey(sourceId, helper.getSignature());\n //获取磁盘缓存的图片文件\n cacheFile = helper.getDiskCache().get(originalKey);\n if (cacheFile != null) {\n this.sourceKey = sourceId;\n //获取能够处理File类型的modelLoaders集合,\n //modelLoader就是图片加载类型,比如网络url、本地Uri、文件File都有各自的loader\n modelLoaders = helper.getModelLoaders(cacheFile);\n modelLoaderIndex = 0;\n }\n }\n loadData = null;\n boolean started = false;\n while (!started && hasNextModelLoader()) {\n //成功找到ByteBufferFileLoader,可以处理File\n ModelLoader modelLoader = modelLoaders.get(modelLoaderIndex++);\n //传入磁盘缓存的图片文件cacheFile\n loadData =\n modelLoader.buildLoadData(\n cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions());\n if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {\n started = true;\n loadData.fetcher.loadData(helper.getPriority(), this);\n }\n }\n return started;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"继续跟"},{"type":"codeinline","content":[{"type":"text","text":"modelLoader.buildLoadData"}]},{"type":"text","text":",后边就是把图片文件cacheFile封装成"},{"type":"codeinline","content":[{"type":"text","text":"ByteBufferFetcher"}]},{"type":"text","text":",然后调用上边的"},{"type":"codeinline","content":[{"type":"text","text":"loadData.fetcher.loadData"}]},{"type":"text","text":"进行回调,就不继续跟了,"},{"type":"codeinline","content":[{"type":"text","text":"startNext"}]},{"type":"text","text":"方法在"},{"type":"codeinline","content":[{"type":"text","text":"DecodeJob"}]},{"type":"text","text":"里会被调用,树干篇中可知他就是图片加载过程用到的一个Runnable,好了,下面看看缓存写入时机,反向追踪"},{"type":"codeinline","content":[{"type":"text","text":"edit"}]},{"type":"text","text":"方法,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//DiskLruCacheWrapper.java\npublic void put(Key key, Writer writer) {\n String safeKey = safeKeyGenerator.getSafeKey(key);\n writeLocker.acquire(safeKey);\n try {\n try {\n DiskLruCache diskCache = getDiskCache();\n Value current = diskCache.get(safeKey);\n //已经有缓存,结束\n if (current != null) {\n return;\n }\n\t\t\t//获取Editor\n DiskLruCache.Editor editor = diskCache.edit(safeKey);\n try {\n File file = editor.getFile(0);\n if (writer.write(file)) {//编码写入文件\n //提交“事务”,追加一行日志:CLEAN,表示该条目对应的缓存文件已经干净可以使用了\n editor.commit();\n }\n } finally {\n editor.abortUnlessCommitted();\n }\n } catch (IOException e) {\n }\n } finally {\n writeLocker.release(safeKey);\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同样,"},{"type":"codeinline","content":[{"type":"text","text":"put"}]},{"type":"text","text":"方法也会在"},{"type":"codeinline","content":[{"type":"text","text":"DecodeJob"}]},{"type":"text","text":"里被调用,就不往上跟了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2c/2c2fb30fb1bc2d171672bbe8c5880173.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"合并内存缓存和磁盘缓存,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/bd/bd182445a818a06d627c900b4fdb5900.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"BitmapPool令人诟病"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Glide"}]},{"type":"text","text":"有将Bitmap进行池化,默认是"},{"type":"codeinline","content":[{"type":"text","text":"LruBitmapPool"}]},{"type":"text","text":",他会决定怎么复用Bitmap、何时回收Bitmap、池子上限时清理,也就是说,他全盘接管了Bitmap的处理,如果项目中有"},{"type":"codeinline","content":[{"type":"text","text":"在回调方法外持有Bitmap"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"手动回收Bitmap"}]},{"type":"text","text":"的场景,会发生意料外的crash,详见"},{"type":"link","attrs":{"href":"https://muyangmin.github.io/glide-docs-cn/doc/resourcereuse.html#资源重用错误的征兆","title":""},"content":[{"type":"text","text":"资源重用错误的征兆"}]},{"type":"text","text":"。即,我们要有这样的意识,既然使用了"},{"type":"codeinline","content":[{"type":"text","text":"Glide"}]},{"type":"text","text":",就不要再关心Bitmap的事情了,全盘交由"},{"type":"codeinline","content":[{"type":"text","text":"BitmapPool"}]},{"type":"text","text":"管理即可。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"发散:所谓池化,就是设计模式中的享元模式,即维护一个有限个数的对象池来实现对象复用,从而避免频繁的创建销毁对象。比如Handler消息机制中的"},{"type":"codeinline","content":[{"type":"text","text":"Message.obtain"}]},{"type":"text","text":",就是从消息池(链表)里取出对象来复用,池子的消息总数被限制在MAX"},{"type":"text","marks":[{"type":"italic"}],"text":"POOL"},{"type":"text","text":"SIZE=50。Android内的很多实现都是基于Handler(消息驱动)的,池化能减少很大部分的创建销毁。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Decoder解码"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"链路有点长,直接看调用栈,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c3/c31785bffa628b504e0e2190fb60eadb.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可见最终走的是native层的"},{"type":"codeinline","content":[{"type":"text","text":"nativeDecodeStream"}]},{"type":"text","text":",哈迪就不跟了,对inputstream转成bitmap感兴趣的读者自行研究啦~"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0b/0ba09e821484cd12c9c21b9af1621b4e.jpeg","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"总结"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Glide"}]},{"type":"text","text":"有如下优势:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"空Fragment感知页面生命周期,避免无效请求"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"高度可配置,详见"},{"type":"link","attrs":{"href":"https://muyangmin.github.io/glide-docs-cn/doc/configuration.html","title":""},"content":[{"type":"text","text":"配置"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"三级缓存(网络层缓存如okhttp就不考虑了):内存活跃资源"},{"type":"codeinline","content":[{"type":"text","text":"ActiveResources"}]},{"type":"text","text":"、内存非活跃资源"},{"type":"codeinline","content":[{"type":"text","text":"LruResourceCache"}]},{"type":"text","text":"、磁盘缓存"},{"type":"codeinline","content":[{"type":"text","text":"DiskLruCache"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"可定制,引入apt处理注解,打包行为,扩展接口。(哈迪没怎么用,感觉有点鸡肋,可能以后会真香)"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"可扩展,可以替换网络层、定制自己的图片来源"},{"type":"codeinline","content":[{"type":"text","text":"ModelLoader"}]},{"type":"text","text":",详见"},{"type":"link","attrs":{"href":"https://muyangmin.github.io/glide-docs-cn/tut/custom-modelloader.html","title":""},"content":[{"type":"text","text":"编写定制的ModelLoader"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"text","text":"无侵入,into可以传入最简单的ImageView"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":7,"align":null,"origin":null},"content":[{"type":"text","text":"优秀的设计模式运用、应用层优雅的链式调用"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"至于缺点吧,暂时还没想到。本文只列出了哈迪觉得比较精彩的细节,可能还有遗漏的一些点,大家有补充的可以留下评论,后续我会更新进本文。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"参考资料"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://muyangmin.github.io/glide-docs-cn/","title":""},"content":[{"type":"text","text":"官方文档"}]},{"type":"text","text":" & "},{"type":"link","attrs":{"href":"https://github.com/bumptech/glide","title":""},"content":[{"type":"text","text":"GitHub"}]}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/18/18280df97439e1bea7bded6864c8a8a8.jpeg","alt":null,"title":"","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章