forked from floraachy/ImageKnife
843 lines
31 KiB
Plaintext
843 lines
31 KiB
Plaintext
/*
|
||
* Copyright (C) 2021 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 { DiskLruCache } from "../cache/DiskLruCache"
|
||
import { EngineKeyFactories } from "../cache/key/EngineKeyFactories"
|
||
import { EngineKeyInterface } from "../cache/key/EngineKeyInterface"
|
||
import { RequestOption, Size } from "../imageknife/RequestOption"
|
||
import { AsyncCallback } from "../imageknife/interface/AsyncCallback"
|
||
import { PlaceHolderManager } from "../imageknife/holder/PlaceHolderManager"
|
||
import { RetryHolderManager } from "../imageknife/holder/RetryHolderManager"
|
||
import { ErrorHolderManager } from "../imageknife/holder/ErrorHolderManager"
|
||
import { RequestManager } from "../imageknife/requestmanage/RequestManager"
|
||
import { NONE } from "../cache/diskstrategy/enum/NONE"
|
||
import { FileTypeUtil } from '../imageknife/utils/FileTypeUtil'
|
||
import { DownloadClient } from '../imageknife/networkmanage/DownloadClient'
|
||
import { IDataFetch } from '../imageknife/networkmanage/IDataFetch'
|
||
import { ParseResClient } from '../imageknife/resourcemanage/ParseResClient'
|
||
import { IResourceFetch } from '../imageknife/resourcemanage/IResourceFetch'
|
||
import { ImageKnifeData, ImageKnifeType } from '../imageknife/ImageKnifeData'
|
||
import { ImageKnifeGlobal } from '../imageknife/ImageKnifeGlobal'
|
||
import image from "@ohos.multimedia.image"
|
||
import { CompressBuilder } from "../imageknife/compress/CompressBuilder"
|
||
import { IDrawLifeCycle } from '../imageknife/interface/IDrawLifeCycle'
|
||
import { LogUtil } from '../imageknife/utils/LogUtil'
|
||
import { EasyLinkedHashMap } from './utils/base/EasyLinkedHashMap'
|
||
import { MethodMutex } from './utils/base/MethodMutex'
|
||
import worker from '@ohos.worker'
|
||
import common from '@ohos.app.ability.common'
|
||
import { MemoryLruCache } from '../cache/MemoryLruCache'
|
||
import { BusinessError } from '@ohos.base'
|
||
import taskpool from '@ohos.taskpool'
|
||
import { GIFFrame } from './utils/gif/GIFFrame'
|
||
import emitter from '@ohos.events.emitter';
|
||
|
||
import { MemoryCacheProxy } from './requestmanage/MemoryCacheProxy'
|
||
import { ObjectKey } from './ObjectKey'
|
||
import { TaskParams } from './TaskParams'
|
||
import { Constants } from './constants/Constants'
|
||
import { TransformUtils } from './transform/TransformUtils'
|
||
|
||
export class ImageKnife {
|
||
static readonly SEPARATOR: string = '/'
|
||
memoryCache: MemoryLruCache;
|
||
dataFetch: IDataFetch;
|
||
resourceFetch: IResourceFetch<ArrayBuffer>;
|
||
filesPath: string = ""; // data/data/包名/files目录
|
||
diskMemoryCache: DiskLruCache;
|
||
memoryCacheProxy: MemoryCacheProxy<string, ImageKnifeData> = new MemoryCacheProxy(new MemoryLruCache(100, 100 * 1024 * 1024));
|
||
headerMap: Map<string, Object> = new Map<string, Object>(); //定义全局map
|
||
placeholderCache: string = "placeholderCache"
|
||
runningMaps: EasyLinkedHashMap<string, RequestOption>;
|
||
pendingMaps: EasyLinkedHashMap<string, RequestOption>;
|
||
pausedMaps: EasyLinkedHashMap<string, RequestOption>;
|
||
isPaused: boolean = false;
|
||
mutex: MethodMutex = new MethodMutex();
|
||
fileTypeUtil: FileTypeUtil; // 通用文件格式辨别
|
||
diskCacheFolder: string = "ImageKnifeDiskCache"
|
||
defaultListener: AsyncCallback<ImageKnifeData> = {
|
||
callback: (err: string, data: ImageKnifeData) => {
|
||
return false
|
||
}
|
||
}; // 全局监听器
|
||
|
||
// gifWorker
|
||
gifWorker: worker.ThreadWorker | undefined = undefined;
|
||
defaultLifeCycle: IDrawLifeCycle | undefined = undefined;
|
||
// 开发者可配置全局缓存
|
||
engineKeyImpl: EngineKeyInterface;
|
||
|
||
private constructor() {
|
||
|
||
this.runningMaps = new EasyLinkedHashMap();
|
||
this.pendingMaps = new EasyLinkedHashMap();
|
||
this.pausedMaps = new EasyLinkedHashMap();
|
||
|
||
// 构造方法传入size 为保存文件个数
|
||
this.memoryCache = new MemoryLruCache(100, 100 * 1024 * 1024);
|
||
|
||
// 创建网络下载能力
|
||
this.dataFetch = new DownloadClient();
|
||
|
||
// 创建本地数据解析能力
|
||
this.resourceFetch = new ParseResClient();
|
||
|
||
// 初始化本地 文件保存
|
||
this.filesPath = (ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext).filesDir as string;
|
||
|
||
this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance()
|
||
.getHapContext() as common.UIAbilityContext);
|
||
|
||
// 通用文件格式识别初始化
|
||
this.fileTypeUtil = new FileTypeUtil();
|
||
|
||
this.engineKeyImpl = new EngineKeyFactories();
|
||
|
||
}
|
||
|
||
//全局设置请求头调用方法
|
||
addHeader(key: string, value: Object) {
|
||
this.headerMap.set(key, value);
|
||
}
|
||
|
||
deleteHeader(key: string) {
|
||
this.headerMap.delete(key);
|
||
}
|
||
|
||
getMemoryCache(): MemoryLruCache {
|
||
return this.memoryCache;
|
||
}
|
||
|
||
getMemoryCacheProxy(): MemoryCacheProxy<string, ImageKnifeData> {
|
||
return this.memoryCacheProxy;
|
||
}
|
||
|
||
public static with(context: Object): ImageKnifeGlobal {
|
||
// 存入hapContext;
|
||
let global: ImageKnifeGlobal = ImageKnifeGlobal.getInstance();
|
||
global.setHapContext(context)
|
||
|
||
// 初始化ImageKnife
|
||
if (!ImageKnife.sInstance) {
|
||
ImageKnife.sInstance = new ImageKnife();
|
||
}
|
||
|
||
// 存入ImageKnife
|
||
global.setImageKnife(ImageKnife.sInstance)
|
||
|
||
return global;
|
||
}
|
||
|
||
getDiskMemoryCache(): DiskLruCache {
|
||
return this.diskMemoryCache;
|
||
};
|
||
|
||
setDiskMemoryCache(diskLruCache: DiskLruCache) {
|
||
this.diskMemoryCache = diskLruCache;
|
||
};
|
||
|
||
getFileTypeUtil(): FileTypeUtil {
|
||
return this.fileTypeUtil;
|
||
}
|
||
|
||
getImageKnifeContext() {
|
||
return ImageKnifeGlobal.getInstance().getHapContext();
|
||
}
|
||
|
||
setMemoryCache(lrucache: MemoryLruCache) {
|
||
this.memoryCache = lrucache;
|
||
}
|
||
|
||
getDefaultListener() {
|
||
return this.defaultListener;
|
||
}
|
||
|
||
setGifWorker(worker: worker.ThreadWorker) {
|
||
this.gifWorker = worker
|
||
}
|
||
|
||
getGifWorker() {
|
||
return this.gifWorker;
|
||
}
|
||
|
||
getDefaultLifeCycle() {
|
||
return this.defaultLifeCycle;
|
||
}
|
||
|
||
setDefaultLifeCycle(viewLifeCycle: IDrawLifeCycle) {
|
||
this.defaultLifeCycle = viewLifeCycle;
|
||
}
|
||
|
||
setEngineKeyImpl(impl: EngineKeyInterface) {
|
||
this.engineKeyImpl = impl;
|
||
}
|
||
|
||
private static sInstance: ImageKnife;
|
||
|
||
setDefaultListener(newDefaultListener: AsyncCallback<ImageKnifeData>) {
|
||
this.defaultListener = newDefaultListener;
|
||
}
|
||
|
||
public compressBuilder(): CompressBuilder {
|
||
return new CompressBuilder();
|
||
}
|
||
|
||
|
||
// 设置缓存张数,缓存大小,单位字节
|
||
public setLruCacheSize(size: number, memory: number) {
|
||
if (this.memoryCache.map.size() <= 0) {
|
||
this.memoryCache = new MemoryLruCache(size, memory);
|
||
} else {
|
||
let newLruCache = new MemoryLruCache(size, memory);
|
||
this.memoryCache.foreachLruCache((value: ImageKnifeData, key: string, map: Object) => {
|
||
newLruCache.put(key, value);
|
||
})
|
||
this.memoryCache = newLruCache;
|
||
}
|
||
}
|
||
|
||
public replaceDataFetch(fetch: IDataFetch) {
|
||
this.dataFetch = fetch;
|
||
}
|
||
|
||
|
||
// 替代原来的DiskLruCache
|
||
public replaceDiskLruCache(size: number) {
|
||
this.diskMemoryCache.setMaxSize(size)
|
||
// if (this.diskMemoryCache.getCacheMap().size() <= 0) {
|
||
// this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext(), size);
|
||
// } else {
|
||
// let newDiskLruCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext(), size);
|
||
// this.diskMemoryCache.foreachDiskLruCache((value: string | ArrayBuffer, key: string, map: Object) => {
|
||
// newDiskLruCache.set(key, value);
|
||
// })
|
||
// this.diskMemoryCache = newDiskLruCache;
|
||
// }
|
||
}
|
||
|
||
// 预加载 resource资源一级缓存,string资源实现二级缓存
|
||
preload(request: RequestOption): void {
|
||
// 每个request 公共信息补充
|
||
request.setFilesPath(this.filesPath);
|
||
return this.parseSource(request);
|
||
}
|
||
|
||
// 暂停所有请求
|
||
async pauseRequests(): Promise<void> {
|
||
await this.mutex.lock(async () => {
|
||
this.isPaused = true;
|
||
|
||
// 将未删除的所有request [run pend] 放入 [pause]
|
||
LogUtil.log('dodo pauseRequests start1 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
|
||
|
||
// 将run存入pause
|
||
let runNode = this.runningMaps.getHead();
|
||
while (runNode) {
|
||
let request = runNode.value;
|
||
this.pausedMaps.put(request.uuid, request)
|
||
runNode = runNode.next;
|
||
}
|
||
this.runningMaps.clear();
|
||
LogUtil.log('dodo pauseRequests start2 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
|
||
|
||
let pendNode = this.pendingMaps.getHead();
|
||
while (pendNode) {
|
||
let request = pendNode.value;
|
||
this.pausedMaps.put(request.uuid, request)
|
||
pendNode = pendNode.next
|
||
}
|
||
this.pendingMaps.clear()
|
||
LogUtil.log('dodo pauseRequests start3 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
|
||
})
|
||
}
|
||
|
||
// 恢复所有被暂停的请求
|
||
async resumeRequests(): Promise<void> {
|
||
await this.mutex.lock(async () => {
|
||
this.isPaused = false;
|
||
LogUtil.log('dodo resumeRequest start1 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
|
||
// 重启了之后需要把paused 里面的所有request重新发送
|
||
let headNode = this.pausedMaps.getHead();
|
||
while (headNode) {
|
||
let request = headNode.value
|
||
this.loadCacheManager(request)
|
||
headNode = headNode.next
|
||
}
|
||
this.pausedMaps.clear()
|
||
LogUtil.log('dodo resumeRequest start1 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
|
||
})
|
||
}
|
||
|
||
// 删除 请求
|
||
remove(uuid: string) {
|
||
if (this.isPaused) {
|
||
this.pausedMaps.remove(uuid)
|
||
} else {
|
||
// TODO 哪些请求可以删除
|
||
this.pendingMaps.remove(uuid)
|
||
this.runningMaps.remove(uuid)
|
||
}
|
||
}
|
||
|
||
// 正常加载
|
||
call(request: RequestOption): void {
|
||
// 添加全局监听
|
||
if (this.defaultListener) {
|
||
request.addListener(this.defaultListener)
|
||
}
|
||
|
||
// 每个request 公共信息补充
|
||
request.setFilesPath(this.filesPath);
|
||
|
||
if (this.headerMap.size > 0) {
|
||
request.addHeaderMap(this.headerMap)
|
||
}
|
||
|
||
this.generateDataCacheKey(request)
|
||
// 首先执行占位图 解析任务
|
||
if (request.placeholderSrc) {
|
||
this.taskpoolLoadResource(request, Constants.PLACE_HOLDER);
|
||
}
|
||
// 其次执行重试占位图 解析任务
|
||
if (request.retryholderSrc) {
|
||
this.taskpoolLoadResource(request, Constants.RETRY_HOLDER);
|
||
}
|
||
|
||
// 最后解析错误占位图
|
||
if (request.errorholderSrc) {
|
||
this.taskpoolLoadResource(request, Constants.ERROR_HOLDER);
|
||
}
|
||
return this.parseSource(request);
|
||
}
|
||
|
||
generateDataCacheKey(request: RequestOption) {
|
||
let factories: EngineKeyInterface;
|
||
let cacheKey: string;
|
||
let transferKey: string;
|
||
let dataKey: string;
|
||
//设置全局缓存key
|
||
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);
|
||
if (request.placeholderSrc) {
|
||
let placeholderLoadKey = '';
|
||
if (typeof request.placeholderSrc == 'string') {
|
||
placeholderLoadKey = request.placeholderSrc;
|
||
} else {
|
||
placeholderLoadKey = JSON.stringify(request.placeholderSrc);
|
||
}
|
||
request.placeholderCacheKey = this.generateCacheKey(placeholderLoadKey, size, dontAnimateFlag, signature)
|
||
}
|
||
if (request.retryholderSrc) {
|
||
let retryholderLoadKey = '';
|
||
if (typeof request.retryholderSrc == 'string') {
|
||
retryholderLoadKey = request.retryholderSrc;
|
||
} else {
|
||
retryholderLoadKey = JSON.stringify(request.retryholderSrc);
|
||
}
|
||
request.retryholderCacheKey = this.generateCacheKey(retryholderLoadKey, size, dontAnimateFlag, signature)
|
||
}
|
||
if (request.errorholderSrc) {
|
||
let errorholderLoadKey = '';
|
||
if (typeof request.errorholderSrc == 'string') {
|
||
errorholderLoadKey = request.errorholderSrc;
|
||
} else {
|
||
errorholderLoadKey = JSON.stringify(request.errorholderSrc);
|
||
}
|
||
request.errorholderCacheKey = this.generateCacheKey(errorholderLoadKey, size, dontAnimateFlag, signature)
|
||
}
|
||
request.generateCacheKey = cacheKey;
|
||
request.generateResourceKey = transferKey;
|
||
request.generateDataKey = dataKey;
|
||
}
|
||
|
||
private generateCacheKey(loadkey: string, size: string, dontAnimateFlag: boolean, signature?: ObjectKey) {
|
||
let factories: EngineKeyInterface;
|
||
|
||
//设置全局缓存key
|
||
if (this.engineKeyImpl) {
|
||
factories = this.engineKeyImpl;
|
||
} else {
|
||
factories = new EngineKeyFactories();
|
||
}
|
||
return factories.generateMemoryCacheKey(loadkey, size, '', dontAnimateFlag, signature);
|
||
}
|
||
|
||
// 删除执行结束的running
|
||
removeRunning(request: RequestOption) {
|
||
if (this.isPaused) {
|
||
|
||
} else {
|
||
this.runningMaps.remove(request.uuid);
|
||
LogUtil.log('dodo runningMaps length =' + this.runningMaps.size())
|
||
let previousRequest = request;
|
||
this.loadNextPending(previousRequest);
|
||
}
|
||
}
|
||
|
||
// 执行相同key的pending队列请求
|
||
private keyEqualPendingToRun(nextPending: RequestOption) {
|
||
|
||
|
||
this.pendingMaps.remove(nextPending.uuid)
|
||
this.runningMaps.put(nextPending.uuid, nextPending);
|
||
|
||
// RequestManager.execute((nextPending as RequestOption), this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch)
|
||
this.taskpoolLoadResource(nextPending, Constants.MAIN_HOLDER);
|
||
|
||
}
|
||
|
||
private searchNextKeyToRun() {
|
||
// 其次则寻找pending中第一个和running不重复的requestOrKey
|
||
let pendingTailNode = this.pendingMaps.getTail();
|
||
while (pendingTailNode) {
|
||
let pendingRequest = pendingTailNode.value;
|
||
let hasEqual = false;
|
||
let runningTailNode = this.runningMaps.getTail();
|
||
while (runningTailNode) {
|
||
let runningRequest = runningTailNode.value;
|
||
if (this.requestOrKeyEqual(pendingRequest, runningRequest)) {
|
||
hasEqual = true;
|
||
break
|
||
}
|
||
runningTailNode = runningTailNode.prev;
|
||
}
|
||
|
||
if (!hasEqual) {
|
||
break;
|
||
}
|
||
pendingTailNode = pendingTailNode.prev;
|
||
}
|
||
|
||
if (pendingTailNode != null && pendingTailNode.value != null) {
|
||
let nextPending = pendingTailNode.value;
|
||
this.runningMaps.put(nextPending.uuid, nextPending)
|
||
this.pendingMaps.remove(nextPending.uuid)
|
||
//RequestManager.execute((nextPending as RequestOption), this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch)
|
||
this.taskpoolLoadResource(nextPending, Constants.MAIN_HOLDER);
|
||
}
|
||
}
|
||
|
||
|
||
// 加载下一个key的请求
|
||
private loadNextPending(request: RequestOption) {
|
||
let hasEqualRunning = false;
|
||
let tailNode = this.pendingMaps.getTail();
|
||
while (tailNode) {
|
||
if (this.requestOrKeyEqual(request, tailNode.value)) {
|
||
hasEqualRunning = true;
|
||
break;
|
||
}
|
||
tailNode = tailNode.prev;
|
||
}
|
||
|
||
if (hasEqualRunning) {
|
||
if (tailNode != null && tailNode.value != null) {
|
||
this.keyEqualPendingToRun(tailNode.value);
|
||
}
|
||
} else {
|
||
this.searchNextKeyToRun();
|
||
}
|
||
}
|
||
|
||
// 启动新线程 去磁盘取 去网络取
|
||
private loadCacheManager(request: RequestOption) {
|
||
if (this.isPaused) {
|
||
// 将当前request存入pausedMaps
|
||
this.pausedMaps.put(request.uuid, request);
|
||
} else {
|
||
// 正常逻辑
|
||
if (this.keyNotEmpty(request)) {
|
||
let hasRunningRequest = false;
|
||
// 遍历双向链表 从尾巴到头
|
||
let tailNode = this.runningMaps.getTail();
|
||
while (tailNode) {
|
||
if (this.requestOrKeyEqual(request, tailNode.value)) {
|
||
hasRunningRequest = true;
|
||
break;
|
||
}
|
||
tailNode = tailNode.prev
|
||
}
|
||
|
||
if (hasRunningRequest) {
|
||
this.pendingMaps.put(request.uuid, request);
|
||
|
||
|
||
} else {
|
||
this.runningMaps.put(request.uuid, request)
|
||
|
||
this.taskpoolLoadResource(request, Constants.MAIN_HOLDER);
|
||
}
|
||
}
|
||
else {
|
||
LogUtil.log("key没有生成无法进入存取!")
|
||
}
|
||
}
|
||
}
|
||
|
||
//组装任务参数
|
||
private assembleTaskParams(request: RequestOption, usageType: string) {
|
||
//图片变换方法无法直接传递到子线程,这里先把对象名和构造参数传递到子线程,然后在子线程中实例化变换方法
|
||
let transformations: string [][] = [];
|
||
if (usageType == Constants.MAIN_HOLDER) {
|
||
for (let i = 0; i < request.transformations.length; i++) {
|
||
transformations.push([request.transformations[i].getClassName(), request.transformations[i].getConstructorParams()])
|
||
}
|
||
}
|
||
let displayProgress = request.progressFunc ? true : false;
|
||
//將要传递到子线程的参数,放在一个json对象上,避免方法参数过多
|
||
let taskParams: TaskParams = {
|
||
headers: request.headers,
|
||
moduleContext: request.moduleContext,
|
||
transformations: transformations,
|
||
usageType: usageType,
|
||
displayProgress: displayProgress,
|
||
uuid: request.uuid,
|
||
dontAnimateFlag: request.dontAnimateFlag,
|
||
priority: request.priority,
|
||
generateCacheKey: request.generateCacheKey,
|
||
generateResourceKey: request.generateResourceKey,
|
||
generateDataKey: request.generateDataKey,
|
||
thumbSizeMultiplier: request.thumbSizeMultiplier,
|
||
thumbDelayTime: request.thumbDelayTime,
|
||
size: request.size,
|
||
onlyRetrieveFromCache: request.onlyRetrieveFromCache,
|
||
gpuEnabled: request.gpuEnabled,
|
||
signature: request.signature,
|
||
isCacheable: request.isCacheable,
|
||
diskMemoryCachePath: this.diskMemoryCache.getPath()
|
||
}
|
||
return taskParams;
|
||
}
|
||
|
||
//多线程请求加载资源
|
||
private taskpoolLoadResource(request: RequestOption, usageType: string) {
|
||
let mainCache = this.memoryCacheProxy.loadMemoryCache(request.generateCacheKey, request.isCacheable);
|
||
let placeholderCache = this.memoryCacheProxy.loadMemoryCache(request.placeholderCacheKey, request.isCacheable);
|
||
let retryholderCache = this.memoryCacheProxy.loadMemoryCache(request.retryholderCacheKey, request.isCacheable);
|
||
let errorholderCacheKey = this.memoryCacheProxy.loadMemoryCache(request.errorholderCacheKey, request.isCacheable);
|
||
|
||
if (usageType == Constants.PLACE_HOLDER && placeholderCache && !mainCache && !retryholderCache && !errorholderCacheKey) {
|
||
LogUtil.info("imageknife load placeholder from MemoryCache")
|
||
request.placeholderOnComplete(placeholderCache);
|
||
return;
|
||
} else if (usageType == Constants.RETRY_HOLDER && retryholderCache && !mainCache && !errorholderCacheKey) {
|
||
LogUtil.info("imageknife load retryholder from MemoryCache")
|
||
request.retryholderOnComplete(retryholderCache);
|
||
return;
|
||
} else if (usageType == Constants.ERROR_HOLDER && errorholderCacheKey && !mainCache) {
|
||
LogUtil.info("imageknife load errorholder from MemoryCache")
|
||
request.errorholderOnComplete(errorholderCacheKey);
|
||
return;
|
||
} else if (usageType == Constants.MAIN_HOLDER && mainCache) {
|
||
LogUtil.info("imageknife load mainsource from MemoryCache")
|
||
mainCache.waitSaveDisk = false;
|
||
request.loadComplete(mainCache);
|
||
return;
|
||
}
|
||
|
||
let taskParams: TaskParams = this.assembleTaskParams(request, usageType);
|
||
let loadSrcJson = JSON.stringify({
|
||
loadSrc: request.loadSrc,
|
||
placeholderSrc: request.placeholderSrc,
|
||
errorholderSrc: request.errorholderSrc,
|
||
retryholderSrc: request.retryholderSrc,
|
||
});
|
||
//使用taskpool多线程执行资源下载
|
||
let task: ESObject = new taskpool.Task(taskExecute, taskParams, loadSrcJson)
|
||
task.setTransferList([])
|
||
|
||
emitter.on(Constants.PROGRESS_EMITTER as ESObject, (data: ESObject) => {
|
||
if (request.progressFunc && data?.data?.value) {
|
||
let percent = data.data.value as number;
|
||
request.progressFunc.asyncSuccess(percent);
|
||
}
|
||
});
|
||
taskpool.execute(task,request.priority).then((data: ESObject) => {
|
||
if (usageType == Constants.PLACE_HOLDER) {
|
||
if ((typeof (data as PixelMap).isEditable) == 'boolean') {
|
||
let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, data as PixelMap);
|
||
request.placeholderOnComplete(imageKnifeData)
|
||
this.memoryCacheProxy.putValue(request.placeholderCacheKey,imageKnifeData)
|
||
} else {
|
||
request.placeholderOnError("request placeholder error")
|
||
}
|
||
} else if (usageType == Constants.RETRY_HOLDER) {
|
||
if ((typeof (data as PixelMap).isEditable) == 'boolean') {
|
||
let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, data as PixelMap);
|
||
request.retryholderOnComplete(imageKnifeData)
|
||
this.memoryCacheProxy.putValue(request.retryholderCacheKey,imageKnifeData)
|
||
} else {
|
||
request.retryholderOnError("request retryholder error")
|
||
}
|
||
} else if (usageType == Constants.ERROR_HOLDER) {
|
||
if ((typeof (data as PixelMap).isEditable) == 'boolean') {
|
||
let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, data as PixelMap);
|
||
request.errorholderOnComplete(imageKnifeData)
|
||
this.memoryCacheProxy.putValue(request.errorholderCacheKey,imageKnifeData)
|
||
} else {
|
||
request.errorholderOnError("request errorholder error")
|
||
}
|
||
} else {
|
||
if ((typeof (data as PixelMap).isEditable) == 'boolean') {
|
||
let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, data as PixelMap);
|
||
imageKnifeData.needSaveDisk = true;
|
||
request.loadComplete(imageKnifeData)
|
||
this.memoryCacheProxy.putValue(request.generateCacheKey,imageKnifeData)
|
||
this.setDiskCache(request)
|
||
} else if ((data as GIFFrame[]).length > 0) {
|
||
let imageKnifeData = ImageKnifeData.createImageGIFFrame(ImageKnifeType.GIFFRAME, data as GIFFrame[]);
|
||
imageKnifeData.needSaveDisk = true;
|
||
request.loadComplete(imageKnifeData)
|
||
this.memoryCacheProxy.putValue(request.generateCacheKey,imageKnifeData)
|
||
this.setDiskCache(request)
|
||
} else {
|
||
request.loadError("request resources error")
|
||
}
|
||
}
|
||
}).catch((err: BusinessError | string) => {
|
||
request.loadError(err)
|
||
})
|
||
|
||
}
|
||
|
||
private setDiskCache(request: RequestOption):void{
|
||
try {
|
||
// let diskMemoryCache = ImageKnifeGlobal.getInstance().getImageKnife()?.getDiskMemoryCache();
|
||
let dataArraybuffer: ArrayBuffer = DiskLruCache.getFileCacheByFile(this.diskMemoryCache.getPath() as string, request.generateDataKey) as ArrayBuffer;
|
||
//缓存原图片
|
||
if (dataArraybuffer) {
|
||
this.diskMemoryCache.setCacheMapAndSize(request.generateDataKey, dataArraybuffer);
|
||
}
|
||
//缓存变换后图片
|
||
let resourceArraybuffer: ArrayBuffer = DiskLruCache.getFileCacheByFile(this.diskMemoryCache.getPath() as string, request.generateResourceKey) as ArrayBuffer;
|
||
if (resourceArraybuffer) {
|
||
this.diskMemoryCache.setCacheMapAndSize(request.generateResourceKey, resourceArraybuffer);
|
||
}
|
||
|
||
} catch (e) {
|
||
LogUtil.error("imageknife DiskMemoryCache setDiskCache error :" + e.message);
|
||
}
|
||
}
|
||
|
||
private keyNotEmpty(request: RequestOption): boolean {
|
||
if (
|
||
request.generateCacheKey != null && request.generateCacheKey.length > 0 &&
|
||
request.generateDataKey != null && request.generateDataKey.length > 0 &&
|
||
request.generateResourceKey != null && request.generateResourceKey.length > 0
|
||
) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
private keyEqual(request1: RequestOption, request2: RequestOption): boolean {
|
||
// key 完全相等的情况
|
||
if (
|
||
request1.generateCacheKey == request2.generateCacheKey &&
|
||
request1.generateResourceKey == request2.generateResourceKey &&
|
||
request1.generateDataKey == request2.generateDataKey
|
||
) {
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
// 非严格校验模式,如果所有key相等我们认为一定相等, 如果请求类型是string 网络请求url或者uri相等 我们也认为该请求应该只发送一个即可,后续请求会去缓存或者磁盘读取
|
||
private requestOrKeyEqual(request1: RequestOption, request2: RequestOption): boolean {
|
||
// key 完全相等的情况
|
||
if (
|
||
request1.generateCacheKey == request2.generateCacheKey &&
|
||
request1.generateResourceKey == request2.generateResourceKey &&
|
||
request1.generateDataKey == request2.generateDataKey
|
||
) {
|
||
return true;
|
||
}
|
||
|
||
// 如果加载的是网络url或者是本地文件uri读取,那么loadSrc相同就认为是同一个请求
|
||
if (
|
||
typeof request1.loadSrc == 'string' && typeof request2.loadSrc == 'string' && request1.loadSrc == request2.loadSrc
|
||
) {
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
private parseSource(request: RequestOption): void {
|
||
if ((typeof (request.loadSrc as image.PixelMap).isEditable) == 'boolean') {
|
||
let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, request.loadSrc as PixelMap)
|
||
request.loadComplete(imageKnifeData);
|
||
} else if (typeof request.loadSrc == 'string') {
|
||
// 进入三级缓存模型
|
||
return this.loadCacheManager(request);
|
||
} else {
|
||
let res = request.loadSrc as Resource;
|
||
if (typeof res.id != 'undefined' && typeof res.type != 'undefined') {
|
||
// 进入三级缓存模型 本地资源不参与磁盘缓存
|
||
let none = new NONE();
|
||
request.diskCacheStrategy(none);
|
||
this.loadCacheManager(request);
|
||
} else {
|
||
LogUtil.error("输入参数有问题!")
|
||
}
|
||
}
|
||
}
|
||
|
||
prefetchToDiskCache(url: string): Promise<string> {
|
||
return new Promise((resolve, reject) => {
|
||
let key = this.engineKeyImpl.generateOriginalDiskCacheKey(url, undefined);
|
||
let cachedPath = this.getDiskMemoryCache().getFileToPath(key);
|
||
if (cachedPath == null || cachedPath == "" || cachedPath == undefined) {
|
||
let request = new RequestOption();
|
||
request.load(url)
|
||
.addListener({ callback: (err: BusinessError | string, data: ImageKnifeData) => {
|
||
if (err) {
|
||
reject(err)
|
||
} else {
|
||
let cachedPath = this.getDiskMemoryCache().getFileToPath(key);
|
||
resolve(cachedPath);
|
||
}
|
||
return false;
|
||
}
|
||
})
|
||
this.call(request);
|
||
} else {
|
||
resolve(cachedPath);
|
||
}
|
||
|
||
})
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* 加载资源子线程包含流程:网络请求资源->下载资源到本地->解码成ixelMap | GIFFrame[]->缓存到内存和磁盘
|
||
* @param taskParams:任务参数,JSON字符串类型
|
||
* @param headers:请求头
|
||
* @param moduleContext:模块上下文
|
||
* @returns
|
||
*/
|
||
@Concurrent
|
||
async function taskExecute(taskParams: TaskParams, loadSrcJson: string): Promise<PixelMap | GIFFrame[]> {
|
||
|
||
let emitProgressPercent = (percentValue: number) => {
|
||
let eventData: emitter.EventData = {
|
||
data: {
|
||
"value": percentValue,
|
||
}
|
||
};
|
||
emitter.emit(Constants.PROGRESS_EMITTER as ESObject, eventData)
|
||
}
|
||
|
||
let transformations = taskParams.transformations;
|
||
let usageType = taskParams.usageType;
|
||
let displayProgress = taskParams.displayProgress;
|
||
//子线程构造RequestOption对象
|
||
let newRequestOption = new RequestOption();
|
||
let loadSrcObj: object = JSON.parse(loadSrcJson);
|
||
newRequestOption.priority = taskParams.priority
|
||
newRequestOption.uuid = taskParams.uuid;
|
||
newRequestOption.loadSrc = loadSrcObj["loadSrc"] as string | PixelMap | Resource;
|
||
newRequestOption.dontAnimateFlag = taskParams.dontAnimateFlag;
|
||
newRequestOption.generateCacheKey = taskParams.generateCacheKey;
|
||
newRequestOption.generateResourceKey = taskParams.generateResourceKey;
|
||
newRequestOption.generateDataKey = taskParams.generateDataKey;
|
||
newRequestOption.thumbSizeMultiplier = taskParams.thumbSizeMultiplier;
|
||
newRequestOption.thumbDelayTime = taskParams.thumbDelayTime;
|
||
newRequestOption.size = taskParams.size;
|
||
newRequestOption.diskMemoryCachePath = taskParams.diskMemoryCachePath;
|
||
newRequestOption.placeholderSrc = loadSrcObj["placeholderSrc"] as PixelMap | Resource | undefined;
|
||
newRequestOption.errorholderSrc = loadSrcObj["errorholderSrc"] as PixelMap | Resource | undefined;
|
||
newRequestOption.retryholderSrc = loadSrcObj["retryholderSrc"] as PixelMap | Resource | undefined;
|
||
newRequestOption.onlyRetrieveFromCache = taskParams.onlyRetrieveFromCache;
|
||
newRequestOption.gpuEnabled = taskParams.gpuEnabled;
|
||
newRequestOption.headers = taskParams.headers;
|
||
newRequestOption.signature = taskParams.signature;
|
||
ImageKnifeGlobal.getInstance().setHapContext(taskParams.moduleContext as common.UIAbilityContext);
|
||
newRequestOption.moduleContext = taskParams.moduleContext;
|
||
newRequestOption.isCacheable = taskParams.isCacheable;
|
||
|
||
if (displayProgress) {
|
||
newRequestOption.addProgressListener({
|
||
asyncSuccess: (percentValue: number) => {
|
||
// 如果进度条百分比 未展示大小,展示其动画
|
||
emitProgressPercent(percentValue)
|
||
}
|
||
})
|
||
}
|
||
//如果是本地图片不作磁盘缓存
|
||
if (typeof newRequestOption.loadSrc !== 'string') {
|
||
let none = new NONE();
|
||
newRequestOption.diskCacheStrategy(none);
|
||
}
|
||
|
||
if (usageType == Constants.PLACE_HOLDER) {
|
||
let manager = new PlaceHolderManager<PixelMap>(newRequestOption);
|
||
return await new Promise<PixelMap>(manager.process);
|
||
} else if (usageType == Constants.RETRY_HOLDER) {
|
||
let manager = new RetryHolderManager<PixelMap>(newRequestOption);
|
||
return await new Promise<PixelMap>(manager.process);
|
||
} else if (usageType == Constants.ERROR_HOLDER) {
|
||
let manager = new ErrorHolderManager<PixelMap>(newRequestOption);
|
||
return await new Promise<PixelMap>(manager.process);
|
||
} else {
|
||
if (transformations) {
|
||
newRequestOption.setTransformations(TransformUtils.addTransformations(transformations))
|
||
}
|
||
let newDataFetch = new DownloadClient();
|
||
let newResourceFetch = new ParseResClient();
|
||
let manager = new RequestManager(newRequestOption, newDataFetch, newResourceFetch);
|
||
return await new Promise<PixelMap | GIFFrame[]>(manager.process);
|
||
}
|
||
}
|
||
|