HarmonyOS鸿蒙Next ArkTS中如何实现带缓存的图片加载(如 Glide 效果)?

HarmonyOS鸿蒙Next ArkTS中如何实现带缓存的图片加载(如 Glide 效果)? 频繁加载相同网络图片导致流量浪费和卡顿,所以实现带缓存的图片加载(如 Glide 效果),如何做呢?

8 回复

鸿蒙的图片加载ImageKnife是参考开源库 Glide OpenHarmony图片加载库 还有C端实现 OpenHarmony图片加载库C端实现

更多关于HarmonyOS鸿蒙Next ArkTS中如何实现带缓存的图片加载(如 Glide 效果)?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


可以参考使用库 imageknife:

https://ohpm.openharmony.cn/#/cn/detail/@ohos%2Fimageknife

使用cacheDownload.download接口下载网络图片。

资源若下载成功会被缓存到应用内存或应用沙箱目录的特定文件中。

import { cacheDownload } from '@kit.BasicServicesKit';
@Entry
@Component
struct Index {
  @State src: string = 'https://www.example.com/xxx.gif'; // 请填写一个具体的网络图片地址。
  async aboutToAppear(): Promise<void> {
    // 提供缓存下载任务的配置选项。
    let options: cacheDownload.CacheDownloadOptions = {};
    try {
      // 进行缓存下载,资源若下载成功会被缓存到应用内存或应用沙箱目录的特定文件中。
      cacheDownload.download(this.src, options);
      console.info(`success to download the resource. `);
    } catch (err) {
      console.error(`Failed to download the resource: code: ${err.code}, message: ${err.message}`);
    }
  }
  build() {
    Column() {
      // 若src指定的是网络图片且已成功下载并缓存,则本次显示无需重复下载。
      Image(this.src)
        .width(100)
        .height(100)
        .objectFit(ImageFit.Cover)
        .borderWidth(1)
    }
    .height('100%')
    .width('100%')
  }
}

背景知识:

1、系统自带的 Image 组件是有缓存图片功能的

2、自己实现一个三级缓存功能

问题解决:

实现三级缓存功能实例工具:

import { fileIo, fileIo as fs } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';
import { MD5 } from '@yunkss/eftool';
import { image } from '@kit.ImageKit';
import { FileUtils } from './FileUtils';
import { request } from '@kit.BasicServicesKit';

// 生成缓存文件名(MD5示例)
function getCacheKey(url: string): string {
    return `avatar_${MD5.digest(url).getDataRow()}.jpg`; // 需自行实现MD5生成逻辑
}

// 获取缓存路径
function getCachePath(context: common.UIAbilityContext, url: string): string {
    const path = `${context.cacheDir}/image`
    const success = fs.accessSync(path)
    if (!success) {
        fs.mkdirSync(path)
    }
    return `${path}/${getCacheKey(url)}`;
}

// 检查文件缓存
function checkDiskCache(context: common.UIAbilityContext,
    url: string,
    call: (image: image.PixelMap | null) => void) {
    const cachePath = getCachePath(context, url);
    try {
        const exists = fs.accessSync(cachePath);
        if (exists) {
            const imageSource = image.createImageSource(cachePath);
            imageSource.createPixelMap((err: BusinessError, data: PixelMap) => {
                call(data)
            });
        } else {
            call(null)
        }
    } catch (err) {
        console.warn('Disk cache check failed:', err);
    }
}


const memoryCache = new Map<string, image.PixelMap>(); // 使用Map存储解码后图像

export function getCachedImage(context: common.UIAbilityContext,
    url: string,
    call: (image: image.PixelMap) => void) {
    // 1. 检查内存缓存
    console.log("获取图片:" + url)
    if (memoryCache.has(url)) {
        call(memoryCache.get(url)!);
        return
    }

    console.log("获取磁盘获取:" + url)
    // 2. 检查磁盘缓存
    checkDiskCache(context, url, (diskCache) => {
        if (diskCache) {
            console.log("获取磁盘获取成功")
            memoryCache.set(url, diskCache); // 写入内存缓存
            call(diskCache);
        } else {

            console.log("获取网络获取:" + url)
            // 3. 网络下载并缓存

            downloadFile(context, url, (path: string) => {
                const netSource = image.createImageSource(path);
                netSource.createPixelMap((err: BusinessError, pixelMap: PixelMap) => {
                    // 写入内存缓存
                    memoryCache.set(url, pixelMap);

                    console.log("获取缓存中到文件:" + url)
                    // 异步存储到磁盘
                    const cachePath = getCachePath(context, url);
                    const imagePacker = image.createImagePacker();
                    const packOpts: image.PackingOption = { format: "image/jpeg", quality: 90 };
                    imagePacker.packing(pixelMap, packOpts,
                        (err: BusinessError, data: ArrayBuffer) => {
                            const file =
                                fs.openSync(cachePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
                            fs.writeSync(file.fd, data);

                            call(pixelMap)
                        });
                });
            })
        }
    });


}

//网络下载图片
function downloadFile(context: common.UIAbilityContext, url: string, call: (path: string) => void) {
    const cachePath = getCachePath(context, url);
    const config: request.DownloadConfig = {
        url: url,
        filePath: cachePath
    }
    try {
        request.downloadFile(context, config, (err: BusinessError, data: request.DownloadTask) => {
            if (err) {
                console.error("下载异常2:" + err.message)
                return
            }
            data.on("complete", () => {
                console.log("图片下载完成 " + cachePath)
                call(cachePath)
            })
        })

    } catch (error) {
        console.error("下载异常1:" + error)
    }

}

使用:

import { PreferencesUtils } from '../utils/PreferencesUtils';
import { common } from '@kit.AbilityKit';
import { image } from '@kit.ImageKit';
import { getCachedImage } from '../utils/CacheUtils';

@Entry
@Component
struct SingleTitleSolutionOne {
        this.getUIContext().getHostContext() as common.UIAbilityContext
    @State pixelMap: image.PixelMap | null = null

    aboutToAppear() {
    }

    build() {
        Column() {
            Text('标题/Banner')
                .fontSize(30)
                .width('100%')
                .height(200)
                .backgroundColor('#66666666')
                .textAlign(TextAlign.Center)
                .onClick(async () => {
                    //获取缓存图片
                    getCachedImage(this.context,
                        'https://aisearch.cdn.bcebos.com/homepage/dashboard/ai_picture_create/01.png',
                        (pic: image.PixelMap) => {
                            this.pixelMap = pic
                        });
                })


            if (this.pixelMap) {
                Image(this.pixelMap)
                    .width("100")
                    .height("100")
            }
        }
        .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])
        
    }
}

