作者:闲鱼技能——新宿
布景:
去年,闲鱼新一代图片库 PowerImage 在经过一系列灰度、问题修正、代码调优后,已全量稳定应用于闲鱼。相关于上一代 IFImage,PowerIma数组的定义ge 经过进一步的演进,适应了更多的事务场景与最新的 flutter 特性,处理了一系列痛点:比方,因为彻底抛弃了原生的swift语言 ImageCache,在与原生图片混用的场景下,会让一些低频的图片反而占用了缓存;比方,咱们在模拟器上无法展现图片;比方咱们在相册中,需求在图片库之外再搭建图片通道。swiftly
简介:
PowerImage 是一个充分利用 native 原生图片库才能、高扩展性的flutflutteredter图片库。咱们奇妙架构是什么意思地将外接纹路与 ffi 计划组合,以更靠近fluttershy原生的规划,处理了一系列事务痛点。
才能特点:
- • 支撑加载 ui.Image 才能。在根据外接纹路的计划中,运用方无法拿到真实的 ui架构图怎么画.Image 去运用,这导致图片库在这种特殊的运用场景下力不从心。
- • 支撑图片预加载才能。正如原生precacheImage一样。这在某些对图片展现速度要求较高的场景下十数组分有用。
- • 新增纹路缓存,与原生图片库缓存打通!一致图片缓存缓存,防止原生图片混用带来的内存问题。
- • 支撑模拟器。在 fluSwifttter-1.23.0-18.1.p数组c语言re之前的版别,模拟器无法展现 Texture Widget。
- •缓存视频合并app 完善自定义图片类型通道。处理事务自定义图片获取缓存的视频在哪诉求。
- • 完善的反常捕获与收集。
- • 支撑动图。(来fluttering自淘特的PR)
Flutter 原生计划:
在介绍新计划开端fluttering之前,先简单回想一下 flutter 原生图片计划。
原生 Image Widget 先经过 ImageProvider 得到 ImageStream,经过监听它的数组词状况,进行各种状况的展现。比方frameBuilder
、loadingBuilder
,最终在图片加载flutter面试题成功后,会 rebuild
出 RawImage
,RawImage
会经过 RenderImage
来制作,整个制作的中心是 ImageInfo
中的 ui.Image
。
- • Image:担flutter面试题任图片加载的各个状况的展现,如加载中、失败、加载成功展现图片等。
- • ImageProvider:担任 ImageStream 的获取,比方体系内置的 NetworkImage、AssetImage 等。
- • ImageSt缓存视频在手机哪里找ream:图片资源加载的目标数组指针。
在整理 flutter 原生图片计划之后,咱们发现是不是有机会在某个环节将 flutter 图片和 native 以原生的方法打通?flutter面试题
新一代计划:
咱们奇妙地将 FFi 计划与外接纹路计划组合,处理了一系列事务痛点。
FFI:
正如开头说的那些问题,Tflutter框架exture 计划有些做不到的工作,这需求其他计划来互补,这其中中心需求的便是 ui.Image
。咱们把 native 内存地址、长度等信息传递给 flutter 侧,用于生成 ui.Image
。
首先 native 侧先获取必要的参数(以 iOS 为例):
_rowBytes = CGImageGetBytesPerRow(cgImage); CGDataProviderRef dataProvider = CGImageGetDataProvider(cgImage); CFDataRef rawDataRef = CGDataProviderCopyData(dataProvider); _handle = (long)CFDataGetBytePtr(rawDataRef); NSData *data = CFBridgingRelease(rawDataRef); self.data = data; _length = data.length;
dart 侧拿到后
@override FutureOr<ImageInfo> createImageInfo(Map map) { Completer<ImageInfo> completer = Completer<ImageInfo>(); int handle = map['handle']; int length = map['length']; int width = map['width']; int height = map['height']; int rowBytes = map['rowBytes']; ui.PixelFormat pixelFormat = ui.PixelFormat.values[map['flutterPixelFormat'] ?? 0]; Pointer<Uint8> pointer = Pointer<Uint8>.fromAddress(handle); Uint8List pixels = pointer.asTypedList(length); ui.decodeImageFromPixels(pixels, width, height, pixelFormat, (ui.Image image) { ImageInfo imageInfo = ImageInfo(image: image); completer.complete(imageInfo); //开释 native 内存 PowerImageLoader.instance.releaseImageRequest(options); }, rowBytes: rowBytes); return completer.future; }
咱们能够经过 ffi 拿到 naflutter面试题tive 内存,然后生成 ui.Image。这里有个问swift是什么组织缩写题,虽然经过 ffi 能直接获取 native 内存,架构师和程序员的区别但是架构因为 decodeImageFromPixels
会有内存复制,在复制解码后的图片数据时,内存峰值会swift代码愈加严峻。
这里有两个优化方向:
- 1. 解码前的图片数据给flutter是什么意思 flutter,由 flutter 供给的解码器解码,然后削减内存复制峰值。
- 2. 与 flutter 官方讨论,测验从内部削减这次内存复制。
FFI 这种方法适架构师和程序员的区别合轻度运用、特殊场景运用,支撑这种方法能够处理无法获取 ui.Image 的问题,也能缓存文件夹名称够在模拟器上展现图片(flutterswift系统 &架构图lt;= 1.23.0-18.1.p缓存视频合并re),而且图片缓存将彻底交给 ImageCache 办理。缓存清理
Texture:
Texture 计划与原生结合有缓存的视频在哪一些难度,这里涉及到没有 ui.Image
只要 textu数组和链表的区别reId。这里有几个问题需求处理:
问题一:Image Widget 需求 ui.Image
去 build RawImage
然后制作,这在本文前面的Flutter 原生计划介绍中也提到了。 问题二:ImageCache 依赖 ImageInfo 中 ui.Image
的架构师证书宽高进行 cache 巨细核算以及缓存前的校验。 问题三:native 侧 texture 生命周期flutter框架办理swift代码
都有处理计划:
问题一:经过自定义 Image 处理,透出fluttershy imageBuilder 来让外部自定义图片 widget 问题二:为数组词 Texture架构师证书 自定义 ui.image
,如下:
import 'dart:typed_data';import 'dart:ui' as ui show Image;import 'dart:ui';class TextureImage implements ui.Image { int _width; int _height; int textureId; TextureImage(this.textureId, int width, int height) : _width = width, _height = height; @override void dispose() { // TODO: implement dispose } @override int get height => _height; @override Future<ByteData> toByteData( {ImageByteFormat format = ImageByteFormat.rawRgba}) { // TODO: implement toByteData throw UnimplementedError(); } @override int get width => _width;}
这样的话,TextureImage 实际上便是个壳,仅仅用来核算 cache 巨缓存英文细。 实际上,ImageCache 核算巨细,彻底没必要直接接触到 ui.架构图怎么画Image,能够直接找 ImageInfo 取,这样的话就没有这个问题了swifter。这个问题数组指针能够详细看 @皓黯 的 ISSUE 与 PR。
问题三:关于 native 侧感知 flutter image 开释机遇的问题
修正的 ImageCache 开释如下(部分代码):
typedef void HasRemovedCallback(dynamic key, dynamic value);class RemoveAwareMap<K, V> implements Map<K, V> { HasRemovedCallback hasRemovedCallback; ...}//------ final RemoveAwareMap<Object, _PendingImage> _pendingImages = RemoveAwareMap<Object, _PendingImage>();//------void hasImageRemovedCallback(dynamic key, dynamic value) { if (key is ImageProviderExt) { waitingToBeCheckedKeys.add(key); } if (isScheduledImageStatusCheck) return; isScheduledImageStatusCheck = true; //We should do check in MicroTask to avoid if image is remove and add right away scheduleMicrotask(() { waitingToBeCheckedKeys.forEach((key) { if (!_pendingImages.containsKey(key) && !_cache.containsKey(key) && !_liveImages.containsKey(key)) { if (key is ImageProviderExt) { key.dispose(); } } }); waitingToBeCheckedKeys.clear(); isScheduledImageStatusCheck = false; }); }
整体架构:
咱们将两种处理计划十分高雅地结合在了一起:
咱们抽象出了 PowerImageProvider ,关于 external(ffi)、texture,分别生产自己的 ImageInfo 即可。它将经过对 PowerImageLoade数组去重方法r 的flutter菜鸟教程调用,供给一致的加载与开释才能。
蓝色实线的 ImageExt 即为自定义的 Image Widget,为 texture 方法透出了 imageBuilder。
蓝色虚线 ImageCachswift语言eExt 即为 ImageCache 的扩展,仅在 flutter < 2.2.0 版别才需求,它将供给 ImageCache 开释机遇的回调。
这次,咱们也规划了超强的扩展才能。除缓存了支撑网络图、本地图、flutter 资源、native 资源外,咱们供给了自定义图片类型的通道,flutter 能够架构图怎么画传递任何自定义的参数组合给 native,只需 native 注册缓存视频怎样转入相册对应类型 loader,比方「相册」这种swifter场景,运用方能够自定义 imageType 为 album ,nativ数组指针e 运用自己的逻辑进行加载图片。有了这个自定义通道,甚至图片滤镜都能够运用 PowerImage 进行展现改写。
除了图片类型的扩展,渲染类型也可进行自定swift代码义。比方在上面 ffi 中说的,为了下降内存复制带来的峰值问题,运用方能够在 flu缓存视频合并apptter 侧进行解数组排序码,当然这需求 native 图片库供给解码前的数据。
数据:
FFI vs Texture:
机型:iPhone 11 Pro;图片:300 张网络图;行为:在listView中手动翻滚到底部再翻滚到顶部;native Cache:20 maxMemoryCount; flutter Cache:30MBflutter version 2.5.3; release 形式下
这里有两个现象:
FFI: 186MB动摇Texture: 194MB动摇
在 2.5.3 版别中,Texture 计划与 FFI,在内存水位上差异不flutter有必要学吗大数组指针,内数组的定义存动摇上缓存清理面与 flutter 1.22 结论相反。
图中棋格图,为架构师和程序员的区别打开 che架构ckerboardRasterCacheImages
后所展现,能够看出,ffi计划会缓存整个cell,而texture计划,只要cell中的文字被缓存,RasterCache 会使得 ffi 在流畅度方面会有必定优势。
翻滚流畅性分析:
设备: Android OnePlus 8t,CPU和GPU进行了锁频。case: GridView每行4张图片,300张图片,从上往下,再从下往上,滑动起伏从500,1000,1500,2000,2500,5轮滑动。重复20次。方法: for i in {1..20}; do flutter drive --target=test_driver/app.dart --profile; done 跑数据,获取TimeLine数据并分析。
结数组和链表的区别论:
- • UI thread 耗时 texture 方法最好,PowerImage 略好于 IFImage,FFI方法动摇比较大。swifter
- • Raster thread 耗时 PowerImage 好于数组 IFImage。Origin 原生方法好是因为对图片 resize了,其他方法加载的是原图。
更精简的代码:
dart 侧代码有较大起伏的削减,这归功数组去重方法于技能计划贴合swiftly fluswift语言tter 原生规划,咱们与原生图片共用较多代码。
FFI 计划补全了外接纹路fluttered的flutter面试题缺乏,遵从原生 Image 的规划规范,不仅让咱们享受到 ImageCache 带来的一致办理,也带来了更精简的代码。
单测:
为了保证中心代码的稳定性,咱们有着较为完善的单测,行覆盖率接近95%。flutter开发的app有哪些
关于开源:
咱们等待经过社架构是什么意思区的力量让 PowerImage 愈加完swifter善与强壮,也期望 PowerImage 能为咱们在工程研制中带来收益。
Issues:数组
关于 issue,咱们期望咱们在运用 PowerImage 遇缓存视频变成本地视频到问题与诉求时,积极沟通,提出 issue 时尽可能供给详细的信息,以削减沟通本钱。架构图怎么制作在提出 issue 前,请保证已阅读 readme。
关于 bug 的 issue,咱们自定义swift是什么意思啊了模板(Bug report),能够方便地填一些必要的信息。其他类型则能够选择 Open a blank issue
。
咱们每周会花部swift代码分时间一致处理 issues,也等待咱们的讨论与 PR。
PR:
为了保持 PowerImage 中心功能的稳定性,咱们有着完善的单测,行覆盖率达到了 95%(power_缓存视频在手机哪里找image库)。数组词
在提交PR时,请保证所提交的代码被单测覆盖到,而且涉及到的单测代码请一起提交。
得益于 Github 的 Actions 才能,咱们在主分支 push 代码、对主分支进行 PR 操架构师作时,都会触发 flutter test
使命,只要单测经过才可合入。
未来:
开源是 PowerImage 的开端,而不是完毕,PowerImage 可做的工作还有许多,风趣而丰厚。比方第一个 issue 中描述的flutter菜鸟教程 loa架构师和程序员的区别dingBuilder
怎么完成?比方 ffi 计划怎么支撑动图?再比方Kotlin和swift是什么组织缩写Swift
PowerImage 未来将继续演进,在当时 texture 计划与 ffi 计划共存的情况下,伴随着 flutter 自身的迭代,咱们将更倾向于向 ffi 发展,正如在上文的对比中, ffi 计划能够天然享受 raster cache 所带来的流畅度的优势。
PowerImage 也会继续追随 flutteflutter翻译r 的脚步,以一直贴合原生的规划理念,不断进步,咱们期望更多的同学加入进来,共同生长。
其他四个Flutter开源项目:闲鱼技能公众号-闲鱼开源
PowerImage相关链接:
GitHub:(✅star)
github.com/alibaba/pow…
Flutter pub:(✅like)
pub.dev/packages/po…