Pre Merge pull request !231 from zgf/3.x

This commit is contained in:
zgf 2024-04-30 09:31:57 +00:00 committed by Gitee
commit 8ad5b6a21c
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
17 changed files with 473 additions and 44 deletions

View File

@ -1,6 +1,14 @@
## 3.0.0-rc.1
- 新增从内存或文件缓存获取图片数据接口getCacheImage
- 新增图片预加载preLoadCache并会犯文件缓存路径
- ImageKnifeOption新增writeCacheStrategy存入策略(只存入内存或文件缓存)
- ImageKnifeOption新增onlyRetrieveFromCache仅用缓存加载
- 新增单个和全局请求头
- 补齐自定key特性
- 获取组件宽高改用onSizeChange
## 3.0.0-rc.0 ## 3.0.0-rc.0
- 使用Image组件替换Canvas组件渲染并重构大部分的实现逻辑提升渲染性能 - 使用Image组件替换Canvas组件渲染并重构大部分的实现逻辑提升渲染性能
- 补齐自定key特性
较2.x版本增强点 较2.x版本增强点
- 使用Image组件代替Canvas组件渲染 - 使用Image组件代替Canvas组件渲染

View File

@ -121,7 +121,34 @@ ImageKnifeComponent({ ImageKnifeOption:
} }
}).width(100).height(100) }).width(100).height(100)
``` ```
## 接口说明
### ImageKnifeOption参数列表
| 参数名称 | 入参内容 | 功能简介 |
|-----------------------|-------------------------------|---------------|
| loadSrc | string、PixelMap、Resource | 主图展示 |
| placeholderSrc | PixelMap、Resource | 占位图图展示(可选) |
| errorholderSrc | PixelMap、Resource | 错误图展示(可选) |
| objectFit | ImageFit | 图片展示样式(可选) |
| writeCacheStrategy | WriteCacheStrategyType | 写入缓存策略(可选) |
| onlyRetrieveFromCache | boolean | 跳过网络和本地请求(可选) |
| customGetImage | (context: Context, src: string | 自定义网络(可选) | | Resource | 错误占位图数据源 |
| border | BorderOptions | 边框圆角(可选) |
| priority | taskpool.Priority | 加载优先级(可选) |
| context | common.UIAbilityContext | 上下文(可选) |
| progressListener | (progress: number)=>void | 进度(可选) |
| signature | ObjectKey | 自定义缓存关键字 |
| headerOption | Array<HeaderOptions> | 设置请求头 |
### ImageKnife接口
| 参数名称 | 入参内容 | 功能简介 |
|--------------|---------------------------|---------------|
| preLoadCache | url:string | 预加载并返回文件缓存路径 |
| getCacheImage | url: string, cacheType: CacheType | 从内存或文件缓存中获取资源 |
| addHeader | key: string, value: Object | 全局添加请求头属性 |
| serHeaderOptions | Array<HeaderOptions> | 全局设置请求头 |
| deleteHeader | key: string | 全局删除请求头 |
| setEngineKeyImpl | CustomEngineKeyImpl | 全局配置缓存key |
## 约束与限制 ## 约束与限制
API11 API11

View File

@ -37,6 +37,9 @@ export default class EntryAbility extends UIAbility {
// 初始化ImageKnife的文件缓存 // 初始化ImageKnife的文件缓存
await ImageKnife.getInstance().initFileCache(this.context, 256, 256 * 1024 * 1024) await ImageKnife.getInstance().initFileCache(this.context, 256, 256 * 1024 * 1024)
ImageKnife.getInstance().setEngineKeyImpl(new CustomEngineKeyImpl()) ImageKnife.getInstance().setEngineKeyImpl(new CustomEngineKeyImpl())
// 全局配置请求头
ImageKnife.getInstance().addHeader('refer', "http://1.94.37.200:7070/AntiTheftChain/downloadImage");
ImageKnife.getInstance().deleteHeader('refer');
windowStage.loadContent('pages/Index', (err, data) => { windowStage.loadContent('pages/Index', (err, data) => {
if (err.code) { if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');

View File

@ -68,6 +68,24 @@ struct Index {
}); });
}) })
Button("测试文件缓存预加载").onClick(()=>{
router.push({
uri: 'pages/TestPrefetchToFileCache',
});
})
Button("测试获取内存文件缓存").onClick(()=>{
router.push({
uri: 'pages/TestIsUrlExist',
});
})
Button("测试单个请求头").onClick(()=>{
router.push({
uri: 'pages/TestHeader',
});
})
} }
.width('100%') .width('100%')