日志:

首次网络下载:

cke_36903.png

缓存获取:

cke_76835.png

本地获取:

cke_89533.png

Image 组件是带缓存策略的

  1. Image的缓存策略

Image模块提供了三级Cache机制,解码后内存图片缓存、解码前数据缓存、物理磁盘缓存。在加载图片时会逐级查找,如果在Cache中找到之前加载过的图片则提前返回对应的结果。

  1. Image组件如何配置打开和关闭缓存
  • 内存图片缓存:通过setImageCacheCount接口打开缓存,如果希望每次联网都获取最新资源,可以不设置(默认为0),不进行缓存。
  • 磁盘缓存:磁盘缓存是默认开启的,默认值为100M,可以将setImageFileCacheSize的值设置为0关闭磁盘缓存。
  • 解码前数据缓存:通过setImageRawDataCacheSize设置内存中缓存解码前图片数据的大小上限,单位为字节,提升再次加载同源图片的加载速度。如果不设置则默认为0,不进行缓存。

setImageCacheCount、setImageRawDataCacheSize和setImageFileCacheSize这三个图片缓存接口灵活性不足,后续不再演进。

对于复杂情况,需要更灵活的控制缓存策略,建议使用ImageKnifeimageknifepro

HarmonyOS 提供 内置图片缓存机制

  • 使用 Image 组件加载网络 URI 时,自动启用内存+磁盘缓存
  • 缓存策略不可配置,但可通过 URL 变更强制刷新(如加 ?v=2);
  • 若需自定义缓存(如 LRU、过期时间),可封装 PixelMap 加载器:
    • 先查 Preferences 或本地文件缓存;
    • 未命中则下载,存入 context.cacheDir
    • 使用 image.createImageSource().createPixelMap() 解码。

在ArkTS中实现带缓存的图片加载,可使用Image组件配合资源管理器。系统内置了缓存机制,加载网络图片时会自动缓存。

对于更复杂的缓存控制,可通过ResourceManager访问缓存数据,或使用http模块下载图片后存储至应用沙箱,再通过Image组件加载本地路径。

注意:鸿蒙Next的ArkTS不支持直接使用Android的Glide库。

在HarmonyOS Next的ArkTS中,可以通过Image组件结合资源管理机制实现带缓存的图片加载,达到类似Glide的效果。核心是利用系统的缓存策略和异步加载能力。

主要实现方案:

  1. 使用Image组件并设置网络源

    Image(src: string)
    

    直接传入网络URL时,系统会自动管理内存缓存。

  2. 配置缓存策略: 通过ImageinterpolationrenderMode属性可以间接影响缓存行为,但更关键的缓存控制需要通过资源管理器。

  3. 自定义缓存层(推荐)

    • 使用@ohos.file.fs模块实现磁盘缓存
    • 使用LRUCache实现内存缓存
    • 结合TaskPool实现异步加载

示例代码结构

// 1. 定义缓存管理器
class ImageCacheManager {
  private memoryCache: LRUCache<string, image.PixelMap>
  private diskCachePath: string
  
  async load(url: string): Promise<image.PixelMap> {
    // 检查内存缓存
    // 检查磁盘缓存
    // 网络下载并缓存
  }
}

// 2. 在UI中使用
@State cachedImage: PixelMap | null = null

async loadImage() {
  this.cachedImage = await ImageCacheManager.load(url)
}

build() {
  Image(this.cachedImage)
}

关键优化点

  • 使用image.createImageSource处理网络图片
  • 通过imageSource.createPixelMap解码图片
  • 利用TaskPool执行耗时操作避免阻塞UI

系统级的Image组件已经内置了基础的内存缓存,对于大多数场景足够使用。需要高级功能时可扩展自定义缓存层。

回到顶部