1.修复概率出现jscrash问题 2.修复进度条问题

Signed-off-by: baofeng <baofeng6@h-partners.com>
This commit is contained in:
baofeng 2024-03-14 10:27:54 +08:00
parent 9b90cf9a6e
commit e271199f19
11 changed files with 177 additions and 132 deletions

View File

@ -1,5 +1,11 @@
## 2.1.2-rc.10
## 2.1.2-rc.11
- 修复概率出现jscrash问题
- 修复进度条问题
- 修复单帧gif图片加载失败
## 2.1.2-rc.10
- 修复部分gif图片识别成静态图
- 修复同一张图片发送多次请求
- 复用场景缓存到树aboutToRecycle清理定时器
## 2.1.2-rc.9

View File

@ -4,7 +4,7 @@
"name": "entry",
"description": "example description",
"repository": {},
"version": "2.1.2-rc.10",
"version": "2.1.2-rc.11",
"dependencies": {
"@ohos/libraryimageknife": "file:../sharedlibrary",
"@ohos/sharedlibrary2": "file:../sharedlibrary2",

View File

@ -29,7 +29,6 @@ struct TestManyNetImageLoadWithPage2 {
Column() {
Grid(this.scroller) {
LazyForEach(this.data, (item: Material, index) => {
if(index < 100) {
GridItem() {
Stack({ alignContent: Alignment.BottomEnd }) {
ImageKnifeComponent({
@ -39,12 +38,10 @@ struct TestManyNetImageLoadWithPage2 {
mainScaleType: ScaleType.CENTER_CROP,
placeholderScaleType: ScaleType.CENTER_CROP,
isCacheable: false,
// strategy: this.setting
strategy: this.setting
}
}).width('100%').height('100%')
//Grid组件并发加载大量图片会出现白块对比使用image库也会出现
//Image(item.thumbnail).width('100%').backgroundColor(Color.Blue).objectFit(ImageFit.Contain)
Text(index+"."+item.name)
Text(index + "." + item.name)
.fontSize(10)
.maxLines(1)
.fontColor(Color.White)
@ -53,18 +50,15 @@ struct TestManyNetImageLoadWithPage2 {
.width('100%')
.backgroundColor(Color.Orange)
}
}.width('45%').height(200)
}
}.width('45%').height(200)
}, (item: Material) => item.material_id)
}
.columnsTemplate('1fr 1fr')
.columnsGap(8)
.maxCount(8)
.rowsGap(10)
.width('100%')
.hitTestBehavior(HitTestMode.None)
.maxCount(10)
.cachedCount(3)
.cachedCount(1)
.height('100%')
}.margin({ top: 5 })
}
}

View File