View File

@ -0,0 +1,39 @@
/*
* 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,ImageKnife,ImageKnifeOption } from '@ohos/imageknife'
@Entry
@Component
struct TestPrefetchToFileCachePage {
@State imageKnifeOption: ImageKnifeOption = {
loadSrc:"https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658",
placeholderSrc:$r('app.media.loading'),
headerOption:[
{
key:"abc",
value:"单个"
}
]
}
build() {
Column() {
ImageKnifeComponent({
ImageKnifeOption: this.imageKnifeOption
}).width(300).height(300)
}
.height('100%') .width('100%')
}
}

View File

@ -0,0 +1,77 @@
/*
* 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,ImageKnife,ImageKnifeOption,ReadCacheStrategyType } from '@ohos/imageknife'
@Entry
@Component
struct TestIsUrlExist {
@State imageKnifeOption: ImageKnifeOption = {
loadSrc:$r('app.media.startIcon'),
placeholderSrc:$r('app.media.loading')
}
@State source: PixelMap | string = ""
@State source1: PixelMap | string = ""
build() {
Column() {
Flex(){
Button("加载gif图").onClick(()=>{
this.imageKnifeOption.loadSrc = "https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658"
})
Button("内存缓存获取gif").onClick(()=>{
ImageKnife.getInstance()
.getCacheImage("https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658",ReadCacheStrategyType.Memory)
.then((data)=>{
this.source = data!.source
})
})
Button("文件缓存获取gif").onClick(()=>{
ImageKnife.getInstance()
.getCacheImage("https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658",ReadCacheStrategyType.File)
.then((data)=>{
this.source1 = data!.source
})
})
}
Flex(){
Button("加载静态图").onClick(()=>{
this.imageKnifeOption.loadSrc = 'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp'
})
Button("内存缓存获取").onClick(()=>{
ImageKnife.getInstance()
.getCacheImage('https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp',ReadCacheStrategyType.Memory)
.then((data)=>{
this.source = data!.source
})
})
Button("文件缓存获取").onClick(()=>{
ImageKnife.getInstance()
.getCacheImage('https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp',ReadCacheStrategyType.File)
.then((data)=>{
this.source1 = data!.source
})
})
}
ImageKnifeComponent({
ImageKnifeOption: this.imageKnifeOption
}).width(200).height(200)
Image(this.source)
.width(200).height(200).backgroundColor(Color.Pink)
Image(this.source1)
.width(200).height(200).backgroundColor(Color.Pink)
}
.height('100%') .width('100%')
}
}

View File

@ -0,0 +1,42 @@
/*
* 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,ImageKnife,ImageKnifeOption } from '@ohos/imageknife'
@Entry
@Component
struct TestPrefetchToFileCachePage {
@State imageKnifeOption: ImageKnifeOption = {
loadSrc:$r('app.media.startIcon'),
placeholderSrc:$r('app.media.loading')
}
async preload(url:string) {
let fileCachePath = await ImageKnife.getInstance().preLoadCache(url)
console.log("preload-fileCachePath=="+ fileCachePath)
}
build() {
Column() {
Button("磁盘预加载").onClick(async ()=>{
await this.preload("https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658")
})
Button("加载图片").onClick(()=>{
this.imageKnifeOption.loadSrc = "https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658"
})
ImageKnifeComponent({
ImageKnifeOption: this.imageKnifeOption
}).width(300).height(300)
}
.height('100%') .width('100%')
}
}

View File

@ -8,6 +8,9 @@
"pages/TransformPage", "pages/TransformPage",
"pages/UserPage", "pages/UserPage",
"pages/TestImageFlash", "pages/TestImageFlash",
"pages/SignatureTestPage" "pages/SignatureTestPage",
"pages/TestPrefetchToFileCache",
"pages/TestIsUrlExist",
"pages/TestHeader"
] ]
} }

View File

@ -14,6 +14,8 @@ export { IEngineKey } from './src/main/ets/key/IEngineKey'
export { ObjectKey } from './src/main/ets/model/ObjectKey' export { ObjectKey } from './src/main/ets/model/ObjectKey'
export { ImageKnifeData , ReadCacheStrategyType} from "./src/main/ets/model/ImageKnifeData"

View File

@ -14,7 +14,7 @@
"main": "index.ets", "main": "index.ets",
"repository": "https://gitee.com/openharmony-tpc/ImageKnife", "repository": "https://gitee.com/openharmony-tpc/ImageKnife",
"type": "module", "type": "module",
"version": "3.0.0-rc.0", "version": "3.0.0-rc.1",
"dependencies": { "dependencies": {
}, },

View File

@ -13,12 +13,18 @@
* limitations under the License. * limitations under the License.
*/ */
import { ImageKnifeRequest } from './ImageKnifeRequest'; import { ImageKnifeRequest } from './ImageKnifeRequest';
import { ImageKnifeData } from './model/ImageKnifeData'; import { ReadCacheStrategyType, ImageKnifeData } from './model/ImageKnifeData';
import { MemoryLruCache } from './utils/MemoryLruCache'; import { MemoryLruCache } from './utils/MemoryLruCache';
import { IMemoryCache } from './utils/IMemoryCache' import { IMemoryCache } from './utils/IMemoryCache'
import { FileCache } from './utils/FileCache'; import { FileCache } from './utils/FileCache';
import { ImageKnifeDispatcher } from './ImageKnifeDispatcher'; import { ImageKnifeDispatcher } from './ImageKnifeDispatcher';
import { IEngineKey } from './key/IEngineKey'; import { IEngineKey } from './key/IEngineKey';
import { HeaderOptions, ImageKnifeOption } from './ImageKnifeOption';
import { DefaultEngineKey } from './key/DefaultEngineKey';
import { FileTypeUtil } from './utils/FileTypeUtil';
import { util } from '@kit.ArkTS';
import { image } from '@kit.ImageKit';
import { common } from '@kit.AbilityKit';
export class ImageKnife { export class ImageKnife {
@ -28,6 +34,7 @@ export class ImageKnife {
// 文件缓存 // 文件缓存
private fileCache?: FileCache private fileCache?: FileCache
private dispatcher: ImageKnifeDispatcher = new ImageKnifeDispatcher() private dispatcher: ImageKnifeDispatcher = new ImageKnifeDispatcher()
headerMap: Map<string, Object> = new Map<string, Object>(); //定义全局map
public static getInstance(): ImageKnife { public static getInstance(): ImageKnife {
if (!ImageKnife.instance) { if (!ImageKnife.instance) {
@ -49,7 +56,30 @@ export class ImageKnife {
this.fileCache = new FileCache(context, size, memory) this.fileCache = new FileCache(context, size, memory)
this.fileCache.initFileCache() this.fileCache.initFileCache()
} }
/**
* 全局添加单个请求头
* @param key 请求头属性
* @param value 请求头值
*/
addHeader(key: string, value: Object) {
this.headerMap.set(key, value);
}
/**
* 全局设置请求头调用方法
* @param options 请求头数组
*/
serHeaderOptions(options:Array<HeaderOptions>) {
options.forEach((value)=>{
this.headerMap.set(value.key,value.value)
})
}
/**
* 删除单个请求头属性
* @param key 请求头属性
*/
deleteHeader(key: string) {
this.headerMap.delete(key);
}
/** /**
* 设置自定义的内存缓存 * 设置自定义的内存缓存
* @param newMemoryCache 自定义内存缓存 * @param newMemoryCache 自定义内存缓存
@ -86,6 +116,99 @@ export class ImageKnife {
this.fileCache?.put(key, data) this.fileCache?.put(key, data)
} }
getFileCache(): FileCache{
return this.fileCache as FileCache
}
/**
* 预加载到缓存
* @param loadSrc 图片地址url
* @returns 返回文件缓存路径
*/
preLoadCache(loadSrc:string): Promise<string> {
return new Promise((resolve,reject)=>{
let imageKnifeOption = new ImageKnifeOption()
imageKnifeOption.loadSrc = loadSrc
let engineKeyImpl: IEngineKey = new DefaultEngineKey()
let keys = engineKeyImpl.generateCacheKey(loadSrc)
let cachePath = ImageKnife.getInstance().getFileCache().getFileToPath(keys)
if(cachePath == null || cachePath == "" || cachePath == undefined) {
let request = new ImageKnifeRequest(
imageKnifeOption,
imageKnifeOption.context !== undefined ? imageKnifeOption.context : getContext(this) as common.UIAbilityContext,
0,
0,
0,
{
showPixelMap(version: number, pixelMap: PixelMap | string) {
resolve(ImageKnife.getInstance().getFileCache().getFileToPath(keys))
}
}
)
this.execute(request)
} else {
resolve(cachePath)
}
})
}
/**
* 从内存或文件缓存中获取图片数据
* @param url 图片地址url
* @param cacheType 缓存策略
* @returns 图片数据
*/
getCacheImage(loadSrc: string, cacheType: ReadCacheStrategyType = ReadCacheStrategyType.Default):Promise<ImageKnifeData | undefined> {
return new Promise((resolve,reject)=>{
let engineKeyImpl: IEngineKey = new DefaultEngineKey()
if(cacheType == ReadCacheStrategyType.Memory) {
resolve(this.ReadMemoryCache(loadSrc,engineKeyImpl))
} else if (cacheType == ReadCacheStrategyType.File) {
this.ReadFileCache(loadSrc,engineKeyImpl,resolve)
} else {
let data = this.ReadMemoryCache(loadSrc,engineKeyImpl)
data == undefined ? this.ReadFileCache(loadSrc,engineKeyImpl,resolve) : data
}
})
}
ReadMemoryCache(loadSrc:string,engineKey:IEngineKey): ImageKnifeData | undefined{
let keys = engineKey.generateCacheKey(loadSrc)
return ImageKnife.getInstance()
.loadFromMemoryCache(keys)
}
ReadFileCache(loadSrc:string,engineKey:IEngineKey,onComplete:(data:ImageKnifeData | undefined)=>void){
let keys = engineKey.generateCacheKey(loadSrc)
let buffer = ImageKnife.getInstance().loadFromFileCache(keys)
if(buffer != undefined) {
let fileTypeUtil = new FileTypeUtil();
let typeValue = fileTypeUtil.getFileType(buffer);
if (typeValue === 'gif' || typeValue === 'webp') {
let base64Help = new util.Base64Helper()
let base64str = "data:image/" + typeValue + ";base64," + base64Help.encodeToStringSync(new Uint8Array(buffer))
onComplete({
source:base64str,
imageWidth: 0,
imageHeight: 0
})
}
let imageSource: image.ImageSource = image.createImageSource(buffer);
let decodingOptions: image.DecodingOptions = {
editable: true,
}
imageSource.createPixelMap(decodingOptions)
.then((pixelmap: PixelMap) => {
onComplete({
source:pixelmap,
imageWidth: 0,
imageHeight: 0
})
imageSource.release()
})
} else {
onComplete(undefined)
}
}
/** /**
* 清除所有文件缓存 * 清除所有文件缓存
* @returns * @returns
@ -115,6 +238,9 @@ export class ImageKnife {
} }
async execute(request: ImageKnifeRequest): Promise<void> { async execute(request: ImageKnifeRequest): Promise<void> {
if (this.headerMap.size > 0) {
request.addHeaderMap(this.headerMap)
}
this.dispatcher.enqueue(request) this.dispatcher.enqueue(request)
} }

View File

@ -23,7 +23,7 @@ import common from '@ohos.app.ability.common';
import { FileCache } from './utils/FileCache'; import { FileCache } from './utils/FileCache';
import fs from '@ohos.file.fs'; import fs from '@ohos.file.fs';
import { ImageKnife } from './ImageKnife'; import { ImageKnife } from './ImageKnife';
import { ImageKnifeData } from './model/ImageKnifeData'; import { ImageKnifeData, WriteCacheStrategyType } from './model/ImageKnifeData';
import http from '@ohos.net.http'; import http from '@ohos.net.http';
import image from '@ohos.multimedia.image'; import image from '@ohos.multimedia.image';
import emitter from '@ohos.events.emitter'; import emitter from '@ohos.events.emitter';
@ -33,6 +33,7 @@ import { FileTypeUtil } from './utils/FileTypeUtil';
import util from '@ohos.util'; import util from '@ohos.util';
import { IEngineKey } from './key/IEngineKey'; import { IEngineKey } from './key/IEngineKey';
import { DefaultEngineKey } from './key/DefaultEngineKey'; import { DefaultEngineKey } from './key/DefaultEngineKey';
import { HeaderOptions } from './ImageKnifeOption';
export class ImageKnifeDispatcher { export class ImageKnifeDispatcher {
// 最大并发 // 最大并发
@ -110,7 +111,11 @@ export class ImageKnifeDispatcher {
context: currentRequest.context, context: currentRequest.context,
src: imageSrc, src: imageSrc,
key: key, key: key,
customGetImage: currentRequest.ImageKnifeOption.customGetImage headers:currentRequest.ImageKnifeOption.headerOption,
allHeaders:currentRequest.headers,
customGetImage: currentRequest.ImageKnifeOption.customGetImage,
onlyRetrieveFromCache: currentRequest.ImageKnifeOption.onlyRetrieveFromCache,
requestSource
} }
// 启动线程下载和解码主图 // 启动线程下载和解码主图
let task = new taskpool.Task(requestJob, request) let task = new taskpool.Task(requestJob, request)
@ -142,7 +147,7 @@ export class ImageKnifeDispatcher {
} }
} }
// 保存文件缓存 // 保存文件缓存
if (requestJobResult.bufferSize > 0) { if (requestJobResult.bufferSize > 0 && currentRequest.ImageKnifeOption.writeCacheStrategy !== WriteCacheStrategyType.Memory) {
ImageKnife.getInstance().saveWithoutWriteFile(key, requestJobResult.bufferSize) ImageKnife.getInstance().saveWithoutWriteFile(key, requestJobResult.bufferSize)
} }
@ -153,9 +158,11 @@ export class ImageKnifeDispatcher {
} }
// 保存内存缓存 // 保存内存缓存
if(currentRequest.ImageKnifeOption.writeCacheStrategy !== WriteCacheStrategyType.File) {
ImageKnife.getInstance() ImageKnife.getInstance()
.saveMemoryCache(this.engineKeyImpl.generateCacheKey(imageSrc, currentRequest.ImageKnifeOption.signature), ImageKnifeData) .saveMemoryCache(this.engineKeyImpl.generateCacheKey(imageSrc, currentRequest.ImageKnifeOption.signature),
ImageKnifeData)
}
if (requestList !== undefined) { if (requestList !== undefined) {
// todo 判断request生命周期已销毁的不需要再绘制 // todo 判断request生命周期已销毁的不需要再绘制
@ -244,7 +251,7 @@ async function requestJob(request: RequestJobRequest): Promise<RequestJobResult
if (request.src.indexOf("http://") == 0 || request.src.indexOf("https://") == 0) { //从网络下载 if (request.src.indexOf("http://") == 0 || request.src.indexOf("https://") == 0) { //从网络下载
// 先从文件缓存获取 // 先从文件缓存获取
resBuf = FileCache.getFileCacheByFile(request.context, request.key) resBuf = FileCache.getFileCacheByFile(request.context, request.key)
if (resBuf === undefined) { if (resBuf === undefined && request.onlyRetrieveFromCache != true && request.requestSource == 0) {
// // 模拟耗时验证 // // 模拟耗时验证
// let start = (new Date()).getTime(); // let start = (new Date()).getTime();
// while ((new Date()).getTime() - start < 5000) { // while ((new Date()).getTime() - start < 5000) {
@ -252,7 +259,18 @@ async function requestJob(request: RequestJobRequest): Promise<RequestJobResult
// } // }
let httpRequest = http.createHttp(); let httpRequest = http.createHttp();
let progress: number = 0 let progress: number = 0
const headerObj: Record<string,object> = {}
if(request.headers != undefined) {
request.headers.forEach((value)=>{
headerObj[value.key] = value.value
})
} else if(request.allHeaders.size > 0) {
request.allHeaders.forEach((value,key)=>{
headerObj[key] = value
})
}
let promise = httpRequest.requestInStream(request.src, { let promise = httpRequest.requestInStream(request.src, {
header:headerObj,
method: http.RequestMethod.GET, method: http.RequestMethod.GET,
connectTimeout: 6000, connectTimeout: 6000,
readTimeout: 6000, readTimeout: 6000,
@ -303,7 +321,12 @@ async function requestJob(request: RequestJobRequest): Promise<RequestJobResult
} }
} }
} else if ((request.src as Resource).id !== undefined) { //从资源文件获取 } else if ((request.src as Resource).id !== undefined) { //从资源文件获取
resBuf = FileCache.getFileCacheByFile(request.context, request.key)
if (resBuf == undefined && request.onlyRetrieveFromCache != true && request.requestSource == 0) {
resBuf = request.context.resourceManager.getMediaContentSync((request.src as Resource).id).buffer as ArrayBuffer resBuf = request.context.resourceManager.getMediaContentSync((request.src as Resource).id).buffer as ArrayBuffer
} else if (resBuf == undefined && request.requestSource != 0) {
resBuf = request.context.resourceManager.getMediaContentSync((request.src as Resource).id).buffer as ArrayBuffer
}
} }
} }
@ -364,5 +387,9 @@ interface RequestJobRequest {
context: common.UIAbilityContext, context: common.UIAbilityContext,
src: string | PixelMap | Resource, src: string | PixelMap | Resource,
key: string, key: string,
customGetImage?: (context: Context, src: string | PixelMap | Resource) => Promise<ArrayBuffer | undefined> headers?:Array<HeaderOptions>,
allHeaders:Map<string,Object>,
customGetImage?: (context: Context, src: string | PixelMap | Resource) => Promise<ArrayBuffer | undefined>,
onlyRetrieveFromCache?: boolean
requestSource:ImageKnifeRequestSource
} }

View File

@ -15,7 +15,11 @@
import taskpool from '@ohos.taskpool'; import taskpool from '@ohos.taskpool';
import common from '@ohos.app.ability.common' import common from '@ohos.app.ability.common'
import { ObjectKey } from './model/ObjectKey'; import { ObjectKey } from './model/ObjectKey';
import { WriteCacheStrategyType } from './model/ImageKnifeData';
export interface HeaderOptions {
key: string;
value: Object;
}
@Observed @Observed
export class ImageKnifeOption { export class ImageKnifeOption {
// 主图资源 // 主图资源
@ -25,6 +29,8 @@ export class ImageKnifeOption {
// 失败占位图 // 失败占位图
errorholderSrc?: PixelMap | Resource; errorholderSrc?: PixelMap | Resource;
headerOption?: Array<HeaderOptions>;
// 自定义缓存关键字 // 自定义缓存关键字
signature?: ObjectKey; signature?: ObjectKey;
@ -33,6 +39,10 @@ export class ImageKnifeOption {
customGetImage?: (context: Context, src: string | PixelMap | Resource) => Promise<ArrayBuffer | undefined> customGetImage?: (context: Context, src: string | PixelMap | Resource) => Promise<ArrayBuffer | undefined>
border?: BorderOptions border?: BorderOptions
// 缓存策略
writeCacheStrategy?: WriteCacheStrategyType
// 仅使用缓存加载数据
onlyRetrieveFromCache?: boolean = false;
priority? : taskpool.Priority = taskpool.Priority.LOW priority? : taskpool.Priority = taskpool.Priority.LOW

View File

@ -29,7 +29,7 @@ export class ImageKnifeRequest {
context: common.UIAbilityContext context: common.UIAbilityContext
ImageKnifeRequestCallback: ImageKnifeRequestCallback ImageKnifeRequestCallback: ImageKnifeRequestCallback
componentVersion: number = 0 componentVersion: number = 0
headers: Map<string,Object> = new Map<string,Object>()
constructor(option: ImageKnifeOption, constructor(option: ImageKnifeOption,
uIAbilityContext: common.UIAbilityContext, uIAbilityContext: common.UIAbilityContext,
width: number, width: number,
@ -43,6 +43,19 @@ export class ImageKnifeRequest {
this.componentVersion = version this.componentVersion = version
this.ImageKnifeRequestCallback = ImageKnifeRequestCallback this.ImageKnifeRequestCallback = ImageKnifeRequestCallback
} }
// RequestOption调用header对于的方法
addHeader(key: string, value: Object) {
this.headers.set(key, value);
}
// 全局调用header对应的方法包含RequestOption的形式
addHeaderMap(map: Map<string, Object>) {
map.forEach((value, key) => {
if (!this.headers.has(key)) {
this.addHeader(key, value);
}
})
}
} }
export enum ImageKnifeRequestState { export enum ImageKnifeRequestState {

View File

@ -35,10 +35,6 @@ export struct ImageKnifeComponent {
private currentHeight: number = 0 private currentHeight: number = 0
private componentVersion: number = 0 private componentVersion: number = 0
private currentContext: common.UIAbilityContext | undefined = undefined private currentContext: common.UIAbilityContext | undefined = undefined
@State keyCanvas: KeyCanvas = {
keyId: util.generateRandomUUID()
}
private listener: inspector.ComponentObserver = inspector.createComponentObserver(this.keyCanvas.keyId)
aboutToAppear(): void { aboutToAppear(): void {
//闪动问题失效,注释相应代码后续修复 //闪动问题失效,注释相应代码后续修复
@ -50,7 +46,6 @@ export struct ImageKnifeComponent {
// this.pixelMap = memoryCache.source; // this.pixelMap = memoryCache.source;
// }else { // }else {
LogUtil.log("aboutToAppear onLayoutComplete") LogUtil.log("aboutToAppear onLayoutComplete")
this.listener.on("layout", this.onLayoutComplete)
// } // }
} }
@ -60,21 +55,36 @@ export struct ImageKnifeComponent {
this.request.requestState = ImageKnifeRequestState.DESTROY this.request.requestState = ImageKnifeRequestState.DESTROY
this.request = undefined this.request = undefined
} }
this.listener.off("layout", this.onLayoutComplete)
} }
aboutToRecycle() { aboutToRecycle() {
if (this.request !== undefined) { if (this.request !== undefined) {
this.request.requestState = ImageKnifeRequestState.DESTROY this.request.requestState = ImageKnifeRequestState.DESTROY
this.request = undefined this.request = undefined
} }
} }
build() { build() {
Image(this.pixelMap) Image(this.pixelMap)
.objectFit(this.ImageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.ImageKnifeOption.objectFit) .objectFit(this.ImageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.ImageKnifeOption.objectFit)
.width(this.adaptiveWidth) .width(this.adaptiveWidth)
.height(this.adaptiveHeight) .height(this.adaptiveHeight)
.key(this.keyCanvas.keyId)
.border(this.ImageKnifeOption.border) .border(this.ImageKnifeOption.border)
.onSizeChange((oldValue:SizeOptions, newValue:SizeOptions) => {
this.currentWidth = newValue.width as number
this.currentHeight = newValue.height as number
this.lastWidth = oldValue.width as number
this.lastHeight = oldValue.height as number
if (this.currentWidth <= 0 || this.currentHeight <= 0) {
// 存在宽或者高为0,此次重回无意义,无需进行request请求
} else {
// 前提:宽高值均有效,值>0. 条件1当前宽高与上一次宽高不同 条件2:当前是第一次绘制
if (this.currentHeight != this.lastHeight || this.currentWidth != this.lastWidth) {
LogUtil.log("execute request:width=" + this.currentWidth + " height= " + this.currentHeight)
ImageKnife.getInstance().execute(this.getRequest(this.currentWidth, this.currentHeight))
}
}
})
} }
watchImageKnifeOption() { watchImageKnifeOption() {
@ -85,12 +95,14 @@ export struct ImageKnifeComponent {
this.componentVersion++ this.componentVersion++
ImageKnife.getInstance().execute(this.getRequest(this.currentWidth, this.currentHeight)) ImageKnife.getInstance().execute(this.getRequest(this.currentWidth, this.currentHeight))
} }
getCurrentContext(): common.UIAbilityContext { getCurrentContext(): common.UIAbilityContext {
if (this.currentContext == undefined) { if (this.currentContext == undefined) {
this.currentContext = getContext(this) as common.UIAbilityContext this.currentContext = getContext(this) as common.UIAbilityContext
} }
return this.currentContext return this.currentContext
} }
getRequest(width: number, height: number): ImageKnifeRequest { getRequest(width: number, height: number): ImageKnifeRequest {
if (this.request == undefined) { if (this.request == undefined) {
this.request = new ImageKnifeRequest( this.request = new ImageKnifeRequest(
@ -130,23 +142,6 @@ export struct ImageKnifeComponent {
return this.request return this.request
} }
onLayoutComplete: () => void = (): void => {
let value: componentUtils.ComponentInfo = componentUtils.getRectangleById(this.keyCanvas.keyId);
this.currentWidth = px2vp(value.size.width)
this.currentHeight = px2vp(value.size.height)
if (this.currentWidth <= 0 || this.currentHeight <= 0) {
// 存在宽或者高为0,此次重回无意义,无需进行request请求
} else {
// 前提:宽高值均有效,值>0. 条件1当前宽高与上一次宽高不同 条件2:当前是第一次绘制
if (this.currentHeight != this.lastHeight || this.currentWidth != this.lastWidth) {
this.lastWidth = this.currentWidth
this.lastHeight = this.currentHeight
LogUtil.log("execute request:width=" + this.currentWidth + " height= " + this.currentHeight)
ImageKnife.getInstance().execute(this.getRequest(this.currentWidth, this.currentHeight))
}
}
}
} }
interface KeyCanvas { interface KeyCanvas {

View File

@ -17,4 +17,26 @@ export interface ImageKnifeData {
imageWidth: number, imageWidth: number,
imageHeight: number imageHeight: number
} }
/**
* 读取缓存策略
*/
export enum ReadCacheStrategyType {
// 默认-读取内存和文件缓存
Default = 0,
// 只读取内存缓存
Memory = 1,
// 只读取文件缓存
File = 2
}
/**
* 写入缓存策略
*/
export enum WriteCacheStrategyType {
// 默认-写入内存和文件缓存
Default = 0,
// 只写入内存缓存
Memory = 1,
// 只写入文件缓存
File = 2
}

View File

@ -259,4 +259,21 @@ export class FileCache {
return FileUtils.getInstance() return FileUtils.getInstance()
.readFileSync(context.cacheDir + FileUtils.SEPARATOR + FileCache.CACHE_FOLDER + FileUtils.SEPARATOR + key) .readFileSync(context.cacheDir + FileUtils.SEPARATOR + FileCache.CACHE_FOLDER + FileUtils.SEPARATOR + key)
} }
/**
* 获取key缓存数据绝对路径
*
* @params key 数值
*/
getFileToPath(key: string): string {
if(!!!key) {
throw new Error("key is null,checking the parameter")
}
let path = this.path + key
if(FileUtils.getInstance().exist(path)) {
return path
} else {
return ""
}
}
} }