HarmonyOS APP开发---"壁纸控"壁纸App,需要用到这个库
想做一个壁纸 App,几百张高清大图瀑布流加载?不缓存等于每次重新下载,不做降采样 4K 图直接 OOM------
@ohos/imageknife的二级缓存 + 降采样 + 生命周期管理,让瀑布流丝滑到底。
📦 仓库地址:gitcode.com/CPF-Applica... | 安装:ohpm install @ohos/imageknife
写在前面
"壁纸控"的核心挑战是"图片多、图片大":
- 瀑布流一次加载几十张 4K 壁纸,不缓存 = 每次重新下载 = 流量爆炸
- 4K 图直接解码进内存 = 单张 30MB+ = 几张就 OOM
- 用户快速滑动,已滑出的图片请求还在跑 = 浪费资源
@ohos/imageknife 基于 Glide 思路,提供了完整的解决方案:内存 LRU 缓存 + 磁盘二级缓存 + 内存降采样 + 生命周期感知。
⚠️ ImageKnife 已停止演进,新项目推荐 imageknifepro。本文仍适用于维护中的存量项目。
这篇文章聊什么
- 瀑布流加载------WaterFlow + ImageKnife 组件
- 缓存策略------内存缓存 + 磁盘缓存 + 降采样
- 预加载------下一条壁纸提前拉进缓存
flowchart TD
A[瀑布流滑动] --> B[请求壁纸 URL]
B --> C{内存缓存命中?}
C -->|是| D[直接渲染]
C -->|否| E{磁盘缓存命中?}
E -->|是| F[磁盘读取+解码]
E -->|否| G[网络下载]
G --> H[降采样解码]
H --> I[存入磁盘缓存]
F --> J[存入内存缓存]
I --> J
J --> D
第一步:安装
bash
ohpm install @ohos/imageknife
需要图片变换(模糊、亮度等)时额外装:
ohpm install @ohos/gpu_transform
第二步:瀑布流加载壁纸
typescript
import { ImageKnife, ImageKnifeOption, ImageKnifeComponent } from '@ohos/imageknife'
@Entry
@Component
struct WallpaperPage {
@State wallpapers: string[] = [
'https://cdn.example.com/wallpaper/4k_001.jpg',
'https://cdn.example.com/wallpaper/4k_002.jpg',
'https://cdn.example.com/wallpaper/4k_003.jpg',
// ... 更多壁纸
]
aboutToAppear() {
// 配置内存缓存 100MB
ImageKnife.with().initMemoryCache(100 * 1024 * 1024)
}
build() {
WaterFlow() {
ForEach(this.wallpapers, (url: string) => {
FlowItem() {
ImageKnifeComponent({
imageKnifeOption: {
loaderSource: { uri: url },
placeholder: $r('app.media.placeholder'),
errorholder: $r('app.media.error'),
// 降采样:按目标尺寸采样,4K 图不会全量进内存
downsampleOf: 'smallest',
}
})
.width('100%')
.aspectRatio(0.6)
}
}, (url: string) => url)
}
.columnsTemplate('1fr 1fr')
.width('100%').height('100%')
}
}
降采样是"壁纸控"不 OOM 的关键------4K 图按显示尺寸(约 300px 宽)采样后,内存从 30MB 降到几百 KB。
第三步:下载进度 + 仅缓存加载
typescript
// 带下载进度
ImageKnifeComponent({
imageKnifeOption: {
loaderSource: { uri: url },
onProgressUpdate: (received, total) => {
console.info(`下载进度: ${Math.ceil(received / total * 100)}%`)
},
}
})
// 离线模式:仅从缓存加载,不走网络
ImageKnifeComponent({
imageKnifeOption: {
loaderSource: { uri: url },
onlyRetrieveFromCache: true, // 缓存没有就显示 errorholder
}
})
第四步:预加载下一条壁纸
用户还在看第 3 张时,提前把第 4、5 张拉进缓存:
typescript
// 预加载(不显示,只进缓存)
ImageKnife.with().preLoadCache('https://cdn.example.com/wallpaper/4k_004.jpg')
ImageKnife.with().preLoadCache('https://cdn.example.com/wallpaper/4k_005.jpg')
// 用户滑到第 4 张时,内存缓存已命中,秒开
为什么"壁纸控"选了 ImageKnife?
| 需求 | 原生 Image | @ohos/imageknife |
|---|---|---|
| 内存缓存 | ❌ | ✅ LRU |
| 磁盘缓存 | ❌ | ✅ 二级缓存 |
| 降采样 | ❌ | ✅ 防 OOM |
| 下载进度 | ❌ | ✅ 回调 |
| 预加载 | ❌ | ✅ preLoadCache |
| 生命周期感知 | ❌ | ✅ 销毁自动取消 |
| 离线模式 | ❌ | ✅ onlyRetrieveFromCache |
总结
"壁纸控"这个场景里,@ohos/imageknife 解决了三件事:
- 不 OOM------降采样让 4K 图按显示尺寸进内存,30MB → 几百 KB
- 不重下------内存 + 磁盘二级缓存,滑过的图再滑回来秒开
- 不浪费------预加载 + 生命周期感知,该提前拉的提前拉,该取消的自动取消
如果你也在做壁纸、相册、电商商品图等海量图片场景,@ohos/imageknife 的缓存体系能让你省心一大半。(新项目建议直接用 imageknifepro)