diff --git a/entry/src/main/ets/pages/testImageKnifeCache.ets b/entry/src/main/ets/pages/testImageKnifeCache.ets new file mode 100644 index 0000000..98f495f --- /dev/null +++ b/entry/src/main/ets/pages/testImageKnifeCache.ets @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2022 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 +} from '@ohos/libraryimageknife' +import image from '@ohos.multimedia.image'; +import { BusinessError } from '@ohos.base'; + + +let imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife(); + +@Entry +@Component +struct testImageKnifeCache { + @State url: string = ''; + @State url1: string = ''; + @State imagePixelMap: PixelMap | undefined = undefined; + @State gifPixelMap: PixelMap | undefined = undefined; + @State imageKnifeOption1: ImageKnifeOption = + { + loadSrc: $r('app.media.icon'), + + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed') + }; + @State imageKnifeOption2: ImageKnifeOption = + { + loadSrc: $r('app.media.icon'), + + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed') + }; + + hasUrlCache(url: string) { + + imageKnife?.isUrlExist(url).then((data: ImageKnifeData) => { + + if (data.isPixelMap()) { + if (data.drawPixelMap) { + let pixelmap = data.drawPixelMap.imagePixelMap + if (pixelmap) { + this.imagePixelMap = pixelmap; + } + } + } + if (data.isGIFFrame()) { + let index: number = 0 + let timeId = -1; + clearTimeout(timeId); + 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) { + this.gifPixelMap = pixelmap; + } + index++; + if (index == data.drawGIFFrame.imageGIFFrames.length - 1) { + index = 0 + } + timeId = setTimeout(renderGif, data!.drawGIFFrame!.imageGIFFrames![index].delay) + } + } + } + renderGif() + } + } + } + + }).catch((err:BusinessError)=>{ + + }); + + } + + build() { + Scroll() { + Column() { + + Text('图片内存和磁盘读取').fontSize(30); + Text('加载的缓存时候关闭掉网络').fontSize(15); + + Button('加载静态图片') + .onClick(() => { + this.imageKnifeOption1 = + { + loadSrc: this.url, + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed') + } + + }) + ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 }) + .width('100%') + .height(200) + .backgroundColor(Color.Orange) + + Button('加载缓存静态图片') + .onClick(() => { + this.imageKnifeOption1 = + { + loadSrc: this.url, + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed') + } + }) + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: this.imagePixelMap as image.PixelMap + } + }) + .width('100%') + .height(200) + .backgroundColor(Color.Orange) + + + Button('加载动态图片') + .onClick(() => { + this.imageKnifeOption2 = + { + loadSrc: this.url1, + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed') + } + + }) + ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption2 }) + .width('100%') + .height(200) + .backgroundColor(Color.Orange) + + Button('加载缓存动态图片') + .onClick(() => { + this.imageKnifeOption2 = + { + loadSrc: this.url1, + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed') + } + }) + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: this.gifPixelMap as image.PixelMap + } + }) + .width('100%') + .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/library/src/main/ets/components/imageknife/ImageKnife.ets b/library/src/main/ets/components/imageknife/ImageKnife.ets index 85568e4..8320b6f 100644 --- a/library/src/main/ets/components/imageknife/ImageKnife.ets +++ b/library/src/main/ets/components/imageknife/ImageKnife.ets @@ -41,6 +41,12 @@ import common from '@ohos.app.ability.common' import HashMap from '@ohos.util.HashMap' import LinkedList from '@ohos.util.LinkedList' import { MemoryLruCache } from '../cache/MemoryLruCache' +import { BusinessError } from '@ohos.base' +import { IParseImage } from './interface/IParseImage' +import { ParseImageUtil } from './utils/ParseImageUtil' +import { GIFParseImpl } from './utils/gif/GIFParseImpl' +import { GIFFrame } from './utils/gif/GIFFrame' +import { SVGParseImpl } from './utils/svg/SVGParseImpl' export class ImageKnife { static readonly SEPARATOR: string = '/' @@ -70,15 +76,16 @@ export class ImageKnife { defaultLifeCycle: IDrawLifeCycle | undefined = undefined; // 开发者可配置全局缓存 engineKeyImpl: EngineKeyInterface; + private mParseImageUtil: IParseImage; private constructor() { - + this.mParseImageUtil = new ParseImageUtil(); this.runningMaps = new EasyLinkedHashMap(); this.pendingMaps = new EasyLinkedHashMap(); this.pausedMaps = new EasyLinkedHashMap(); // 构造方法传入size 为保存文件个数 - this.memoryCache = new MemoryLruCache(100,100*1024*1024); + this.memoryCache = new MemoryLruCache(100, 100 * 1024 * 1024); // 创建disk缓存 传入的size 为多少比特 比如20KB 传入20*1024 this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext()); @@ -96,9 +103,8 @@ export class ImageKnife { this.fileTypeUtil = new FileTypeUtil(); this.engineKeyImpl = new EngineKeyFactories(); - - } + //全局设置请求头调用方法 addHeader(key: string, value: Object) { this.headerMap.set(key, value); @@ -184,11 +190,11 @@ export class ImageKnife { // 设置缓存张数,缓存大小,单位字节 - public setLruCacheSize(size: number,memory:number) { + public setLruCacheSize(size: number, memory: number) { if (this.memoryCache.map.size() <= 0) { - this.memoryCache = new MemoryLruCache(size,memory); + this.memoryCache = new MemoryLruCache(size, memory); } else { - let newLruCache = new MemoryLruCache(size,memory); + let newLruCache = new MemoryLruCache(size, memory); this.memoryCache.foreachLruCache((value: ImageKnifeData, key: string, map: Object) => { newLruCache.put(key, value); }) @@ -214,6 +220,138 @@ export class ImageKnife { // } } + + public isUrlExist(url: string): Promise { + + return new Promise((resolve, reject) => { + let request1 = new RequestOption(); + request1.loadSrc = url; + let request = this.loadResourcesCaches(request1); + + let loadComplete = (imageKnifeData: ImageKnifeData) => { + this.memoryCache.put(request.generateCacheKey, imageKnifeData); + resolve(imageKnifeData); + } + let loadError = (err ?: BusinessError | string) => { + reject(undefined); + } + this.loadMemoryCacheAndDiskFrom(request, loadComplete, loadError); + }) + } + + loadMemoryCacheAndDiskFrom(request: RequestOption, onComplete: (imageKnifeData: ImageKnifeData) => void | PromiseLike, onError: (err?: BusinessError | string) => void) { + let cache = this.memoryCache.get(request.generateCacheKey); + if (cache == null || typeof cache == 'undefined') { + let cached = this.diskMemoryCache.get(request.generateDataKey); + if (cached == null) { + //4.加载磁盘缓存 原图 + cached = this.diskMemoryCache.get(request.generateResourceKey); + } + if (cached != null) { + + let filetype:string | null =this.fileTypeUtil.getFileType(cached); + + if (filetype ==null){ + onError('请检查数据源'); + return; + } + + if (!this.fileTypeUtil.isImage(cached)){ + onError('暂不支持的类型!类型='+filetype); + } + + if (ImageKnifeData.GIF == filetype&&!request.dontAnimateFlag){ + let gifParseImpl = new GIFParseImpl() + gifParseImpl.parseGifs(cached, (data?:GIFFrame[],err?:BusinessError|string)=>{ + if(err){ + onError(err) + } + LogUtil.log("gifProcess data is null:"+(data == null)); + if(!!data){ + let imageKnifeData = ImageKnifeData.createImageGIFFrame(ImageKnifeType.GIFFRAME,data); + LogUtil.log('gifProcess 生成gif 返回数据类型') + onComplete(imageKnifeData) + }else{ + onError('Parse GIF callback data is null, you need check callback data!') + } + }) + + }else if (ImageKnifeData.SVG == filetype){ + let svgParseImpl = new SVGParseImpl() + svgParseImpl.parseSvg(request,cached, onComplete,onError) + }else { + //5.磁盘有数据,解析错误返回onError + let success = (value: PixelMap) => { + let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, value); + onComplete(imageKnifeData); + } + this.mParseImageUtil.parseImage(cached, success, onError) + } + + } else { + //6.磁盘无数据,返回onError + onError("网络缓存and磁盘缓存无数据!") + } + } else { + //需要清理状态 + cache.waitSaveDisk = false; + //2.网络缓存有数据,返回 + onComplete(cache); + } + } + + loadResourcesCaches(request: RequestOption) { + let factories: EngineKeyInterface; + let cacheKey: string; + let transferKey: string; + let dataKey: string; + if (this.engineKeyImpl) { + factories = this.engineKeyImpl; + } else { + factories = new EngineKeyFactories(); + } + // 生成内存缓存key 内存 变换后磁盘 + + let loadKey = ''; + if (typeof request.loadSrc == 'string') { + loadKey = request.loadSrc; + } else { + loadKey = JSON.stringify(request.loadSrc); + } + + let size = JSON.stringify(request.size); + + 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() + ","; + } + } + } + + let dontAnimateFlag = request.dontAnimateFlag; + + let signature = request.signature; + + cacheKey = factories.generateMemoryCacheKey(loadKey, size, transformed, dontAnimateFlag, signature); + + // 生成磁盘缓存变换后数据key 变换后数据保存在磁盘 + transferKey = factories.generateTransformedDiskCacheKey(loadKey, size, transformed, dontAnimateFlag, signature); + + // 生成磁盘缓存源数据key 原始数据保存在磁盘 + dataKey = factories.generateOriginalDiskCacheKey(loadKey, signature); + + request.generateCacheKey = cacheKey; + request.generateResourceKey = transferKey; + request.generateDataKey = dataKey; + + return request; + } + + // 预加载 resource资源一级缓存,string资源实现二级缓存 preload(request: RequestOption): void { // 每个request 公共信息补充 @@ -308,6 +446,7 @@ export class ImageKnife { return this.parseSource(request); } + loadResources(request: RequestOption) { let factories: EngineKeyInterface; let cacheKey: string; @@ -344,13 +483,13 @@ export class ImageKnife { let signature = request.signature; - cacheKey = factories.generateMemoryCacheKey(loadKey,size,transformed,dontAnimateFlag,signature); + cacheKey = factories.generateMemoryCacheKey(loadKey, size, transformed, dontAnimateFlag, signature); // 生成磁盘缓存变换后数据key 变换后数据保存在磁盘 - transferKey = factories.generateTransformedDiskCacheKey(loadKey,size,transformed,dontAnimateFlag,signature); + transferKey = factories.generateTransformedDiskCacheKey(loadKey, size, transformed, dontAnimateFlag, signature); // 生成磁盘缓存源数据key 原始数据保存在磁盘 - dataKey = factories.generateOriginalDiskCacheKey(loadKey,signature); + dataKey = factories.generateOriginalDiskCacheKey(loadKey, signature); request.generateCacheKey = cacheKey; request.generateResourceKey = transferKey; @@ -375,8 +514,6 @@ export class ImageKnife { private keyEqualPendingToRun(nextPending: RequestOption) { - - this.pendingMaps.remove(nextPending.uuid) this.runningMaps.put(nextPending.uuid, nextPending); @@ -428,7 +565,7 @@ export class ImageKnife { } if (hasEqualRunning) { - if(tailNode != null && tailNode.value != null) { + if (tailNode != null && tailNode.value != null) { this.keyEqualPendingToRun(tailNode.value); } } else {