ImageKnife/library/src/main/ets/ImageKnifeDispatcher.ets

437 lines
21 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 { ImageKnifeRequest, ImageKnifeRequestState } from './model/ImageKnifeRequest'
import { DefaultJobQueue } from './queue/DefaultJobQueue'
import { IJobQueue } from './queue/IJobQueue'
import List from '@ohos.util.List';
import LightWeightMap from '@ohos.util.LightWeightMap';
import { LogUtil } from './utils/LogUtil';
import { ImageKnife } from './ImageKnife';
import { ImageKnifeData, CacheStrategy } from './model/ImageKnifeData';
import image from '@ohos.multimedia.image';
import emitter from '@ohos.events.emitter';
import { Constants } from './utils/Constants';
import taskpool from '@ohos.taskpool';
import { FileTypeUtil } from './utils/FileTypeUtil';
import { IEngineKey } from './key/IEngineKey';
import { DefaultEngineKey } from './key/DefaultEngineKey';
import {
ImageKnifeRequestWithSource,
ImageKnifeRequestSource,
RequestJobResult,
RequestJobRequest
} from './model/ImageKnifeData'
import { BusinessError } from '@kit.BasicServicesKit';
import { ImageKnifeLoader } from './ImageKnifeLoader'
export class ImageKnifeDispatcher {
// 最大并发
private maxRequests: number = 8
// 排队队列
private jobQueue: IJobQueue = new DefaultJobQueue()
// 执行中的请求
executingJobMap: LightWeightMap<string, List<ImageKnifeRequestWithSource>> = new LightWeightMap();
// 开发者可配置全局缓存
private engineKey: IEngineKey = new DefaultEngineKey();
showFromMemomry(request: ImageKnifeRequest, imageSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource,isAnimator?: boolean): boolean {
LogUtil.log("ImageKnife_DataTime_showFromMemomry.start:" + request.imageKnifeOption.loadSrc + "requestSource=" + requestSource + " isAnimator=" + isAnimator)
let memoryCache: ImageKnifeData | undefined;
if ((typeof (request.imageKnifeOption.loadSrc as image.PixelMap).isEditable) == 'boolean') {
memoryCache = {
source: request.imageKnifeOption.loadSrc as image.PixelMap,
imageWidth: 0,
imageHeight: 0,
}
} else {
memoryCache = ImageKnife.getInstance()
.loadFromMemoryCache(this.engineKey.generateMemoryKey(imageSrc, requestSource, request.imageKnifeOption,isAnimator));
}
if (memoryCache !== undefined) {
// 画主图
if (request.requestState === ImageKnifeRequestState.PROGRESS) {
// 回调请求开始
if (requestSource === ImageKnifeRequestSource.SRC && request.imageKnifeOption.onLoadListener?.onLoadStart !== undefined) {
request.imageKnifeOption.onLoadListener.onLoadStart()
LogUtil.log("ImageKnife_DataTime_MemoryCache_onLoadStart:" + request.imageKnifeOption.loadSrc)
}
LogUtil.log("ImageKnife_DataTime_MemoryCache_showPixelMap.start:" + request.imageKnifeOption.loadSrc)
request.ImageKnifeRequestCallback?.showPixelMap(request.componentVersion, memoryCache.source, requestSource,memoryCache.imageAnimator)
LogUtil.log("ImageKnife_DataTime_MemoryCache_showPixelMap.end:" + request.imageKnifeOption.loadSrc)
if (requestSource == ImageKnifeRequestSource.SRC) {
request.requestState = ImageKnifeRequestState.COMPLETE
// 回调请求开结束
if (request.imageKnifeOption.onLoadListener?.onLoadSuccess !== undefined) {
request.imageKnifeOption.onLoadListener.onLoadSuccess(memoryCache.source,memoryCache)
LogUtil.log("ImageKnife_DataTime_MemoryCache_onLoadSuccess:" + request.imageKnifeOption.loadSrc)
}
} else if (requestSource == ImageKnifeRequestSource.ERROR_HOLDER) {
request.requestState = ImageKnifeRequestState.ERROR
}
}
LogUtil.log("ImageKnife_DataTime_showFromMemomry.end_hasmemory:" + request.imageKnifeOption.loadSrc)
return true
}
LogUtil.log("ImageKnife_DataTime_showFromMemomry.end_nomemory:" + request.imageKnifeOption.loadSrc)
return false
}
enqueue(request: ImageKnifeRequest,isAnimator?: boolean): void {
//1.内存有的话直接渲染
if (this.showFromMemomry(request, request.imageKnifeOption.loadSrc, ImageKnifeRequestSource.SRC,isAnimator)) {
return
}
// 2.内存获取占位图
if (request.imageKnifeOption.placeholderSrc !== undefined) {
if (this.showFromMemomry(request, request.imageKnifeOption.placeholderSrc, ImageKnifeRequestSource.PLACE_HOLDER)) {
request.drawPlayHolderSuccess = true
}
}
//3.判断是否要排队
if (this.executingJobMap.length > this.maxRequests) {
this.jobQueue.add(request)
return
}
this.executeJob(request,isAnimator)
}
executeJob(request: ImageKnifeRequest,isAnimator?: boolean): void {
LogUtil.log("ImageKnife_DataTime_executeJob.start:" + request.imageKnifeOption.loadSrc)
// 加载占位符
if (request.imageKnifeOption.placeholderSrc !== undefined && request.drawPlayHolderSuccess == false) {
this.getAndShowImage(request, request.imageKnifeOption.placeholderSrc, ImageKnifeRequestSource.PLACE_HOLDER)
}
// 加载主图
this.getAndShowImage(request, request.imageKnifeOption.loadSrc, ImageKnifeRequestSource.SRC,isAnimator)
LogUtil.log("ImageKnife_DataTime_executeJob.end:" + request.imageKnifeOption.loadSrc)
}
/**
* 获取和显示图片
*/
getAndShowImage(currentRequest: ImageKnifeRequest, imageSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource,isAnimator?: boolean): void {
LogUtil.log("ImageKnife_DataTime_getAndShowImage.start:" + currentRequest.imageKnifeOption.loadSrc)
let memoryKey: string = this.engineKey.generateMemoryKey(imageSrc, requestSource, currentRequest.imageKnifeOption,isAnimator)
let requestList: List<ImageKnifeRequestWithSource> | undefined = this.executingJobMap.get(memoryKey)
if (requestList == undefined) {
requestList = new List()
requestList.add({ request: currentRequest, source: requestSource })
this.executingJobMap.set(memoryKey, requestList)
} else {
requestList.add({ request: currentRequest, source: requestSource })
return
}
let isWatchProgress : boolean = false
// 回调请求开始
requestList.forEach((requestWithSource: ImageKnifeRequestWithSource) => {
if (requestWithSource.source === ImageKnifeRequestSource.SRC && requestWithSource.request.imageKnifeOption.onLoadListener?.onLoadStart !== undefined) {
requestWithSource.request.imageKnifeOption.onLoadListener?.onLoadStart()
LogUtil.log("ImageKnife_DataTime_getAndShowImage_onLoadStart:" + currentRequest.imageKnifeOption.loadSrc)
}
if (requestWithSource.request.imageKnifeOption.progressListener !== undefined && requestWithSource.source === ImageKnifeRequestSource.SRC) {
isWatchProgress = true
}
});
let src: string | number = ""
let moduleName: string = ""
let resName: string = ""
if((imageSrc as Resource).id != undefined) {
moduleName = (imageSrc as Resource).moduleName
src = (imageSrc as Resource).id
if(src == -1) {
resName = (imageSrc as Resource).params![0]
}
} else if(typeof imageSrc == "string") {
src = imageSrc
}
let request: RequestJobRequest = {
context: currentRequest.context,
src: src,
headers: currentRequest.imageKnifeOption.headerOption,
allHeaders: currentRequest.headers,
componentWidth:currentRequest.componentWidth,
componentHeight:currentRequest.componentHeight,
customGetImage: currentRequest.imageKnifeOption.customGetImage,
onlyRetrieveFromCache: currentRequest.imageKnifeOption.onlyRetrieveFromCache,
transformation: currentRequest.imageKnifeOption.transformation,
writeCacheStrategy: ImageKnife.getInstance()
.isFileCacheInit() ? currentRequest.imageKnifeOption.writeCacheStrategy : CacheStrategy.Memory, // 未初始化文件缓存时,不写文件缓存
engineKey: this.engineKey,
signature: currentRequest.imageKnifeOption.signature,
requestSource: requestSource,
isWatchProgress: isWatchProgress,
memoryKey: memoryKey,
fileCacheFolder: ImageKnife.getInstance().getFileCache()?.getCacheFolder(),
isAnimator:isAnimator,
moduleName: moduleName == "" ? undefined : moduleName,
resName: resName == "" ? undefined : resName
}
if(request.customGetImage == undefined) {
request.customGetImage = ImageKnife.getInstance().getCustomGetImage()
}
if (ImageKnife.getInstance().isRequestInSubThread){
// 启动线程下载和解码主图
LogUtil.log("ImageKnife_DataTime_getAndShowImage_Task.start:" + currentRequest.imageKnifeOption.loadSrc)
let task = new taskpool.Task(requestJob, request)
LogUtil.log("ImageKnife_DataTime_getAndShowImage_Task.end:" + currentRequest.imageKnifeOption.loadSrc)
if (isWatchProgress){
emitter.on(Constants.PROGRESS_EMITTER + memoryKey, (data) => {
this.progressCallBack(requestList! , data?.data?.value as number)
});
}
LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.start(subthread):" + currentRequest.imageKnifeOption.loadSrc)
taskpool.execute(task).then((res: Object) => {
this.doTaskCallback(res as RequestJobResult | undefined, requestList!, currentRequest, memoryKey, imageSrc, requestSource,isAnimator);
if (isWatchProgress){
emitter.off(Constants.PROGRESS_EMITTER + memoryKey)
}
LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.end:"+currentRequest.imageKnifeOption.loadSrc)
LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:"+currentRequest.imageKnifeOption.loadSrc)
}).catch((err: BusinessError) => {
LogUtil.error("Fail to requestJob in sub thread src=" + imageSrc + " err=" + err)
LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:" + currentRequest.imageKnifeOption.loadSrc)
if (isWatchProgress){
emitter.off(Constants.PROGRESS_EMITTER + memoryKey)
}
this.executingJobMap.remove(memoryKey);
this.dispatchNextJob();
})
} else { //主线程请求
LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.start(mainthread):" + currentRequest.imageKnifeOption.loadSrc)
requestJob(request, requestList).then((res: RequestJobResult | undefined) => {
this.doTaskCallback(res, requestList!, currentRequest, memoryKey, imageSrc, requestSource,isAnimator);
LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.end:"+currentRequest.imageKnifeOption.loadSrc)
LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:"+currentRequest.imageKnifeOption.loadSrc)
}).catch((err: BusinessError) => {
LogUtil.error("Fail to requestJob in main thread src=" + imageSrc + " err=" + err)
LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:" + currentRequest.imageKnifeOption.loadSrc)
this.executingJobMap.remove(memoryKey);
this.dispatchNextJob();
})
}
}
/**
* 回调下载进度
* @param requestList 请求列表
* @param data 进度
*/
private progressCallBack(requestList:List<ImageKnifeRequestWithSource>, data: number) {
for (let i = 0; i < requestList.length; i++) {
let requestWithSource:ImageKnifeRequestWithSource = requestList[i]
if (requestWithSource.request.imageKnifeOption.progressListener !== undefined && requestWithSource.source === ImageKnifeRequestSource.SRC) {
requestWithSource.request.imageKnifeOption.progressListener(data)
}
}
}
private doTaskCallback(requestJobResult: RequestJobResult | undefined, requestList: List<ImageKnifeRequestWithSource> ,
currentRequest: ImageKnifeRequest, memoryKey: string, imageSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource,isAnimator?: boolean):void {
LogUtil.log("ImageKnife_DataTime_getAndShowImage_CallBack.start:"+currentRequest.imageKnifeOption.loadSrc)
if (requestJobResult === undefined){
return
}
let pixelmap = requestJobResult.pixelMap;
if (pixelmap === undefined) {
LogUtil.log("ImageKnife_DataTime_getAndShowImage_CallBack.pixelmap undefined:"+currentRequest.imageKnifeOption.loadSrc)
requestList.forEach((requestWithSource: ImageKnifeRequestWithSource) => {
// 回调请求失败
if (requestWithSource.source === ImageKnifeRequestSource.SRC &&
requestWithSource.request.imageKnifeOption.onLoadListener?.onLoadFailed !== undefined &&
requestJobResult.loadFail) {
requestWithSource.request.imageKnifeOption.onLoadListener.onLoadFailed(requestJobResult.loadFail);
LogUtil.log("ImageKnife_DataTime_getAndShowImage_onLoadFailed:"+currentRequest.imageKnifeOption.loadSrc)
}
if (requestWithSource.source === ImageKnifeRequestSource.SRC &&
requestWithSource.request.imageKnifeOption.errorholderSrc !== undefined) {
if (this.showFromMemomry(requestWithSource.request, requestWithSource.request.imageKnifeOption.errorholderSrc,
ImageKnifeRequestSource.ERROR_HOLDER) === false) {
this.getAndShowImage(requestWithSource.request, requestWithSource.request.imageKnifeOption.errorholderSrc,
ImageKnifeRequestSource.ERROR_HOLDER);
}
}
});
this.executingJobMap.remove(memoryKey);
this.dispatchNextJob();
return;
}
// 保存文件缓存
if (requestJobResult.bufferSize > 0 && currentRequest.imageKnifeOption.writeCacheStrategy !== CacheStrategy.Memory) {
LogUtil.log("ImageKnife_DataTime_getAndShowImage_saveWithoutWriteFile.start:"+currentRequest.imageKnifeOption.loadSrc)
ImageKnife.getInstance().saveWithoutWriteFile(requestJobResult.fileKey, requestJobResult.bufferSize);
LogUtil.log("ImageKnife_DataTime_getAndShowImage_saveWithoutWriteFile.end:"+currentRequest.imageKnifeOption.loadSrc)
}
let ImageKnifeData: ImageKnifeData = {
source: pixelmap!,
imageWidth: requestJobResult.size == undefined ? 0 : requestJobResult.size.width,
imageHeight: requestJobResult.size == undefined ? 0 : requestJobResult.size.height,
type:requestJobResult.type
};
if(requestJobResult.pixelMapList != undefined) {
let imageAnimator: Array<ImageFrameInfo> = []
requestJobResult.pixelMapList.forEach((item,index)=>{
imageAnimator.push({
src:requestJobResult.pixelMapList![index],
duration:requestJobResult.delayList![index]
})
})
ImageKnifeData.imageAnimator = imageAnimator
}
// 保存内存缓存
if (currentRequest.imageKnifeOption.writeCacheStrategy !== CacheStrategy.File) {
LogUtil.log("ImageKnife_DataTime_getAndShowImage_saveMemoryCache.start:"+currentRequest.imageKnifeOption.loadSrc)
ImageKnife.getInstance()
.saveMemoryCache(this.engineKey.generateMemoryKey(imageSrc, requestSource, currentRequest.imageKnifeOption,isAnimator),
ImageKnifeData);
LogUtil.log("ImageKnife_DataTime_getAndShowImage_saveMemoryCache.end:"+currentRequest.imageKnifeOption.loadSrc)
}
if (requestList !== undefined) {
// key相同的request一起绘制
requestList.forEach((requestWithSource: ImageKnifeRequestWithSource) => {
if (requestWithSource.request.requestState !== ImageKnifeRequestState.DESTROY) {
// 画主图
if (requestWithSource.source === ImageKnifeRequestSource.SRC ||
requestWithSource.source === ImageKnifeRequestSource.ERROR_HOLDER
|| (requestWithSource.source === ImageKnifeRequestSource.PLACE_HOLDER &&
requestWithSource.request.requestState === ImageKnifeRequestState.PROGRESS)) {
LogUtil.log("ImageKnife_DataTime_getAndShowImage_showPixelMap.start:"+currentRequest.imageKnifeOption.loadSrc)
requestWithSource.request.ImageKnifeRequestCallback.showPixelMap(requestWithSource.request.componentVersion,
ImageKnifeData.source, requestWithSource.source,ImageKnifeData.imageAnimator);
LogUtil.log("ImageKnife_DataTime_getAndShowImage_showPixelMap.end:"+currentRequest.imageKnifeOption.loadSrc)
}
if (requestWithSource.source == ImageKnifeRequestSource.SRC) {
requestWithSource.request.requestState = ImageKnifeRequestState.COMPLETE;
if (requestWithSource.request.imageKnifeOption.onLoadListener &&
requestWithSource.request.imageKnifeOption.onLoadListener.onLoadSuccess) {
// 回调请求成功
requestWithSource.request.imageKnifeOption.onLoadListener.onLoadSuccess(ImageKnifeData.source,ImageKnifeData);
LogUtil.log("ImageKnife_DataTime_getAndShowImage_onLoadSuccess:"+currentRequest.imageKnifeOption.loadSrc)
}
} else if (requestWithSource.source == ImageKnifeRequestSource.ERROR_HOLDER) {
requestWithSource.request.requestState = ImageKnifeRequestState.ERROR;
}
} else {
if (requestWithSource.source == ImageKnifeRequestSource.SRC && requestWithSource.request.imageKnifeOption.onLoadListener?.onLoadCancel) {
// 回调请求成功
requestWithSource.request.imageKnifeOption.onLoadListener.onLoadCancel("component has destroyed")
}
}
});
this.executingJobMap.remove(memoryKey);
this.dispatchNextJob();
} else {
LogUtil.log("error: no requestlist need to draw for key = " + memoryKey);
}
LogUtil.log("ImageKnife_DataTime_getAndShowImage_CallBack.end:"+currentRequest.imageKnifeOption.loadSrc)
}
dispatchNextJob() {
LogUtil.log("ImageKnife_DataTime_dispatchNextJob.start")
while (true) {
let request = this.jobQueue.pop()
if (request === undefined) {
LogUtil.log("ImageKnife_DataTime_dispatchNextJob.end:no any job")
break // 队列已无任务
}
else if (request.requestState === ImageKnifeRequestState.PROGRESS) {
LogUtil.log("ImageKnife_DataTime_dispatchNextJob.start executeJob:" + request.imageKnifeOption.loadSrc)
this.executeJob(request)
LogUtil.log("ImageKnife_DataTime_dispatchNextJob.end executeJob:" + request.imageKnifeOption.loadSrc)
break
}else if (request.requestState == ImageKnifeRequestState.DESTROY && request.imageKnifeOption.onLoadListener?.onLoadCancel) {
request.imageKnifeOption.onLoadListener.onLoadCancel("component has destroyed")
}
}
}
setMaxRequests(concurrency: number): void {
if (concurrency > 0) {
this.maxRequests = concurrency
}
}
setEngineKeyImpl(impl: IEngineKey): void {
this.engineKey = impl;
}
getEngineKeyImpl(): IEngineKey {
return this.engineKey;
}
}
/**
* 通过taskpool 二级缓存,下载/读取本地文件,编解码
* @param context
* @param src
* @returns
*/
@Concurrent
async function requestJob(request: RequestJobRequest, requestList?: List<ImageKnifeRequestWithSource>): Promise<RequestJobResult | undefined> {
LogUtil.log("ImageKnife_DataTime_requestJob.start:" + request.src + " requestSource=" + request.requestSource)
let src = typeof request.src == "number" ? request.resName != undefined ? request.resName : request.src + "" : request.src
// 生成文件缓存key
let fileKey = request.engineKey.generateFileKey(src, request.signature, request.isAnimator)
//获取图片资源
let resBuf: ArrayBuffer
try {
LogUtil.log("ImageKnife_DataTime_requestJob.getImageArrayBuffer.start:" + request.src)
resBuf = await ImageKnifeLoader.getImageArrayBuffer(request, requestList, fileKey)
LogUtil.log("ImageKnife_DataTime_requestJob.getImageArrayBuffer.end:" + request.src)
} catch (error) {
LogUtil.error("ImageKnife_DataTime_requestJob.end: getImageArrayBuffer error " + request.src + " err=" + error)
return ImageKnifeLoader.makeEmptyResult(error)
}
// 获取图片类型
let typeValue = new FileTypeUtil().getFileType(resBuf);
if(typeValue == null) {
LogUtil.log("ImageKnife_DataTime_requestJob.end: getFileType is null " + request.src)
return ImageKnifeLoader.makeEmptyResult("request is not a valid image source")
}
// 解析图片
LogUtil.log("ImageKnife_DataTime_requestJob.parseImage.start:" + request.src)
let result: RequestJobResult = await ImageKnifeLoader.parseImage(resBuf, typeValue, fileKey, request)
LogUtil.log("ImageKnife_DataTime_requestJob.parseImage.end:" + request.src)
// 图形变化
if (request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined && result?.pixelMap !== undefined && typeof result.pixelMap !== 'string') {
LogUtil.log("ImageKnife_DataTime_requestJob.transform.start:" + request.src)
result.pixelMap = await request.transformation?.transform(request.context, result.pixelMap, request.componentWidth, request.componentHeight);
LogUtil.log("ImageKnife_DataTime_requestJob.transform.end:" + request.src)
}
LogUtil.log("ImageKnife_DataTime_requestJob.end:" + request.src)
return result
}