@ -14,7 +14,7 @@
"main": "index.ets",
"repository": "https://gitee.com/openharmony-tpc/ImageKnife",
"type": "module",
"version": "2.1.2-rc.10",
"version": "2.1.2-rc.11",
"dependencies": {
"pako": "^2.1.0",
"@ohos/gpu_transform": "^1.0.0"

View File

@ -25,7 +25,7 @@ export class FileReader {
// 读取的长度
length: number = 0
// 读写stream
stream: fs.Stream | null = null
stream: fs.Stream
// 缓存buf
buf: ArrayBuffer = new ArrayBuffer(1)
@ -39,14 +39,9 @@ export class FileReader {
throw new Error('FileReader constructor path is null, checking the parameter')
return;
}
try {
this.stream = fs.createStreamSync(path, 'r+');
let stat = fs.statSync(path)
this.fileLength = stat.size
} catch (e) {
console.log(path);
console.log(e.toString());
}
this.stream = fs.createStreamSync(path, 'r+');
let stat = fs.statSync(path)
this.fileLength = stat.size
}
/**
@ -54,8 +49,8 @@ export class FileReader {
*/
readLine(): string {
let line = ''
while (this.length < this.fileLength) {
this.stream?.readSync(this.buf, { length: this.length })
while (this.stream && this.length < this.fileLength) {
this.stream?.readSync(this.buf, { offset: this.length })
this.length++
let temp = String.fromCharCode(...new Uint8Array(this.buf));
line = line + temp
@ -64,7 +59,7 @@ export class FileReader {
// 边界判断 首先拿到下一个字符判断是否是LF 如果是CRLF需要再向后挪一位
if (this.length < this.fileLength) {
let nextBuf = new ArrayBuffer(1)
this.stream?.readSync(nextBuf, { length: this.length })
this.stream?.readSync(nextBuf, { offset: this.length })
let nextTemp = String.fromCharCode(...new Uint8Array(nextBuf));
// 如果是CRLF 需要给当前length+1 向后挪一位
if (nextTemp == FileReader.LF) {

View File

@ -16,7 +16,7 @@
import { DiskLruCache } from "../cache/DiskLruCache"
import { EngineKeyFactories } from "../cache/key/EngineKeyFactories"
import { EngineKeyInterface } from "../cache/key/EngineKeyInterface"
import { RequestOption } from "../imageknife/RequestOption"
import { RequestOption, Size } from "../imageknife/RequestOption"
import { AsyncCallback } from "../imageknife/interface/AsyncCallback"
import { PlaceHolderManager } from "../imageknife/holder/PlaceHolderManager"
import { RetryHolderManager } from "../imageknife/holder/RetryHolderManager"
@ -42,17 +42,12 @@ import { MemoryLruCache } from '../cache/MemoryLruCache'
import { BusinessError } from '@kit.BasicServicesKit'
import { taskpool } from '@kit.ArkTS'
import { GIFFrame } from './utils/gif/GIFFrame'
import emitter from '@ohos.events.emitter';
import { MemoryCacheProxy } from './requestmanage/MemoryCacheProxy'
import { ObjectKey } from './ObjectKey'
export enum ResourceUsage {
PLACEHOLDER = 'placeholder',
RETRYHODLER = 'retryholder',
ERRORHOLDER = 'errorholder',
MAIN = 'main'
}
import { TaskParams } from './TaskParams'
import { Constants } from './constants/Constants'
export class ImageKnife {
static readonly SEPARATOR: string = '/'
@ -313,21 +308,21 @@ export class ImageKnife {
this.generateDataCacheKey(request)
// 首先执行占位图 解析任务
if (request.placeholderSrc) {
this.taskpoolLoadResource(request, ResourceUsage.PLACEHOLDER);
this.taskpoolLoadResource(request, Constants.PLACE_HOLDER);
}
// 其次执行重试占位图 解析任务
if (request.retryholderSrc) {
this.taskpoolLoadResource(request, ResourceUsage.RETRYHODLER);
this.taskpoolLoadResource(request, Constants.RETRY_HOLDER);
}
// 最后解析错误占位图
if (request.errorholderSrc) {
this.taskpoolLoadResource(request, ResourceUsage.ERRORHOLDER);
this.taskpoolLoadResource(request, Constants.ERROR_HOLDER);
}
return this.parseSource(request);
}
generateDataCacheKey(request: RequestOption){
generateDataCacheKey(request: RequestOption) {
let factories: EngineKeyInterface;
let cacheKey: string;
let transferKey: string;
@ -435,7 +430,7 @@ export class ImageKnife {
this.runningMaps.put(nextPending.uuid, nextPending);
// RequestManager.execute((nextPending as RequestOption), this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch)
this.taskpoolLoadResource(nextPending, ResourceUsage.MAIN);
this.taskpoolLoadResource(nextPending, Constants.MAIN_HOLDER);
}
@ -466,7 +461,7 @@ export class ImageKnife {
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, ResourceUsage.MAIN);
this.taskpoolLoadResource(nextPending, Constants.MAIN_HOLDER);
}
}
@ -518,7 +513,7 @@ export class ImageKnife {
} else {
this.runningMaps.put(request.uuid, request)
this.taskpoolLoadResource(request, ResourceUsage.MAIN);
this.taskpoolLoadResource(request, Constants.MAIN_HOLDER);
}
}
else {
@ -527,66 +522,98 @@ export class ImageKnife {
}
}
//多线程请求加载资源
private taskpoolLoadResource(request: RequestOption, usageType: ResourceUsage) {
let mainCache = this.memoryCacheProxy.loadMemoryCache(request.generateCacheKey, request.isCacheable);
let placeholderCache = this.memoryCacheProxy.loadMemoryCache(request.placeholderCacheKey, true);
let retryholderCache = this.memoryCacheProxy.loadMemoryCache(request.retryholderCacheKey, true);
let errorholderCacheKey = this.memoryCacheProxy.loadMemoryCache(request.errorholderCacheKey, true);
//组装任务参数
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,
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
}
return taskParams;
}
if (usageType == "placeholder" && placeholderCache && !mainCache && !retryholderCache && !errorholderCacheKey) {
//多线程请求加载资源
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 == "retryholder" && retryholderCache && !mainCache && !errorholderCacheKey) {
} else if (usageType == Constants.RETRY_HOLDER && retryholderCache && !mainCache && !errorholderCacheKey) {
LogUtil.info("imageknife load retryholder from MemoryCache")
request.retryholderOnComplete(retryholderCache);
return;
} else if (usageType == "errorholder" && errorholderCacheKey && !mainCache) {
} else if (usageType == Constants.ERROR_HOLDER && errorholderCacheKey && !mainCache) {
LogUtil.info("imageknife load errorholder from MemoryCache")
request.errorholderOnComplete(errorholderCacheKey);
return;
} else if (usageType == "main" && mainCache) {
} else if (usageType == Constants.MAIN_HOLDER && mainCache) {
LogUtil.info("imageknife load mainsource from MemoryCache")
mainCache.waitSaveDisk = false;
request.loadComplete(mainCache);
return;
}
//图片变换方法无法直接传递到子线程,这里先把对象名和构造参数传递到子线程,然后在子线程中实例化变换方法
let transformations: string [][] = [];
if (usageType == ResourceUsage.MAIN) {
for (let i = 0; i < request.transformations.length; i++) {
transformations.push([request.transformations[i].getClassName(), request.transformations[i].getConstructorParams()])
}
}
//將要传递到子线程的参数放在一个json对象上避免方法参数过多
let taskParams = JSON.stringify({
transformations: transformations,
request: request,
usageType: usageType
})
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 = new taskpool.Task(taskExecute, taskParams, request.headers, request.moduleContext as common.UIAbilityContext)
let task = new taskpool.Task(taskExecute, taskParams, loadSrcJson)
task.setTransferList([])
emitter.on(Constants.PROGRESS_EMITTER, (data) => {
if (request.progressFunc && data?.data?.value) {
let percent = data.data.value as number;
request.progressFunc.asyncSuccess(percent);
}
});
taskpool.execute(task).then((data) => {
if (usageType == "placeholder") {
if (usageType == Constants.PLACE_HOLDER) {
if ((typeof (data as PixelMap).isEditable) == 'boolean') {
let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, data as PixelMap);
request.placeholderOnComplete(imageKnifeData)
} else {
request.placeholderOnError("request placeholder error")
}
} else if (usageType == "retryholder") {
} 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)
} else {
request.retryholderOnError("request retryholder error")
}
} else if (usageType == "errorholder") {
} 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)
@ -684,51 +711,65 @@ export class ImageKnife {
* @returns
*/
@Concurrent
async function taskExecute(taskParams: string, headers: Map<string, Object>, moduleContext: common.UIAbilityContext): Promise<PixelMap | GIFFrame[]> {
async function taskExecute(taskParams: TaskParams, loadSrcJson: string): Promise<PixelMap | GIFFrame[]> {
// try {
let params: object = JSON.parse(taskParams);
let option = params["request"] as RequestOption;
let transformations = params["transformations"] as string [][];
let usageType = params["usageType"] as string;
let emitProgressPercent = (percentValue: number) => {
let eventData: emitter.EventData = {
data: {
"value": percentValue,
}
};
emitter.emit(Constants.PROGRESS_EMITTER, eventData)
}
let transformations = taskParams.transformations;
let usageType = taskParams.usageType;
let displayProgress = taskParams.displayProgress;
//子线程构造RequestOption对象
let newRequestOption = new RequestOption();
newRequestOption.uuid = option.uuid;
newRequestOption.loadSrc = option.loadSrc;
newRequestOption.dontAnimateFlag = option.dontAnimateFlag;
newRequestOption.generateCacheKey = option.generateCacheKey;
newRequestOption.generateResourceKey = option.generateResourceKey;
newRequestOption.generateDataKey = option.generateDataKey;
newRequestOption.thumbSizeMultiplier = option.thumbSizeMultiplier;
newRequestOption.thumbDelayTime = option.thumbDelayTime;
newRequestOption.size = option.size;
let loadSrcObj: object = JSON.parse(loadSrcJson);
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.placeholderSrc = option.placeholderSrc;
newRequestOption.errorholderSrc = option.errorholderSrc;
newRequestOption.retryholderSrc = option.retryholderSrc;
newRequestOption.onlyRetrieveFromCache = option.onlyRetrieveFromCache;
newRequestOption.gpuEnabled = option.gpuEnabled;
newRequestOption.headers = headers;
newRequestOption.signature = option.signature;
ImageKnifeGlobal.getInstance().setHapContext(moduleContext);
newRequestOption.moduleContext = moduleContext;
if (option.isCacheable != null && option.isCacheable != undefined) {
newRequestOption.isCacheable = option.isCacheable;
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 option.loadSrc !== 'string') {
if (typeof newRequestOption.loadSrc !== 'string') {
let none = new NONE();
newRequestOption.diskCacheStrategy(none);
}
if (usageType == "placeholder") {
if (usageType == Constants.PLACE_HOLDER) {
let manager = new PlaceHolderManager<PixelMap>(newRequestOption);
return await new Promise<PixelMap>(manager.process);
} else if (usageType == "retryholder") {
} else if (usageType == Constants.RETRY_HOLDER) {
let manager = new RetryHolderManager<PixelMap>(newRequestOption);
return await new Promise<PixelMap>(manager.process);
} else if (usageType == "errorholder") {
} else if (usageType == Constants.ERROR_HOLDER) {
let manager = new ErrorHolderManager<PixelMap>(newRequestOption);
return await new Promise<PixelMap>(manager.process);
} else {
@ -744,9 +785,5 @@ async function taskExecute(taskParams: string, headers: Map<string, Object>, mod
let manager = new RequestManager(newRequestOption, newDataFetch, newResourceFetch);
return await new Promise<PixelMap | GIFFrame[]>(manager.process);
}
// } catch (e) {
// console.log(e)
// return await new Promise<PixelMap | GIFFrame[]>(() => {
// });
// }
}

View File

@ -545,15 +545,6 @@ export class RequestOption {
}
}
}
//显示进度条
if (this.progressFunc) {
this.progressFunc.asyncSuccess(0);
setTimeout(() => {
}, 1000);
this.progressFunc.asyncSuccess(100);
}
//输出缓存相关内容和信息
if (this.allCacheInfoCallback) {
// 内存缓存

View File

@ -0,0 +1,23 @@
import { ObjectKey } from './ObjectKey';
import { Size } from '../imageknife/RequestOption'
import common from '@ohos.app.ability.common'
export class TaskParams {
headers: Map<string, Object> = new Map<string, Object>();
moduleContext?: common.UIAbilityContext = undefined;
transformations: string [][] = []
usageType: string = ''
displayProgress: boolean = false
uuid: string = '' // 唯一标识
dontAnimateFlag: boolean = false;
thumbSizeMultiplier: number = 0;
thumbDelayTime: number = 1000;
size: Size = { width: -1, height: -1 };
onlyRetrieveFromCache: boolean = false;
isCacheable: boolean = true;
gpuEnabled: boolean = false;
generateCacheKey: string = "";
generateResourceKey: string = "";
generateDataKey: string = "";
signature?: ObjectKey;
}

View File

@ -14,5 +14,10 @@
*/
export class Constants {
public static PROJECT_TAG: string= "ImageKnife_js"
public static PROJECT_TAG: string = "ImageKnife_js"
public static PROGRESS_EMITTER: string = "progressEmitter"
public static PLACE_HOLDER: string = "placeholder"
public static RETRY_HOLDER: string = "retryholder"
public static ERROR_HOLDER: string = "errorholder"
public static MAIN_HOLDER: string = "main"
}

View File

@ -13,18 +13,12 @@
* limitations under the License.
*/
import { IDataFetch } from '../networkmanage/IDataFetch'
import { IDataFetch } from '../networkmanage/IDataFetch'
import { RequestOption } from '../RequestOption'
import { SparkMD5 } from '../../3rd_party/sparkmd5/spark-md5'
import { FileUtils } from '../../cache/FileUtils'
import loadRequest from '@ohos.request';
import { LogUtil } from '../utils/LogUtil'
import { ImageKnifeGlobal } from '../ImageKnifeGlobal'
import common from '@ohos.app.ability.common'
import { BusinessError } from '@ohos.base'
import http from '@ohos.net.http'
// 数据加载器
class RequestData{
class RequestData {
receiveSize: number = 2000
totalSize: number = 2000
}
@ -47,7 +41,7 @@ export class HttpDownloadClient implements IDataFetch {
})
httpRequest.on('dataReceiveProgress', (data: RequestData) => {
// 下载进度
if(data != undefined && (typeof data.receiveSize == 'number') && (typeof data.totalSize == 'number') ) {
if (data != undefined && (typeof data.receiveSize == 'number') && (typeof data.totalSize == 'number')) {
let percent = Math.round(((data.receiveSize * 1.0) / (data.totalSize * 1.0)) * 100)
if (request.progressFunc) {
request.progressFunc.asyncSuccess(percent)
@ -64,7 +58,7 @@ export class HttpDownloadClient implements IDataFetch {
request.headers.forEach((value, key) => {
headerObj[key] = value
})
let promise = httpRequest.requestInStream(request.loadSrc as string, {
let promise = httpRequest.requestInStream(request.loadSrc as string, {
header: headerObj,
method: http.RequestMethod.GET,
expectDataType: http.HttpDataType.ARRAY_BUFFER,
@ -73,17 +67,17 @@ export class HttpDownloadClient implements IDataFetch {
usingProtocol: http.HttpProtocol.HTTP1_1, // 可选,协议类型默认值由系统自动指定
usingCache: false
});
await promise.then((data)=>{
if(data == 200) {
await promise.then((data) => {
if (data == 200) {
} else {
onError(`HttpDownloadClient has error, http code = ${data}`)
}
}).catch((err:Error)=>{
}).catch((err: Error) => {
onError(`HttpDownloadClient has error, http code = ${err}`)
})
} catch (err) {
onError('HttpDownloadClient catch err request uuid ='+request.uuid)
onError('HttpDownloadClient catch err request uuid =' + request.uuid)
}
}

View File

@ -6,6 +6,6 @@
"name": "imageknife",
"description": "example description",
"repository": {},
"version": "2.1.2-rc.10",
"version": "2.1.2-rc.11",
"dependencies": {}
}