存在的问题
cached_network_image是flutter里常用的网络缓存图片插件,从网络中下载图片加载并缓存,跟Android的Glide很像,使用也比较方便。插件地址:pub.flutter-io.cn/packages/ca…
# 网络缓存图片
cached_network_image: ^3.2.0
本人项目中也引用了此插件,总的来说用着还是挺顺手的,但后面发现了一个问题:每次APP冷启动,网络图片第一次加载的时候都会有很明显的加载延时,不是立刻显示图片。
理论上来说,如果插件已经做好了本地缓存,不应该会有那么明显的延时。
针对这个问题,本人先提出了几个猜测:
1.猜测一
图片体积过大,导致加载延时。
1.1验证
将网络图片下载下来后,发现大的只有几十Kb,小的只有几Kb,基本是不需要再对图片再做压缩处理的,所以不是图片过大导致的。
2.猜测二
只做了内存缓存,没做本地缓存。
因为没做本地缓存,所以每次第一次加载的时候需要对网络图片进行内存缓存,导致加载速度慢,而后续二次加载因为是加载内存缓存所以基本无加载时间。
2.1验证
翻了一遍源码,发现插件内部使用了flutter_cache_manager插件对图片进行本地缓存,下载本地图片并进行队列管理。
并且在本地找到了下载的缓存图片。
再利用CacheObject建了sqlite数据库。
所以,cached_network_image是做了本地缓存的。
查阅资料
Platform Runner过载可能导致系统WatchDog强杀,UI和GPU Runner过载则可能导致Flutter应用的卡顿。但是GPU线程有一些必要操作是比较耗时间的,比如IO,而这些操作正是IO Runner需要处理的。
IO Runner的主要功能是从图片存储(比如磁盘)中读取压缩的图片格式,将图片数据进行处理为GPU Runner的渲染做好准备。在Texture的准备过程中,IO Runner首先要读取压缩的图片二进制数据(比如PNG,JPEG),将其解压转换成GPU能够处理的格式然后将数据上传到GPU。这些复杂操作如果跑在GPU线程的话会导致Flutter应用UI卡顿。但是只有GPU Runner能够访问GPU,所以IO Runner模块在引擎启动的时候配置了一个特殊的Context,这个Context跟GPU Runner使用的Context在同一个ShareGroup。事实上图片数据的读取和解压是可以放到一个线程池里面去做的,但是这个Context的访问只能在特定线程才能保证安全。这也是为什么需要有一个专门的Runner来处理IO任务的原因。获取诸如ui.Image这样的资源只有通过async call,当这个调用发生的时候Flutter Framework告诉IO Runner进行刚刚提到的那些图片异步操作。这样GPU Runner可以使用IO Runner准备好的图片数据而不用进行额外的操作。
用户操作,无论是Dart Code还是Native Plugins都是没有办法直接访问IO Runner。尽管Embeder可以将一些一般复杂任务调度到IO Runner,这不会直接导致Flutter应用卡顿,但是可能会导致图片和其它一些资源加载的延迟间接影响性能。
所以建议为IO Runner创建一个专用的线程。
结论
造成问题的根本原因就是cached_network_image本身没有处理冷启动时从磁盘获取资源延时的问题。
解决方案
在做flutter插件开发时,将图片加载方式转为原生Gilde加载后,问题解决。参考# Flutter原生交互及简单插件的开发