缓存工具后续  

    上一篇中图片下载和缓存中需要注意得一点是对于动图得处理,图片如果是gif或者webp这种类型得图片,给Image组件显示得时候直接给uri,不用转换成pixMap。

 //加入gif判断获取缓存,名称改变,对应的缓存需要更新到缓存里面
            let type = await this.getImageType(downloadPath)
            if (this.GIF_IMAG_TYPES.includes(type)){
              const gifUri = this.URI_HEAD + downloadPath
              ImageCacheUtil.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 //默认200M
    context: Context
    noCatchImageType: string[] = []

    public constructor(context: Context) {
      this.context = context
    }

    public setImageFolderName(imageFolderName: string): Builder{
      this.imageFolderName = imageFolderName
      return this
    }

    public setCacheMaxSize(cacheMaxSize: number): Builder{
      this.cacheMaxSize = cacheMaxSize
      return 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.url
    if (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

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