
【闲谈鸿蒙三方库】图片库@ohos/imageknife 原创
在鸿蒙应用开发场景中,使用系统原生的 image 存在诸多局限性:一方面,无法自定义实现网络下载和图片解码;另一方面,无法实现自定义缓存与图片预加载功能,并且在图片加载过程中还会出现白块现象,严重影响用户体验。
在安卓开发领域,通常借助 Glide 来解决上述类似问题;而在 iOS 开发中,SDWebImage 则是常用的解决方案。而在鸿蒙中,大家可以使用ImageKnife来解决上述问题。
ImageKnife是一个OpenHarmony/HarmonyOS的网络图片加载库。它主要对标安卓glide/fresco,提供内存和文件缓存功能,提升网络图片加载的流畅性。
其主要特性包括:
- 支持自定义内存缓存策略,支持设置内存缓存的大小(默认LRU策略)。
- 支持磁盘二级缓存,支持设置文件缓存的大小和路径。
- 支持自定义实现图片获取/网络下载。
- 支持占位图和错误图。
- 支持传入http请求头,证书,超时时间等。
- 支持监听网络下载回调进度。
- 支持图片加载数据回调,获取图片加载过程数据。
- 支持自定义缓存key。
- 支持自定义http网络请求头。
- 支持图形变换,如模糊,高亮等。
- 支持对已销毁和复用的图片,不再发起请求。
- 支持预加载图片。
开源代码地址:https://gitee.com/openharmony-tpc/ImageKnife
快速上手方法
1.下载安装
当前稳定版本3.2.0
2.权限配置
因核心功能涉及网络下载,需要添加ohos.permission.INTERNET的权限。
3.初始化
如果需要用文件缓存,需要提前初始化文件缓存。
更多功能展示
示例1 简单加载网络图片
由于是鸿蒙原生arkTs库,该库采用自定义组件的方式进行使用,即在页面的build方法中,声明式使用ImageKnifeComponent。其参数imageKnifeOption里的必备属性包含loadSrc,即主图的url地址。此外,可选参数placeholderSrc和errorholderSrc分别表示占位图和错误图,其类型可以是url地址,也可以是Resource本地图片资源。
示例2 设置边框,圆角
通过imageKnifeOption的border属性,设置图片的边框和圆角。
示例3 设置图片填充效果
通过imageKnifeOption的objectFit属性,设置图片的填充效果。其效果与系统ImageFit的完全一致,主要效果包括:
名称 | 描述 |
---|---|
Contain(缺省) | 保持宽高比进行缩小或者放大,使得图片完全显示在显示边界内。 |
Cover | 保持宽高比进行缩小或者放大,使得图片两边都大于或等于显示边界。 |
Auto | 图像会根据其自身尺寸和组件的尺寸进行适当缩放,以在保持比例的同时填充视图。 |
Fill | 不保持宽高比进行放大缩小,使得图片充满显示边界。 |
ScaleDown | 保持宽高比显示,图片缩小或者保持不变。 |
None | 保持原有尺寸显示。 |
除objectFit属性外,还可以通过placeholderObjectFit和errorholderObjectFit分别设置占位图填充效果和错误图填充效果。
示例4 监听图片加载成功与失败,获取加载数据
通过imageKnifeOption的onLoadListener回调,监听图片加载请求的结果,并获取图片宽高等信息。
示例5 监听网络下载进度
通过imageKnifeOption的progressListener回调,获取图片的下载进度。
示例6 自定义下载图片
通过imageKnifeOption的customGetImage,传入一个@Concurrent修饰的方法,并发这个方法中自定义实现图片的获取方法,通常接入其它的网络库,比如Remote Communication Kit
。之所以需要用@Concurrent修饰,是因为该方法将通过taskpool在子线程中执行。
示例7 设置图片变换
通过imageKnifeOption的transformation属性设置设置图片变化。transformation的类型是PixelMapTransformation的抽象类,如果只需要一种图形变化,则传入该图形变化的实例。
如果需要多种图形变化,则实例化一个MultiTransTransformation对象,其构造方法可以传入一个PixelMapTransformation数组。
更多图形变化,需要依赖GPUImage这个三方库
示例8 图片降采样
降采样(Downsampling)是一种常用的技术,用于减小图片的分辨率,从而降低内存占用,避免内存溢出(OOM)等问题。通过imageKnifeOption的downsampleOf属性设置图片的降采样策略。
名称 | 描述 |
---|---|
DEFAULT(缺省) | 默认值,图片分辨率超过上限7680 * 4320,宽高等比降为7680 * 4320。 |
NONE | 不进行降采样。 |
AT_MOST | 请求尺寸大于实际尺寸不进行放大。 |
FIT_CENTER_MEMORY | 两边自适应内存优先。 |
FIT_CENTER_QUALITY | 两边自适应质量优先。 |
CENTER_INSIDE_QUALITY | 按照宽高比的最大比进行适配内存优先。 |
CENTER_INSIDE_MEMORY | 按照宽高比的最大比进行适配质量优先。 |
AT_LEAST | 宽高进行等比缩放宽高里面最小的比例先放进去,然后再根据原图的缩放比去适配。 |
示例9 图片缩放
通过ImageKnifeComponent的transform方法,改变图片的缩放比例。实际场景多监听手指的缩放事件,来调整组件的缩放比例。
示例10 网络请求header设置
通过imageKnifeOption的headerOption,可以设置网络请求头,便于访问某些Header鉴权的网络图片资源。
示例11 动图展示与控制
因为ImageKnifeComponent底部封装系统Image,所以只能展示动图(gif/webp等),而不能控制动图的播放与暂停。如需要控制动图,则可使用该三方库提供的另一个ImageKnifeAnimatorComponent组件。
通过animatorOption的state属性,可以修改动画的播放状态。通过iterations属性,可以设置循环播放的次数,其中-1是无限循环。此外还可以监听动画开始,完成,取消等事件。
实现原理
1.组件依赖关系
ImageKnifeComponent和ImageKnifeAnimatorComponent是ImageKnife三方库提供的两个自定义组件,内部以Imageknife为主要入口,内部实现了图片的加载。
2.核心加载流程
ImageKnifeComponent和ImageKnifeAnimatorComponent在初始化后,通过onSizeChange获取组件宽高后,每个图片生成一个ImageKnifeRequest请求,下发到排队队列。ImageKnifeDispacher管理了请求并发数,并利用taskpool在非UI线程里异步下载图片,写缓存,解码成pixelmap,以及图形变化后,最后交给系统Image和ImageAnimator展示。
3.核心设计点
- 外观封装:通过三方库自定义组件方式提供给应用,内部封装了图片下载,解码,缓存等细节,并使用系统Image显示图片。
- 异步加载:内部使用taskpool线程池管理图片加载,避免在主线程上进行耗时的网络请求和磁盘IO操作,保障应用的流畅性。
- 单例接口:对应用全局提供核心接口,避免频繁地创建和销毁对象。
- 合并相同请求:自动合并相同的图片请求,减少重复网络和解码开销。
- 生命周期感知:组件还未加载就被复用或销毁时,自动取消改图片的加载。
- 解码拓展:通过实现PixelMapTransformation,可拓展图片解码。
- 下载拓展:通过传入customGetImage方法,可自实现网络下载。
拓展进阶
1. ImageKnife动态预加载方案最佳实践
背景
列表是应用开发中最常见的一类开发场景,它可以将杂乱的信息整理成有规律、易于理解和操作的形式,便于用户查找和获取所需要的信息。应用程序中常见的列表场景有新闻列表、购物车列表、各类排行榜等。随着信息数据的累积,特别是一些新闻应用、购物应用、聊天应用,列表数据往往会达到上万条,针对这类大量数据加载的长列表应用,如何对长列表的性能进行优化是非常重要的。一个正确、高性能的长列表应用能明显降低列表渲染时间、提升页面的滑动帧率、降低应用内存占用,大幅提升用户体验。
针对长列表加载这一场景,通常会使用 5 种优化手段,通过这些优化手段的单个使用或组合使用,可以对列表渲染时间、页面滑动帧率、应用内存占用等方面带来优化,提升性能和用户体验:
- 懒加载:提供列表数据按需加载能力,解决一次性加载长列表数据耗时长、占用过多资源的问题,可以提升页面响应速度。
- 缓存列表项:提供屏幕可视区域外列表项长度的自定义调节能力,配合懒加载设置可缓存列表项参数,通过预加载数据提升列表滑动体验。
- 动态预加载:根据历史任务加载耗时情况,动态调整屏幕可视区域外数据预取数量,配合懒加载设置,可在列表不断滑动时,屏幕可视区外实时更新列表数据,通过预取和预渲染数据提升列表滑动体验。
- 组件复用:提供可复用组件对象的缓存资源池,通过重复使用已经创建过并缓存的组件对象,降低相同组件短时间内频繁创建和销毁的开销,提升组件渲染效率。
- 布局优化:使用扁平化布局方案,减少视图嵌套层级和组件数,避免过度绘制,提升页面渲染效率。
预加载方案
三方库ImageKnife自带图片缓存特性,结合懒加载的使用,可以有效提升图片加载的效率,解决白块问题。此外通过ImageKnife的preload方法可以预加载图片,结合列表LazyForEach懒加载机制,可以更好地实现提前预加载,进一步减少图片加载白块问题:
- 首先需要实现 DataSourcePrefetchingImageKnife 类,继承 IDataSourcePrefetching 接口,并通过imageknife的能力实现 prefetch 和 cancel 方法。
其中 prefetch 做图片的请求和缓存处理。cancel 在组件不可视区域取消请求。
- 在应用列表界面,首先创建 DataSourcePrefetchingImageKnife、BasicPrefetcher 对象,然后在 List 的 onScrollIndex 回调中调用 BasicPrefetcher 的 visibleAreaChanged 方法,传入 List 的可见区域起始坐标。这样在下滑过程中,会在图片尚未加载前,自动触发DataSourcePrefetchingImageKnife的prefetch 和 cancel 接口动态预加载。这样当图片真正加载时,仅需要从缓存加载图片,从而大幅度缩短图片加载的时间。
预加载效果
对懒惰加载场景时,cachedCount=5、cachedCount=30 和动态预加载的数据:
cachedCount=5 | cachedCount=30 | 动态预加载 |
---|---|---|
![]() |
![]() |
![]() |
数据设置 | 首屏加载 | 滑动过程滑块数量 |
---|---|---|
cachedCount=5 | 首屏加载快(首屏加载可视区 +5 张)。 | 滑动过程中白块很多。 |
cachedCount=30 | 首屏概率加载慢(首屏加载可视区 +30 张)。 | 滑动过程中没有白块或很少。 |
动态预加载 | 首屏加载快(首屏加载可视区 +5 张;再加载不可视区域图片)。 | 滑动过程中没有白块或很少。 |
局限性
1.因为该库完全使用arkts语言编写,且存在跨语言调用的性能损耗,所以虽然自定义能力比系统image强,但图片加载性能较系统Image有所下降。因此该库正在使用c++重构实现,详见image-knife-c。
2.当前该库设计没有较好地提供扩展方式,让开发者自定义缓存实现,解码能力。
3.由于底层系统网络Network Kit没有提供取消请求的功能,导致销毁的组件无法及时取消网络请求。这样在快速下滑的图片的场景时,前面的图片虽然被销毁了,但后台仍然在下载,影响当前图片的下载速度。
结束语
随着越来越多的鸿蒙应用上线,各种开源的闭源的,开发者自发的和大厂贡献的库也正在逐步涌现出来,同时也有一些早期的库已被淘汰。今年我将抽空基于HarmonyOS 5.0+试试水,根据实际效果和下载量筛选在awesome-harmony-library,帮助大家快速找到合适的三方库,也请大家也帮忙Star下这个仓库:)
