鸿蒙图片缓存(二)
缓存工具后续
上一篇中图片下载和缓存中需要注意得一点是对于动图得处理,图片如果是gif或者webp这种类型得图片,给Image组件显示得时候直接给uri,不用转换成pixMap。
//加入gif判断获取缓存,名称改变,对应的缓存需要更新到缓存里面let type = await this.getImageType(downloadPath)if (this.GIF_IMAG_TYPES.includes(type)){const gifUri = this.URI_HEAD + downloadPathImageCacheUtil.imagLruCache.put(downloadUrlMd5, gifUri)resolve(gifUri)return}
获取磁盘文件转换成PixMap
/*** 通过path和url将文件编码为pixelMap* @param path* @returns*/private async uriOrPathConvertPixelMap(path: string): Promise<image.PixelMap | undefined> {return new Promise((resole, _) => {try {let file = fs.openSync(path, fs.OpenMode.READ_WRITE)const imageSource: image.ImageSource = image.createImageSource(file.fd)fs.close(file)let decodingOptions: image.DecodingOptions = {editable: true,desiredPixelFormat: image.PixelMapFormat.RGBA_8888,}imageSource.createPixelMap(decodingOptions).then((pixelMap: PixelMap) => {resole(pixelMap);}).catch((_: Error) => {resole(undefined)});} catch (e) {resole(undefined)}})}
图片缓存工具封装
export namespace ImageCacheUtil {export class Builder {imageFolderName: string = 'images'cacheMaxSize: number = 1024 * 1024 * 200 //默认200Mcontext: ContextnoCatchImageType: string[] = []public constructor(context: Context) {this.context = context}public setImageFolderName(imageFolderName: string): Builder{this.imageFolderName = imageFolderNamereturn this}public setCacheMaxSize(cacheMaxSize: number): Builder{this.cacheMaxSize = cacheMaxSizereturn this}public setNoCatchImageType(noCatchImageType: string[]): Builder{this.noCatchImageType.push(...noCatchImageType)return this}public build(): ImageCacheUtil {return new ImageCacheUtil(this)}}
}
多线程图片缓存
以上图片下载和缓存都是再主线程实现得,虽然使用了promise但是还是会抢占主线程性能,使用taskTool 多线程中下载图片增加性能。
import { ImageCacheUtil } from "./ImageCacheUtil";
import { image } from "@kit.ImageKit";
import { taskpool as TaskPool } from '@kit.ArkTS';
import { Context } from '@kit.AbilityKit';@Concurrent
export async function downloadImage(context: Context, url: string): Promise<string | image.PixelMap> {return await new ImageCacheUtil.Builder(context).build().downloadCachedFile(url);
}//将线程任务添加到任务池,并执行线程操作
async function fetchImagePixelMap(context: Context, url: string): Promise<string | image.PixelMap> {const task: TaskPool.Task = new TaskPool.Task(downloadImage, context, url);const pixelMap = await TaskPool.execute(task)TaskPool.terminateTask(task)return pixelMap as (string | image.PixelMap);
}//图片下载工具(带并发控制&&线程池)
export class ImageDownloader {private downloadLocks: Map<string, Promise<string | image.PixelMap>> = new Map();private static instance: ImageDownloader;private constructor() {}// 获取单例实例public static getInstance(): ImageDownloader {if (!ImageDownloader.instance) {ImageDownloader.instance = new ImageDownloader();}return ImageDownloader.instance;}// 清除缓存initCheckCacheAndDeleteCache(){new ImageCacheUtil.Builder(getContext()).build().initCheckCacheAndDeleteCache()}// 下载图片(带并发控制)async downloadImage(context: Context, url: string) {let downloadPromise = this.downloadLocks.get(url);if (!downloadPromise) {downloadPromise = this.executeDownload(context, url)?.catch(() => {this.downloadLocks.delete(url);return url})?.finally(() => {this.downloadLocks.delete(url);});this.downloadLocks.set(url, downloadPromise);}}// 获取图片(带并发控制)async getImage(context: Context, url: string): Promise<string | image.PixelMap> {let downloadPromise = this.downloadLocks.get(url);if (!downloadPromise) {downloadPromise = this.executeDownload(context, url)?.catch(() => {this.downloadLocks.delete(url);return url})?.finally(() => {this.downloadLocks.delete(url);});this.downloadLocks.set(url, downloadPromise);}return downloadPromise;}// 执行实际下载逻辑private async executeDownload(context: Context, url: string): Promise<string | image.PixelMap> {return fetchImagePixelMap(context, url); // 恢复通过线程池执行}
}
使用实例
import { image } from '@kit.ImageKit';
import { ImageDownloader } from '../tool/ImageDownloader';@Entry
@Component
struct Home {@State imagePixelMap:image.PixelMap | string = ""aboutToAppear(): void {let downLoadUrl = this.urlif (downLoadUrl) {ImageDownloader.getInstance().getImage(getContext(), downLoadUrl).then((imagePixelMap) => {this.imagePixelMap = imagePixelMap})}}url = "https://ts1.tc.mm.bing.net/th/id/" +"R-C.987f582c510be58755c4933cda68d525?rik=C0D21hJDYvXosw&" +"riu=http%3a%2f%2fimg.pconline.com.cn%2fimages%2fupload%2fupc%2ftx%2fwall" +"paper%2f1305%2f16%2fc4%2f20990657_1368686545122.jpg&" +"ehk=netN2qzcCVS4ALUQfDOwxAwFcy41oxC%2b0xTFvOYy5ds%3d&risl=&pid=ImgRaw&r=0"build() {Column() {Image(this.imagePixelMap)}.height('100%').width('100%')}
}
这是基于系统组件Image使用,另一种封装方式是创建一个新的自定义组件,传入url等配置,组件内部在使用系统组件显示。 这种图片缓存工具常用的三方库可以参考 ImageKnife