diff --git a/CHANGELOG.md b/CHANGELOG.md index 43ad547..4caf4a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ - 提供图片加载成功/失败的事件 - 修复懒加载在多次点击出现卡死的问题 - 支持多种组合变换 +- 提供清理缓存能力 ## 2.2.0-rc.2 - ImageKnife支持下采样 diff --git a/README.md b/README.md index 5b4c3d5..0ada189 100644 --- a/README.md +++ b/README.md @@ -381,6 +381,12 @@ request.skipMemoryCache(true) | resumeRequests() | | 全局恢复暂停 | | isUrlExist(url, cacheType, size) | url, CacheType, Size | 判断图片是否在 缓存和磁盘中存在,如果入参是缓存,需要传入值图片大小,参数 CacheType, Size(可选) | | setMaxRequests(count: number) | count | 设置请求的最大并发数量 | +| removeAllMemoryCache() | | 清除全部内存缓存 | +| removeAllFileCache() | | 清除全部磁盘缓存 | +| removeMemoryCache(url: string) | url: string | 清除指定内存缓存 | +| removeFileCache(url: string) | url: string | 清除指定磁盘缓存 | +| getMemoryCacheKey(url: string) | url: string | 获取指定图片内存缓存key | +| getDiskCacheKey(url: string) | url: string | 获取指定图片磁盘缓存key | ### 缓存策略相关 diff --git a/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets b/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets index f8c5f35..4ab95a2 100644 --- a/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets +++ b/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets @@ -438,6 +438,14 @@ struct IndexFunctionDemo { router.pushUrl({ url: 'pages/testImageKnifeLoadState' }); }).margin({ top: 5, left: 3 }) }.width('100%').height(60).backgroundColor(Color.Pink) + + Text('测试内存删除').fontSize(15) + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Button('测试内存删除') + .onClick(() => { + router.pushUrl({ url: 'pages/testImageKnifeRemoveCache' }); + }).margin({ top: 5, left: 3 }) + }.width('100%').height(60).backgroundColor(Color.Pink) } } .width('100%') diff --git a/entry/src/main/ets/pages/testImageKnifeRemoveCache.ets b/entry/src/main/ets/pages/testImageKnifeRemoveCache.ets new file mode 100644 index 0000000..9f90f5a --- /dev/null +++ b/entry/src/main/ets/pages/testImageKnifeRemoveCache.ets @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + ImageKnifeComponent, + ImageKnifeOption, + ImageKnifeGlobal, + ImageKnife, + ImageKnifeData, + Size, + CacheType +} from '@ohos/libraryimageknife' +import image from '@ohos.multimedia.image'; +import { BusinessError } from '@ohos.base'; + +let imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife(); + +@Entry +@Component +struct testImageKnifeRemoveCache { + @State url: string = ''; + @State urlGif: string = 'https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658'; + @State urlPng: string = 'https://img-blog.csdnimg.cn/20191215043500229.png'; + @State urlJpg: string = 'https://hbimg.huabanimg.com/cc6af25f8d782d3cf3122bef4e61571378271145735e9-vEVggB'; + @State urlBmp: string = 'https://img-blog.csdn.net/20140514114029140'; + @State urlWebp: string = 'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp'; + @State imagePixelMap: PixelMap | undefined = undefined; + @State imagePixelMap_: PixelMap | undefined = undefined; + private index_: number = -1; + private tempSize: number = 200; + private timeId = -1; + private comSize: Size = { + width: this.tempSize, + height: this.tempSize, + } + @State imageKnifeOption: ImageKnifeOption = + { + loadSrc: $r('app.media.icon'), + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed') + }; + loadSuccess = (data: ImageKnifeData) => { + clearTimeout(this.timeId); + if (data.isPixelMap()) { + if (data.drawPixelMap) { + let pixelmap = data.drawPixelMap.imagePixelMap; + if (pixelmap) { + if (this.index_ == 1) { + this.imagePixelMap = pixelmap; + } else if (this.index_ == 2) { + this.imagePixelMap_ = pixelmap; + } + } + } + } + if (data.isGIFFrame()) { + let index: number = 0 + if (data.drawGIFFrame) { + if (data.drawGIFFrame.imageGIFFrames) { + let renderGif = () => { + if (data.drawGIFFrame) { + if (data.drawGIFFrame.imageGIFFrames) { + let pixelmap = data.drawGIFFrame.imageGIFFrames[index].drawPixelMap; + let delay = data.drawGIFFrame.imageGIFFrames[index].delay; + if (pixelmap) { + if (this.index_ == 1) { + this.imagePixelMap = pixelmap; + } else if (this.index_ == 2) { + this.imagePixelMap_ = pixelmap; + } + } + index++; + if (index == data.drawGIFFrame.imageGIFFrames.length - 1) { + index = 0; + } + this.timeId = setTimeout(renderGif, data!.drawGIFFrame!.imageGIFFrames![index].delay); + } + } + } + renderGif(); + } + } + } + } + loadError = (err: BusinessError) => { + } + + build() { + Scroll() { + Column() { + + Text('图片内存和磁盘缓存删除').fontSize(30); + Text('测试删除缓存时候关闭掉网络').fontSize(15); + + Row() { + Button('png') + .margin({right:10}) + .onClick(() => { + this.index_ = 0; + this.url = this.urlPng; + this.imageKnifeOption = + { + loadSrc: this.url, + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed') + } + }) + + Button('bmp') + .margin({right:10}) + .onClick(() => { + this.index_ = 0; + this.url = this.urlBmp; + this.imageKnifeOption = + { + loadSrc: this.url, + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed') + } + }) + + Button('webp') + .margin({right:10}) + .onClick(() => { + this.index_ = 0; + this.url = this.urlWebp; + this.imageKnifeOption = + { + loadSrc: this.url, + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed') + } + }) + + Button('jpg') + .margin({right:10}) + .onClick(() => { + this.index_ = 0; + this.url = this.urlJpg; + this.imageKnifeOption = + { + loadSrc: this.url, + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed') + } + }) + + Button('gif') + .margin({right:10}) + .onClick(() => { + this.index_ = 0; + this.url = this.urlGif; + this.imageKnifeOption = + { + loadSrc: this.url, + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed') + } + }) + }.margin({top: 20}) + + Row() { + Button('缓存图片') + .margin({right:10}) + .onClick(() => { + this.index_ = 1; + imageKnife?.isUrlExist(this.url, CacheType.Cache, this.comSize).then(this.loadSuccess) + .catch(this.loadError); + }) + + Button('磁盘图片') + .margin({right:10}) + .onClick(() => { + this.index_ = 2; + imageKnife?.isUrlExist(this.url, CacheType.Disk, this.comSize).then(this.loadSuccess) + .catch(this.loadError); + }) + }.margin({top: 20}) + + Row() { + Button('清除全部缓存') + .onClick(() => { + imageKnife?.removeAllMemoryCache(); + imageKnife?.removeAllFileCache(); + }) + + Button('清除全部内存缓存') + .onClick(() => { + imageKnife?.removeAllMemoryCache(); + }) + + Button('清除全部磁盘缓存') + .onClick(() => { + imageKnife?.removeAllFileCache(); + }) + }.margin({top: 20}) + + Row() { + Button('清除指定内存缓存') + .margin({right:10}) + .onClick(() => { + imageKnife?.removeMemoryCache(this.url); + }) + + Button('清除指定磁盘缓存') + .margin({right:10}) + .onClick(() => { + imageKnife?.removeFileCache(this.url); + }) + }.margin({top: 20}) + + Text('网络图').fontSize(15); + ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption }) + .width(200) + .height(200) + .backgroundColor(Color.Orange) + Text('缓存图').fontSize(15); + + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: this.imagePixelMap as image.PixelMap + } + }) + .width(200) + .height(200) + .backgroundColor(Color.Orange) + Text('磁盘图').fontSize(15); + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: this.imagePixelMap_ as image.PixelMap + } + }) + .width(200) + .height(200) + .backgroundColor(Color.Orange) + + } + .alignItems(HorizontalAlign.Center) + .width('100%') + + } + .width('100%') + .height('100%') + } + + aboutToAppear() { + + } + + aboutToDisappear() { + + } + + onBackPress() { + + } +} \ No newline at end of file diff --git a/entry/src/main/resources/base/profile/main_pages.json b/entry/src/main/resources/base/profile/main_pages.json index 476a18a..60bb9a8 100644 --- a/entry/src/main/resources/base/profile/main_pages.json +++ b/entry/src/main/resources/base/profile/main_pages.json @@ -60,6 +60,7 @@ "pages/testCustomDataFetchClientWithPage", "pages/testReuseAblePages", "pages/downsamplingPage", - "pages/testImageKnifeLoadState" + "pages/testImageKnifeLoadState", + "pages/testImageKnifeRemoveCache" ] } \ No newline at end of file diff --git a/entry/src/ohosTest/ets/test/imageknife.test.ets b/entry/src/ohosTest/ets/test/imageknife.test.ets index ad85441..5f3b429 100644 --- a/entry/src/ohosTest/ets/test/imageknife.test.ets +++ b/entry/src/ohosTest/ets/test/imageknife.test.ets @@ -14,7 +14,9 @@ */ import hilog from '@ohos.hilog'; import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' -import {ImageKnife,ImageKnifeDrawFactory,ImageKnifeGlobal} from '@ohos/imageknife' +import { DiskLruCache, ImageKnife,ImageKnifeDrawFactory,ImageKnifeGlobal,LruCache} from '@ohos/imageknife'; +import { common } from '@kit.AbilityKit'; +import { GlobalContext } from '../testability/GlobalContext'; const BASE_COUNT: number = 2000; @@ -74,6 +76,33 @@ export default function ImageKnifeTest() { } }) + it('TestRemoveAllMemoryCache',2,()=>{ + let imageKnife:ImageKnife|undefined = ImageKnifeGlobal.getInstance().getImageKnife(); + imageKnife?.removeAllMemoryCache(); + let a = imageKnife?.getMemoryCache(); + expect(a).assertEqual(undefined); + }) + it('TestRemoveAllFileCache',3,()=>{ + let imageKnife:ImageKnife|undefined = ImageKnifeGlobal.getInstance().getImageKnife(); + imageKnife?.removeAllFileCache(); + let a = imageKnife?.getDiskMemoryCache(); + expect(a).assertEqual(undefined); + }) + it('TestRemoveMemoryCache',4,()=>{ + let memoryCache = new LruCache(5); + memoryCache.put("1","1"); + memoryCache.remove("1"); + let result = memoryCache.get("1"); + expect(result).assertEqual(undefined); + }) + it('TestRemoveFileCache',5,()=>{ + let context: object | undefined = GlobalContext.getInstance().getObject("hapContext"); + let disLruCache: DiskLruCache = DiskLruCache.create(context as common.UIAbilityContext, 1024); + disLruCache.set('test', "Hello World Simple Example."); + disLruCache.deleteCacheDataByKey('test'); + let a = disLruCache.get('test'); + expect(a).assertEqual(undefined); + }) }) } diff --git a/library/src/main/ets/components/cache/DiskLruCache.ets b/library/src/main/ets/components/cache/DiskLruCache.ets index 198bc5f..00f2e05 100644 --- a/library/src/main/ets/components/cache/DiskLruCache.ets +++ b/library/src/main/ets/components/cache/DiskLruCache.ets @@ -272,6 +272,14 @@ export class DiskLruCache { this.cacheMap.clear() this.size = 0 } + async cleanAllDiskCache() { + let filenames: string[] = await FileUtils.getInstance().ListFile(this.path); + for (let i = 0; i < filenames.length; i++) { + await FileUtils.getInstance().deleteFile(this.path + filenames[i]); + } + this.cacheMap.clear(); + this.size = 0; + } getCacheMap() { return this.cacheMap; diff --git a/library/src/main/ets/components/cache/FileUtils.ets b/library/src/main/ets/components/cache/FileUtils.ets index 8e9d926..67ea85a 100644 --- a/library/src/main/ets/components/cache/FileUtils.ets +++ b/library/src/main/ets/components/cache/FileUtils.ets @@ -410,5 +410,13 @@ export class FileUtils { fs.readSync(fd, buf) return buf } + async ListFile(path: string): Promise { + try { + return fs.listFile(path); + } catch (err) { + console.error("FileUtils ListFile failed with error message: " + err.message + ", error code: " + err.code); + } + return []; + } } diff --git a/library/src/main/ets/components/cache/LruCache.ets b/library/src/main/ets/components/cache/LruCache.ets index ef27540..b93ec2c 100644 --- a/library/src/main/ets/components/cache/LruCache.ets +++ b/library/src/main/ets/components/cache/LruCache.ets @@ -49,6 +49,13 @@ export class LruCache { } return preValue } + removeAll() { + this.map.each((value, key) => { + this.remove(key); + }) + this.map.clear(); + this.size = 0; + } // 获取键为key的value get(key: K): V | undefined { diff --git a/library/src/main/ets/components/imageknife/ImageKnife.ets b/library/src/main/ets/components/imageknife/ImageKnife.ets index ac4bac1..2009820 100644 --- a/library/src/main/ets/components/imageknife/ImageKnife.ets +++ b/library/src/main/ets/components/imageknife/ImageKnife.ets @@ -171,6 +171,66 @@ export class ImageKnife { setMemoryCache(lrucache: MemoryLruCache) { this.memoryCache = lrucache; } + // 移除全部内存缓存 + removeAllMemoryCache(): void { + this.memoryCacheProxy.removeAllValue(); + } + // 移除全部磁盘缓存 + async removeAllFileCache(): Promise { + if (this.diskMemoryCache !== undefined) { + await this.getDiskMemoryCache().cleanAllDiskCache(); + } + } + // 移除指定内存缓存 + removeMemoryCache(url: string): void { + let key = this.getMemoryCacheKey(url); + this.memoryCacheProxy.removeValue(key); + } + // 移除指定磁盘缓存 + removeFileCache(url: string): void { + let key = this.getDiskCacheKey(url); + this.diskMemoryCache.deleteCacheDataByKey(key); + } + // 获取指定内存缓存key值 + getMemoryCacheKey(url: string) { + let request = new RequestOption(); + let factories: EngineKeyInterface; + let dontAnimateFlag = request.dontAnimateFlag; + let signature = request.signature; + const imageSize: Size = { + width: 200, + height: 200, + } + let size = JSON.stringify(imageSize); + if (this.engineKeyImpl) { + factories = this.engineKeyImpl; + } else { + factories = new EngineKeyFactories(); + } + let transformed = ''; + if (request && request.transformations) { + for (let i = 0; i < request.transformations.length; i++) { + if (i == request.transformations.length - 1) { + transformed += request.transformations[i].getName() + ""; + } else { + transformed += request.transformations[i].getName() + ","; + } + } + } + return factories.generateMemoryCacheKey(url, size, transformed, dontAnimateFlag, signature); + } + // 获取指定磁盘缓存key值 + getDiskCacheKey(url: string) { + let request = new RequestOption(); + let signature = request.signature; + let factories: EngineKeyInterface; + if (this.engineKeyImpl) { + factories = this.engineKeyImpl; + } else { + factories = new EngineKeyFactories(); + } + return factories.generateOriginalDiskCacheKey(url, signature); + } getDefaultListener() { return this.defaultListener; diff --git a/library/src/main/ets/components/imageknife/requestmanage/MemoryCacheProxy.ets b/library/src/main/ets/components/imageknife/requestmanage/MemoryCacheProxy.ets index bacefa7..f021681 100644 --- a/library/src/main/ets/components/imageknife/requestmanage/MemoryCacheProxy.ets +++ b/library/src/main/ets/components/imageknife/requestmanage/MemoryCacheProxy.ets @@ -39,6 +39,9 @@ export class MemoryCacheProxy implements ICache { removeValue(key: K): V|undefined{ return this.mLruCache.remove(key); } + removeAllValue() { + this.mLruCache.removeAll(); + } clear() { this.mLruCache.evicAll();