1.修改imageknifeOpion为小写

2.writeCacheStrategy为memory时,子线程不写文件
3.文件缓存未初始化时,不写缓存

Signed-off-by: madixin <madixin@huawei.com>
This commit is contained in:
madixin 2024-05-03 15:06:58 +08:00
parent 00f5e81571
commit 33ba458600
7 changed files with 53 additions and 52 deletions

View File

@ -6,7 +6,7 @@
本项目参考开源库 [Glide](https://github.com/bumptech/glide) 进行OpenHarmony的自研版本 本项目参考开源库 [Glide](https://github.com/bumptech/glide) 进行OpenHarmony的自研版本
- 支持自定义内存缓存策略,支持设置内存缓存的大小。 - 支持自定义内存缓存策略,支持设置内存缓存的大小(默认LRU策略)
- 支持磁盘二级缓存,对于下载图片会保存一份至磁盘当中。 - 支持磁盘二级缓存,对于下载图片会保存一份至磁盘当中。
- 支持自定义实现图片获取/网络下载 - 支持自定义实现图片获取/网络下载
- 支持监听网络下载回调进度 - 支持监听网络下载回调进度
@ -42,6 +42,9 @@
## 下载安装 ## 下载安装
``` ```
ohpm install @ohos/imageknife ohpm install @ohos/imageknife
// 如果需要用文件缓存,需要提前初始化文件缓存
await ImageKnife.getInstance().initFileCache(context, 256, 256 * 1024 * 1024)
``` ```
## 使用说明 ## 使用说明
@ -162,7 +165,7 @@ ImageKnifeComponent({ ImageKnifeOption:
| addHeader | key: string, value: Object | 全局添加http请求头 | | addHeader | key: string, value: Object | 全局添加http请求头 |
| setHeaderOptions | Array<HeaderOptions> | 全局设置http请求头 | | setHeaderOptions | Array<HeaderOptions> | 全局设置http请求头 |
| deleteHeader | key: string | 全局删除http请求头 | | deleteHeader | key: string | 全局删除http请求头 |
| setEngineKeyImpl | CustomEngineKeyImpl | 全局配置缓存key | | setEngineKeyImpl | IEngineKey | 全局配置缓存key生成策略 |
## 约束与限制 ## 约束与限制
在下述版本验证通过: 在下述版本验证通过:

View File

@ -25,12 +25,6 @@ struct ImageTransformation {
this.isBlur = value this.isBlur = value
this.upedateImageKnifeOption() this.upedateImageKnifeOption()
}) })
.mark({
strokeColor: Color.Black,
size: 50,
strokeWidth: 5
})
.unselectedColor(Color.Red)
.width(30) .width(30)
.height(30) .height(30)
Text('模糊效果').fontSize(20) Text('模糊效果').fontSize(20)

View File

@ -54,13 +54,13 @@ export default function DefaultJobQueueTest() {
job.add(makeRequest("medium4", getContext() as common.UIAbilityContext)) job.add(makeRequest("medium4", getContext() as common.UIAbilityContext))
expect(job.getQueueLength()).assertEqual(7) expect(job.getQueueLength()).assertEqual(7)
expect(job.pop()!.ImageKnifeOption.loadSrc).assertEqual("high1") expect(job.pop()!.imageKnifeOption.loadSrc).assertEqual("high1")
expect(job.pop()!.ImageKnifeOption.loadSrc).assertEqual("medium1") expect(job.pop()!.imageKnifeOption.loadSrc).assertEqual("medium1")
expect(job.pop()!.ImageKnifeOption.loadSrc).assertEqual("medium2") expect(job.pop()!.imageKnifeOption.loadSrc).assertEqual("medium2")
expect(job.pop()!.ImageKnifeOption.loadSrc).assertEqual("medium3") expect(job.pop()!.imageKnifeOption.loadSrc).assertEqual("medium3")
expect(job.pop()!.ImageKnifeOption.loadSrc).assertEqual("medium4") expect(job.pop()!.imageKnifeOption.loadSrc).assertEqual("medium4")
expect(job.pop()!.ImageKnifeOption.loadSrc).assertEqual("low1") expect(job.pop()!.imageKnifeOption.loadSrc).assertEqual("low1")
expect(job.pop()!.ImageKnifeOption.loadSrc).assertEqual("low2") expect(job.pop()!.imageKnifeOption.loadSrc).assertEqual("low2")
expect(job.pop()).assertEqual(undefined) expect(job.pop()).assertEqual(undefined)
expect(job.getQueueLength()).assertEqual(0) expect(job.getQueueLength()).assertEqual(0)

View File

@ -48,7 +48,7 @@ export class ImageKnifeDispatcher {
showFromMemomry(request: ImageKnifeRequest, imageSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource): boolean { showFromMemomry(request: ImageKnifeRequest, imageSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource): boolean {
let memoryCache: ImageKnifeData | undefined = ImageKnife.getInstance() let memoryCache: ImageKnifeData | undefined = ImageKnife.getInstance()
.loadFromMemoryCache(this.engineKeyImpl.generateMemoryKey(imageSrc, request.ImageKnifeOption)) .loadFromMemoryCache(this.engineKeyImpl.generateMemoryKey(imageSrc, request.imageKnifeOption))
if (memoryCache !== undefined) { if (memoryCache !== undefined) {
// 画主图 // 画主图
if (request.requestState === ImageKnifeRequestState.PROGRESS) { if (request.requestState === ImageKnifeRequestState.PROGRESS) {
@ -69,7 +69,7 @@ export class ImageKnifeDispatcher {
enqueue(request: ImageKnifeRequest): void { enqueue(request: ImageKnifeRequest): void {
//1.内存有的话直接渲染 //1.内存有的话直接渲染
if (this.showFromMemomry(request, request.ImageKnifeOption.loadSrc, ImageKnifeRequestSource.SRC)) { if (this.showFromMemomry(request, request.imageKnifeOption.loadSrc, ImageKnifeRequestSource.SRC)) {
return return
} }
@ -83,22 +83,22 @@ export class ImageKnifeDispatcher {
executeJob(request: ImageKnifeRequest): void { executeJob(request: ImageKnifeRequest): void {
// 加载占位符 // 加载占位符
if (request.ImageKnifeOption.placeholderSrc !== undefined) { if (request.imageKnifeOption.placeholderSrc !== undefined) {
if (this.showFromMemomry(request, request.ImageKnifeOption.placeholderSrc, ImageKnifeRequestSource.PLACE_HOLDER) === false) { if (this.showFromMemomry(request, request.imageKnifeOption.placeholderSrc, ImageKnifeRequestSource.PLACE_HOLDER) === false) {
this.getAndShowImage(request, request.ImageKnifeOption.placeholderSrc, ImageKnifeRequestSource.PLACE_HOLDER) this.getAndShowImage(request, request.imageKnifeOption.placeholderSrc, ImageKnifeRequestSource.PLACE_HOLDER)
} }
} }
// 加载主图 // 加载主图
this.getAndShowImage(request, request.ImageKnifeOption.loadSrc, ImageKnifeRequestSource.SRC) this.getAndShowImage(request, request.imageKnifeOption.loadSrc, ImageKnifeRequestSource.SRC)
} }
/** /**
* 获取和显示图片 * 获取和显示图片
*/ */
getAndShowImage(currentRequest: ImageKnifeRequest, imageSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource): void { getAndShowImage(currentRequest: ImageKnifeRequest, imageSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource): void {
let keyMemory: string = this.engineKeyImpl.generateMemoryKey(imageSrc, currentRequest.ImageKnifeOption) let keyMemory: string = this.engineKeyImpl.generateMemoryKey(imageSrc, currentRequest.imageKnifeOption)
let keyFile: string = this.engineKeyImpl.generateFileKey(imageSrc, currentRequest.ImageKnifeOption.signature) let keyFile: string = this.engineKeyImpl.generateFileKey(imageSrc, currentRequest.imageKnifeOption.signature)
let requestList: List<ImageKnifeRequestWithSource> | undefined = this.executingJobMap.get(keyMemory) let requestList: List<ImageKnifeRequestWithSource> | undefined = this.executingJobMap.get(keyMemory)
if (requestList == undefined) { if (requestList == undefined) {
requestList = new List() requestList = new List()
@ -113,18 +113,19 @@ export class ImageKnifeDispatcher {
context: currentRequest.context, context: currentRequest.context,
src: imageSrc, src: imageSrc,
key: keyFile, key: keyFile,
headers:currentRequest.ImageKnifeOption.headerOption, headers:currentRequest.imageKnifeOption.headerOption,
allHeaders:currentRequest.headers, allHeaders:currentRequest.headers,
customGetImage: currentRequest.ImageKnifeOption.customGetImage, customGetImage: currentRequest.imageKnifeOption.customGetImage,
onlyRetrieveFromCache: currentRequest.ImageKnifeOption.onlyRetrieveFromCache, onlyRetrieveFromCache: currentRequest.imageKnifeOption.onlyRetrieveFromCache,
transformation:currentRequest.ImageKnifeOption.transformation, transformation:currentRequest.imageKnifeOption.transformation,
writeCacheStrategy: currentRequest.imageKnifeOption.writeCacheStrategy,
requestSource requestSource
} }
// 启动线程下载和解码主图 // 启动线程下载和解码主图
let task = new taskpool.Task(requestJob, request) let task = new taskpool.Task(requestJob, request)
// 监听网络回调事件 // 监听网络回调事件
if (currentRequest.ImageKnifeOption.progressListener !== undefined && requestSource === ImageKnifeRequestSource.SRC) { if (currentRequest.imageKnifeOption.progressListener !== undefined && requestSource === ImageKnifeRequestSource.SRC) {
let progressCallBack = currentRequest.ImageKnifeOption.progressListener let progressCallBack = currentRequest.imageKnifeOption.progressListener
emitter.on(Constants.PROGRESS_EMITTER, (data) => { emitter.on(Constants.PROGRESS_EMITTER, (data) => {
progressCallBack(data?.data?.value as number) progressCallBack(data?.data?.value as number)
}); });
@ -136,10 +137,10 @@ export class ImageKnifeDispatcher {
if (pixelmap === undefined) { if (pixelmap === undefined) {
if (requestList !== undefined) { if (requestList !== undefined) {
requestList.forEach((requestWithSource: ImageKnifeRequestWithSource) => { requestList.forEach((requestWithSource: ImageKnifeRequestWithSource) => {
if (requestWithSource.source === ImageKnifeRequestSource.SRC && currentRequest.ImageKnifeOption.errorholderSrc !== undefined) { if (requestWithSource.source === ImageKnifeRequestSource.SRC && currentRequest.imageKnifeOption.errorholderSrc !== undefined) {
if (this.showFromMemomry(currentRequest, currentRequest.ImageKnifeOption.errorholderSrc, ImageKnifeRequestSource.ERROR_HOLDER) === false) { if (this.showFromMemomry(currentRequest, currentRequest.imageKnifeOption.errorholderSrc, ImageKnifeRequestSource.ERROR_HOLDER) === false) {
this.getAndShowImage(currentRequest, currentRequest.ImageKnifeOption.errorholderSrc, ImageKnifeRequestSource.ERROR_HOLDER) this.getAndShowImage(currentRequest, currentRequest.imageKnifeOption.errorholderSrc, ImageKnifeRequestSource.ERROR_HOLDER)
} }
} }
}); });
@ -150,7 +151,7 @@ export class ImageKnifeDispatcher {
} }
} }
// 保存文件缓存 // 保存文件缓存
if (requestJobResult.bufferSize > 0 && currentRequest.ImageKnifeOption.writeCacheStrategy !== WriteCacheStrategyType.Memory) { if (requestJobResult.bufferSize > 0 && currentRequest.imageKnifeOption.writeCacheStrategy !== WriteCacheStrategyType.Memory) {
ImageKnife.getInstance().saveWithoutWriteFile(keyFile, requestJobResult.bufferSize) ImageKnife.getInstance().saveWithoutWriteFile(keyFile, requestJobResult.bufferSize)
} }
@ -161,9 +162,9 @@ export class ImageKnifeDispatcher {
} }
// 保存内存缓存 // 保存内存缓存
if(currentRequest.ImageKnifeOption.writeCacheStrategy !== WriteCacheStrategyType.File) { if(currentRequest.imageKnifeOption.writeCacheStrategy !== WriteCacheStrategyType.File) {
ImageKnife.getInstance() ImageKnife.getInstance()
.saveMemoryCache(this.engineKeyImpl.generateMemoryKey(imageSrc, currentRequest.ImageKnifeOption), .saveMemoryCache(this.engineKeyImpl.generateMemoryKey(imageSrc, currentRequest.imageKnifeOption),
ImageKnifeData) ImageKnifeData)
} }
if (requestList !== undefined) { if (requestList !== undefined) {
@ -305,7 +306,7 @@ async function requestJob(request: RequestJobRequest): Promise<RequestJobResult
}); });
// 保存文件缓存 // 保存文件缓存
if (resBuf !== undefined) { if (resBuf !== undefined && request.writeCacheStrategy !== WriteCacheStrategyType.Memory) {
let copyBuf = buffer.concat([buffer.from(resBuf)]).buffer; // IDE有bug不能直接获取resBuf.byteLength let copyBuf = buffer.concat([buffer.from(resBuf)]).buffer; // IDE有bug不能直接获取resBuf.byteLength
bufferSize = copyBuf.byteLength bufferSize = copyBuf.byteLength
FileCache.saveFileCacheOnlyFile(request.context, request.key, resBuf) FileCache.saveFileCacheOnlyFile(request.context, request.key, resBuf)
@ -324,7 +325,6 @@ 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) { 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) { } else if (resBuf == undefined && request.requestSource != 0) {
@ -401,4 +401,5 @@ interface RequestJobRequest {
onlyRetrieveFromCache?: boolean onlyRetrieveFromCache?: boolean
requestSource:ImageKnifeRequestSource requestSource:ImageKnifeRequestSource
transformation?: PixelMapTransformation transformation?: PixelMapTransformation
writeCacheStrategy?: WriteCacheStrategyType
} }

View File

@ -25,7 +25,7 @@ export class ImageKnifeRequest {
requestState: ImageKnifeRequestState = ImageKnifeRequestState.PROGRESS requestState: ImageKnifeRequestState = ImageKnifeRequestState.PROGRESS
componentWidth: number = 0 componentWidth: number = 0
componentHeight: number = 0 componentHeight: number = 0
ImageKnifeOption: ImageKnifeOption imageKnifeOption: ImageKnifeOption
context: common.UIAbilityContext context: common.UIAbilityContext
ImageKnifeRequestCallback: ImageKnifeRequestCallback ImageKnifeRequestCallback: ImageKnifeRequestCallback
componentVersion: number = 0 componentVersion: number = 0
@ -36,7 +36,7 @@ export class ImageKnifeRequest {
height: number, height: number,
version: number, version: number,
ImageKnifeRequestCallback: ImageKnifeRequestCallback) { ImageKnifeRequestCallback: ImageKnifeRequestCallback) {
this.ImageKnifeOption = option this.imageKnifeOption = option
this.context = uIAbilityContext this.context = uIAbilityContext
this.componentWidth = width this.componentWidth = width
this.componentHeight = height this.componentHeight = height

View File

@ -27,9 +27,9 @@ export class DefaultJobQueue implements IJobQueue {
} }
add(request: ImageKnifeRequest): void { add(request: ImageKnifeRequest): void {
if (request.ImageKnifeOption.priority === undefined || request.ImageKnifeOption.priority === taskpool.Priority.MEDIUM) { if (request.imageKnifeOption.priority === undefined || request.imageKnifeOption.priority === taskpool.Priority.MEDIUM) {
this.normalQueue.add(request) this.normalQueue.add(request)
} else if (request.ImageKnifeOption.priority === taskpool.Priority.HIGH) { } else if (request.imageKnifeOption.priority === taskpool.Priority.HIGH) {
this.highQueue.add(request) this.highQueue.add(request)
} else { } else {
this.lowQueue.add(request) this.lowQueue.add(request)

View File

@ -31,7 +31,7 @@ export class FileCache {
maxSize: number = 0 maxSize: number = 0
path: string = "" path: string = ""
private lruCache: util.LRUCache<string, number> private lruCache: util.LRUCache<string, number>
private isInited: boolean = false static isInited: boolean = false
private context?: Context private context?: Context
readonly defaultMaxSize: number = 512; readonly defaultMaxSize: number = 512;
readonly defaultSize: number = 128; readonly defaultSize: number = 128;
@ -57,7 +57,7 @@ export class FileCache {
* 遍历缓存文件目录,初始化缓存 * 遍历缓存文件目录,初始化缓存
*/ */
public async initFileCache() { public async initFileCache() {
if (this.isInited) { if (FileCache.isInited) {
return return
} }
@ -100,7 +100,7 @@ export class FileCache {
} }
this.trimToSize(); this.trimToSize();
this.isInited = true FileCache.isInited = true
} }
// 添加缓存键值对,同时写文件 // 添加缓存键值对,同时写文件
@ -108,7 +108,7 @@ export class FileCache {
if (key == null || value == null) { if (key == null || value == null) {
throw new Error('key or value is invalid '); throw new Error('key or value is invalid ');
} }
if (!this.isInited) { if (!FileCache.isInited) {
return return
} }
@ -132,7 +132,7 @@ export class FileCache {
if (key == null || value == null) { if (key == null || value == null) {
throw new Error('key or value is invalid '); throw new Error('key or value is invalid ');
} }
if (!this.isInited) { if (!FileCache.isInited) {
return return
} }
@ -151,7 +151,7 @@ export class FileCache {
} }
get(key: string): ArrayBuffer | undefined { get(key: string): ArrayBuffer | undefined {
if (!this.isInited) { if (!FileCache.isInited) {
return return
} }
@ -167,7 +167,7 @@ export class FileCache {
if (key == null) { if (key == null) {
throw new Error('key is null,checking the parameter'); throw new Error('key is null,checking the parameter');
} }
if (!this.isInited) { if (!FileCache.isInited) {
return return
} }
@ -179,10 +179,10 @@ export class FileCache {
} }
async removeAll(): Promise<void> { async removeAll(): Promise<void> {
if (!this.isInited) { if (!FileCache.isInited) {
return return
} }
this.isInited = false FileCache.isInited = false
this.lruCache.clear() this.lruCache.clear()
this.currentMemory = 0; this.currentMemory = 0;
@ -191,7 +191,7 @@ export class FileCache {
await FileUtils.getInstance().deleteFile(this.path + filenames[i]) await FileUtils.getInstance().deleteFile(this.path + filenames[i])
} }
this.isInited = true FileCache.isInited = true
} }
size(): number { size(): number {
@ -199,7 +199,7 @@ export class FileCache {
} }
// 移除较少使用的缓存数据 // 移除较少使用的缓存数据
trimToSize(): void { private trimToSize(): void {
while (true) { while (true) {
if (this.currentMemory <= this.maxMemory || this.lruCache.isEmpty()) { if (this.currentMemory <= this.maxMemory || this.lruCache.isEmpty()) {
break break
@ -242,6 +242,9 @@ export class FileCache {
* @param value * @param value
*/ */
static saveFileCacheOnlyFile(context: Context, key: string, value: ArrayBuffer): boolean { static saveFileCacheOnlyFile(context: Context, key: string, value: ArrayBuffer): boolean {
if (!FileCache.isInited) {
return false
}
// 写文件 // 写文件
FileUtils.getInstance() FileUtils.getInstance()
.writeFileSync(context.cacheDir + FileUtils.SEPARATOR + FileCache.CACHE_FOLDER + FileUtils.SEPARATOR + key, value) .writeFileSync(context.cacheDir + FileUtils.SEPARATOR + FileCache.CACHE_FOLDER + FileUtils.SEPARATOR + key, value)