From 9ff235a856b0c66d281727fc30a311532189b60a Mon Sep 17 00:00:00 2001 From: zhang_hanyong Date: Tue, 23 Apr 2024 15:04:21 +0800 Subject: [PATCH 1/4] =?UTF-8?q?ImageKnife=E5=8A=A0=E8=BD=BD=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E6=94=AF=E6=8C=81=E8=87=AA=E5=AE=9A=E4=B9=89=E7=BD=91?= =?UTF-8?q?=E7=BB=9C=E6=A0=88=E5=92=8C=E5=9B=BE=E7=89=87=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: zhang_hanyong --- CHANGELOG.md | 1 + .../ets/pages/imageknifeTestCaseIndex.ets | 8 + .../testCustomDataFetchClientWithPage.ets | 237 ++++++++++++++++++ .../resources/base/profile/main_pages.json | 3 +- library/index.ets | 2 + .../ets/components/imageknife/ImageKnife.ets | 2 + .../imageknife/ImageKnifeComponent.ets | 4 +- .../imageknife/ImageKnifeOption.ets | 4 + .../components/imageknife/RequestOption.ets | 3 + .../ets/components/imageknife/TaskParams.ets | 2 + .../networkmanage/CustomDataFetchClient.ets | 113 +++++++++ .../networkmanage/DownloadClient.ets | 17 +- .../networkmanage/HttpDownloadClient.ets | 1 + sharedlibrary/src/main/ets/Index.ets | 2 + 14 files changed, 392 insertions(+), 7 deletions(-) create mode 100644 entry/src/main/ets/pages/testCustomDataFetchClientWithPage.ets create mode 100644 library/src/main/ets/components/imageknife/networkmanage/CustomDataFetchClient.ets diff --git a/CHANGELOG.md b/CHANGELOG.md index 3260d3f..d8b8b3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - imageKnife支持heic测试demo独立页面展示 - drawLifeCycle支持gif图 - ImageKnife支持根据自定义key获取已缓存的图片 +- ImageKnife加载图片支持自定义网络栈和图片加载组件 ## 2.1.2-rc.12 - 新增gif播放次数功能 diff --git a/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets b/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets index bb3d5ec..6bb39fb 100644 --- a/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets +++ b/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets @@ -404,6 +404,14 @@ struct IndexFunctionDemo { router.pushUrl({ url: 'pages/testImageKnifeNetPlaceholder' }); }).margin({ top: 5, left: 3 }) }.width('100%').height(60).backgroundColor(Color.Pink) + + Text('测试加载图片自定义网络栈').fontSize(15) + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Button('加载图片自定义网络栈') + .onClick(() => { + router.pushUrl({ url: 'pages/testCustomDataFetchClientWithPage' }); + }).margin({ top: 5, left: 3 }) + }.width('100%').height(60).backgroundColor(Color.Pink) } } .width('100%') diff --git a/entry/src/main/ets/pages/testCustomDataFetchClientWithPage.ets b/entry/src/main/ets/pages/testCustomDataFetchClientWithPage.ets new file mode 100644 index 0000000..c2a82f0 --- /dev/null +++ b/entry/src/main/ets/pages/testCustomDataFetchClientWithPage.ets @@ -0,0 +1,237 @@ +/* + * 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 { + CustomDataFetchClient, + DataFetchResult, + DownloadClient, + ImageKnifeComponent, + ImageKnifeGlobal, + ImageKnifeOption, + LogUtil, + ScaleType +} from '@ohos/libraryimageknife'; + +import http from '@ohos.net.http'; + +class CommonDataSource implements IDataSource { + private dataArray: T[] = [] + private listeners: DataChangeListener[] = [] + + constructor(element: []) { + this.dataArray = element + } + + public getData(index: number) { + return this.dataArray[index] + } + + public totalCount(): number { + return this.dataArray.length + } + + public addData(index: number, data: T[]): void { + this.dataArray = this.dataArray.concat(data) + this.notifyDataAdd(index) + } + + unregisterDataChangeListener(listener: DataChangeListener): void { + const pos = this.listeners.indexOf(listener); + if (pos >= 0) { + this.listeners.splice(pos, 1); + } + } + + registerDataChangeListener(listener: DataChangeListener): void { + if (this.listeners.indexOf(listener) < 0) { + this.listeners.push(listener) + } + } + + notifyDataAdd(index: number): void { + this.listeners.forEach((listener: DataChangeListener) => { + listener.onDataAdd(index) + }) + } +} + +@Entry +@Component +struct TestCustomDataFetchClientWithPage { + @State hotCommendList: CommonDataSource = new CommonDataSource([]) + @State singleImageKnifeOption: ImageKnifeOption = + { + loadSrc: $r('app.media.icon'), + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + }; + @State isSingleImageVisible: boolean = true; + @State isAllImageVisible: boolean = false; + @State isCustom: boolean = false; + private data: Array = [ + "http://img2.xkhouse.com/bbs/hfhouse/data/attachment/forum/corebbs/2009-11/2009113011534566298.jpg", + "http://a.hiphotos.baidu.com/image/pic/item/e824b899a9014c087eb617650e7b02087af4f464.jpg" + ] + private addData: Array = [ + "http://c.hiphotos.baidu.com/image/pic/item/9c16fdfaaf51f3de1e296fa390eef01f3b29795a.jpg", + "http://h.hiphotos.baidu.com/image/pic/item/902397dda144ad340668b847d4a20cf430ad851e.jpg" + + ] + private cancelData: Array = [ + "http://b.hiphotos.baidu.com/image/pic/item/359b033b5bb5c9ea5c0e3c23d139b6003bf3b374.jpg", + "http://a.hiphotos.baidu.com/image/pic/item/8d5494eef01f3a292d2472199d25bc315d607c7c.jpg" + ] + + aboutToAppear(): void { + this.hotCommendList.addData(this.hotCommendList.totalCount(), this.data) + LogUtil.log('TestCustomDataFetch about to appear.') + } + + build() { + Scroll() { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + + Button("单个图片").margin(16).onClick(() => { + LogUtil.log('TestCustomDataFetch click single.'); + this.isSingleImageVisible = true; + this.isAllImageVisible = false; + ImageKnifeGlobal.getInstance().getImageKnife()?.replaceDataFetch(new CustomDataFetchClient()); + + this.singleImageKnifeOption = { + loadSrc: 'http://e.hiphotos.baidu.com/image/pic/item/4e4a20a4462309f7e41f5cfe760e0cf3d6cad6ee.jpg', + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + customGetImage: custom + } + }) + Button("全部图片").margin(16).onClick(() => { + LogUtil.log('TestCustomDataFetch click all.'); + this.isSingleImageVisible = false; + this.isAllImageVisible = true; + + ImageKnifeGlobal.getInstance().getImageKnife()?.replaceDataFetch(new CustomDataFetchClient()); + this.hotCommendList.addData(this.hotCommendList.totalCount(), this.addData) + + }) + + Button("取消自定义全部图片").margin(16).onClick(() => { + LogUtil.log('TestCustomDataFetch click cancel.'); + this.isSingleImageVisible = false; + this.isAllImageVisible = true; + + ImageKnifeGlobal.getInstance().getImageKnife()?.replaceDataFetch(new DownloadClient()); + this.hotCommendList.addData(this.hotCommendList.totalCount(), this.cancelData) + }) + } + + // 单个图片使用自定义网络栈 + ImageKnifeComponent({ imageKnifeOption: this.singleImageKnifeOption }) + .width(200) + .height(200) + .margin({ top: 50 }) + .visibility(this.isSingleImageVisible ? Visibility.Visible : Visibility.None) + + // 全部图片使用自定义网络栈 + Column() { + Grid() { + LazyForEach(this.hotCommendList, (item: string) => { + GridItem() { + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: item, + placeholderSrc: $r('app.media.icon'), + errorholderSrc: $r('app.media.icon_failed'), + placeholderScaleType: ScaleType.CENTER_CROP, + mainScaleType: ScaleType.CENTER_CROP, + } + }).width('100%').height('100%') + }.width('40%').height(200) + }, (item: string) => JSON.stringify(item)) + } + .columnsTemplate('1fr 1fr') + .columnsGap(8) + .rowsGap(10) + .width('100%') + .hitTestBehavior(HitTestMode.None) + .maxCount(10) + }.margin({ top: 5 }) + .visibility(this.isAllImageVisible ? Visibility.Visible : Visibility.None) + } + } + .width('100%') + .height('100%') + } +} + +@Concurrent +async function custom(context: Context, loadSrc: string): Promise { + let result: DataFetchResult = new DataFetchResult(); + try { + let arrayBuffers = new Array(); + let httpRequest = http.createHttp() + httpRequest.on('headersReceive', (header: Object) => { + // 跟服务器连接成功准备下载 + }) + httpRequest.on('dataReceive', (data: ArrayBuffer) => { + // 下载数据流多次返回 + arrayBuffers.push(data); + }) + httpRequest.on('dataEnd', () => { + // 下载完毕 + }) + + const resultCode = await httpRequest.requestInStream(loadSrc as string, + { + method: http.RequestMethod.GET, + expectDataType: http.HttpDataType.ARRAY_BUFFER, + connectTimeout: 60000, // 可选 默认60000ms + readTimeout: 0, // 可选, 默认为60000ms + usingProtocol: http.HttpProtocol.HTTP1_1, // 可选,协议类型默认值由系统自动指定 + usingCache: false + }).catch((err: Error) => { + result.error = 'TestCustomDataFetchClientWithPage requestInStream error.' + JSON.stringify(err); + }) + if (resultCode == 200) { + //let combineArray = this.combineArrayBuffers(arrayBuffers); + // 计算多个ArrayBuffer的总字节大小 + let totalByteLength = 0; + for (const arrayBuffer of arrayBuffers) { + totalByteLength += arrayBuffer.byteLength; + } + // 创建一个新的ArrayBuffer + const combinedArrayBuffer = new ArrayBuffer(totalByteLength); + + // 创建一个Uint8Array来操作新的ArrayBuffer + const combinedUint8Array = new Uint8Array(combinedArrayBuffer); + + // 依次复制每个ArrayBuffer的内容到新的ArrayBuffer中 + let offset = 0; + for (const arrayBuffer of arrayBuffers) { + const sourceUint8Array = new Uint8Array(arrayBuffer); + combinedUint8Array.set(sourceUint8Array, offset); + offset += sourceUint8Array.length; + } + result.data = combinedArrayBuffer; + } else { + result.error = 'TestCustomDataFetchClientWithPage error. resultCode = ' + resultCode; + } + console.log('TestCustomDataFetch single onComplete, code = ' + resultCode + ',length = ' + result.data?.byteLength); + } catch (error) { + result.error = 'TestCustomDataFetchClientWithPage error' + error.stack; + } + return result; +} + + diff --git a/entry/src/main/resources/base/profile/main_pages.json b/entry/src/main/resources/base/profile/main_pages.json index f0b6f5d..6d5811e 100644 --- a/entry/src/main/resources/base/profile/main_pages.json +++ b/entry/src/main/resources/base/profile/main_pages.json @@ -54,6 +54,7 @@ "pages/testStopPlayingGifPage", "pages/testImageKnifeDataFetch", "pages/testImageKnifeHeic", - "pages/testImageKnifeNetPlaceholder" + "pages/testImageKnifeNetPlaceholder", + "pages/testCustomDataFetchClientWithPage" ] } \ No newline at end of file diff --git a/library/index.ets b/library/index.ets index f25c784..5e8d3ca 100644 --- a/library/index.ets +++ b/library/index.ets @@ -110,6 +110,8 @@ export {IDataFetch} from './src/main/ets/components/imageknife/networkmanage/IDa export {ICache} from './src/main/ets/components/imageknife/requestmanage/ICache' export { FileTypeUtil } from './src/main/ets/components/imageknife/utils/FileTypeUtil' export { ParseImageUtil } from './src/main/ets/components/imageknife/utils/ParseImageUtil' +export { DownloadClient } from './src/main/ets/components/imageknife/networkmanage/DownloadClient'; +export { CustomDataFetchClient } from './src/main/ets/components/imageknife/networkmanage/CustomDataFetchClient'; /** * svg parse diff --git a/library/src/main/ets/components/imageknife/ImageKnife.ets b/library/src/main/ets/components/imageknife/ImageKnife.ets index 34038b1..35fa987 100644 --- a/library/src/main/ets/components/imageknife/ImageKnife.ets +++ b/library/src/main/ets/components/imageknife/ImageKnife.ets @@ -680,6 +680,7 @@ export class ImageKnife { errorholderSrc: this.transformResource(request.errorholderSrc) as PixelMap | MResource | undefined, retryholderSrc:this.transformResource(request.retryholderSrc) as PixelMap | MResource | undefined, fallbackSrc: this.transformResource(request.fallbackSrc) as PixelMap | MResource | undefined, + customGetImage: request.customGetImage } return data; } @@ -940,6 +941,7 @@ async function taskExecute(sendData:SendableData,taskData:TaskParams): Promise

{ diff --git a/library/src/main/ets/components/imageknife/ImageKnifeComponent.ets b/library/src/main/ets/components/imageknife/ImageKnifeComponent.ets index c4b21f4..7265bbb 100644 --- a/library/src/main/ets/components/imageknife/ImageKnifeComponent.ets +++ b/library/src/main/ets/components/imageknife/ImageKnifeComponent.ets @@ -216,7 +216,6 @@ export struct ImageKnifeComponent { return false; } }) - let realSize:Size = { width: this.context.width, height: this.context.height @@ -312,6 +311,9 @@ export struct ImageKnifeComponent { this.runNextFunction(this.displayRetryholder,data) }}) } + if (this.imageKnifeOption.customGetImage) { + request.customGetImage = this.imageKnifeOption.customGetImage; + } } configHspContext(request: RequestOption){ diff --git a/library/src/main/ets/components/imageknife/ImageKnifeOption.ets b/library/src/main/ets/components/imageknife/ImageKnifeOption.ets index ce110be..724b166 100644 --- a/library/src/main/ets/components/imageknife/ImageKnifeOption.ets +++ b/library/src/main/ets/components/imageknife/ImageKnifeOption.ets @@ -27,6 +27,7 @@ import { RoundCorner } from './transform/RoundedCornersTransformation' import { ObjectKey } from './ObjectKey' import common from '@ohos.app.ability.common' import { Priority } from './RequestOption' +import { DataFetchResult } from './networkmanage/DataFetchResult' export interface CropCircleWithBorder{ border: number, @@ -145,6 +146,9 @@ export class ImageKnifeOption { context?: common.UIAbilityContext; // sizeAnimate?: AnimateParam 由业务自定义不再支持 + // 设置是否使用应用自定义的方式加载图片 + customGetImage?: (context: Context, src: string) => Promise; + constructor() { } diff --git a/library/src/main/ets/components/imageknife/RequestOption.ets b/library/src/main/ets/components/imageknife/RequestOption.ets index db78fc7..a01a6ad 100644 --- a/library/src/main/ets/components/imageknife/RequestOption.ets +++ b/library/src/main/ets/components/imageknife/RequestOption.ets @@ -59,6 +59,7 @@ import { DiskLruCache } from '../cache/DiskLruCache' import { SparkMD5 } from '../3rd_party/sparkmd5/spark-md5' import { FileUtils } from '../cache/FileUtils' import util from '@ohos.util' +import { DataFetchResult } from './networkmanage/DataFetchResult' export interface Size { width: number, @@ -149,6 +150,8 @@ export class RequestOption { fallbackCacheKey: string = ""; // 自定义缓存关键字 signature?: ObjectKey; + // 设置是否使用应用自定义的方式加载图片 + customGetImage?: (context: Context, src: string) => Promise; // 下载原始文件地址 downloadFilePath: string = ""; //磁盘缓存文件路径 diff --git a/library/src/main/ets/components/imageknife/TaskParams.ets b/library/src/main/ets/components/imageknife/TaskParams.ets index 4d4ddd7..51aaa2b 100644 --- a/library/src/main/ets/components/imageknife/TaskParams.ets +++ b/library/src/main/ets/components/imageknife/TaskParams.ets @@ -16,6 +16,7 @@ import { ObjectKey } from './ObjectKey'; import { Priority, Size } from '../imageknife/RequestOption' import common from '@ohos.app.ability.common' import { MResource } from './utils/MResource'; +import { DataFetchResult } from './networkmanage/DataFetchResult'; export class TaskParams { headers: Map = new Map(); @@ -27,4 +28,5 @@ export class TaskParams { errorholderSrc: PixelMap | MResource | undefined = undefined; retryholderSrc: PixelMap | MResource | undefined = undefined; fallbackSrc: PixelMap | MResource | undefined = undefined; + customGetImage?: (context: Context, loadSrc: string) => Promise; } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/networkmanage/CustomDataFetchClient.ets b/library/src/main/ets/components/imageknife/networkmanage/CustomDataFetchClient.ets new file mode 100644 index 0000000..db50dd1 --- /dev/null +++ b/library/src/main/ets/components/imageknife/networkmanage/CustomDataFetchClient.ets @@ -0,0 +1,113 @@ +/* + * 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 { IDataFetch } from '../networkmanage/IDataFetch' +import { RequestOption } from '../RequestOption' +import { RequestData } from './RequestData' +import { ImageKnifeGlobal } from '../ImageKnifeGlobal' +import common from '@ohos.app.ability.common' +import http from '@ohos.net.http' +import { DataFetchResult } from './DataFetchResult' + +@Sendable +export class CustomDataFetchClient implements IDataFetch { + async loadData(request: RequestOption) { + let result: DataFetchResult = new DataFetchResult(); + if (!request || typeof request.loadSrc !== 'string') { + result.error = 'CustomDataFetchClient request or loadSrc error.'; + return result; + } + // 自定义单个图片的网络栈 + if (request.customGetImage) { + return await request.customGetImage(ImageKnifeGlobal.getInstance() + .getHapContext() as common.UIAbilityContext, request.loadSrc); + } + // 所有图片的网络栈 + try { + let httpRequest = http.createHttp() + let arrayBuffers = new Array(); + httpRequest.on('headersReceive', (header: Object) => { + // 跟服务器连接成功准备下载 + if (request.progressFunc) { + // 进度条为0 + request.progressFunc.asyncSuccess(0) + } + }) + httpRequest.on('dataReceive', (data: ArrayBuffer) => { + // 下载数据流多次返回 + arrayBuffers.push(data); + }) + httpRequest.on('dataReceiveProgress', (data: RequestData) => { + // 下载进度 + 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) + } + } + }) + httpRequest.on('dataEnd', () => { + // 下载完毕 + }) + const headerObj: Record = {} + request.headers.forEach((value, key) => { + headerObj[key] = value + }) + const data = await httpRequest.requestInStream(request.loadSrc as string, { + header: headerObj, + method: http.RequestMethod.GET, + expectDataType: http.HttpDataType.ARRAY_BUFFER, + connectTimeout: 60000, // 可选 默认60000ms + readTimeout: 0, // 可选, 默认为60000ms + usingProtocol: http.HttpProtocol.HTTP1_1, // 可选,协议类型默认值由系统自动指定 + usingCache: false + }).catch((err: Error) => { + result.error = 'CustomDataFetchClient has error, http code = ' + JSON.stringify(err); + }) + if (data == 200) { + result.data = this.combineArrayBuffers(arrayBuffers); + } else { + result.error = 'CustomDataFetchClient resultCode error, code = ' + data; + } + console.log('TestCustomDataFetch all onComplete, code = ' + data + ',length = ' + result.data?.byteLength); + } catch (err) { + result.error = 'CustomDataFetchClient catch err request uuid =' + request.uuid; + } + return result; + } + + combineArrayBuffers(arrayBuffers: ArrayBuffer[]): ArrayBuffer { + // 计算多个ArrayBuffer的总字节大小 + let totalByteLength = 0; + for (const arrayBuffer of arrayBuffers) { + totalByteLength += arrayBuffer.byteLength; + } + + // 创建一个新的ArrayBuffer + const combinedArrayBuffer = new ArrayBuffer(totalByteLength); + + // 创建一个Uint8Array来操作新的ArrayBuffer + const combinedUint8Array = new Uint8Array(combinedArrayBuffer); + + // 依次复制每个ArrayBuffer的内容到新的ArrayBuffer中 + let offset = 0; + for (const arrayBuffer of arrayBuffers) { + const sourceUint8Array = new Uint8Array(arrayBuffer); + combinedUint8Array.set(sourceUint8Array, offset); + offset += sourceUint8Array.length; + } + return combinedArrayBuffer; + } +} \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/networkmanage/DownloadClient.ets b/library/src/main/ets/components/imageknife/networkmanage/DownloadClient.ets index ed1c4e6..c769b17 100644 --- a/library/src/main/ets/components/imageknife/networkmanage/DownloadClient.ets +++ b/library/src/main/ets/components/imageknife/networkmanage/DownloadClient.ets @@ -35,11 +35,7 @@ export class DownloadClient implements IDataFetch { async loadData(request: RequestOption): Promise { if (typeof request.loadSrc == 'string') { - let fileDir:string = (ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext).filesDir as string; - let cacheDir:string = (ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext).cacheDir as string - - if (request.loadSrc.startsWith(fileDir) || - request.loadSrc.startsWith(cacheDir)) { + if (this.isLocalLoadSrc(ImageKnifeGlobal.getInstance().getHapContext(), request.loadSrc)) { // 本地沙盒 return this.localFileClient.loadData(request) } else if (request.loadSrc.startsWith('datashare://') || request.loadSrc.startsWith('file://')) { @@ -54,4 +50,15 @@ export class DownloadClient implements IDataFetch { return result; } } + + isLocalLoadSrc(context: Object | undefined, loadSrc: string): boolean { + if (context != undefined) { + let fileDir: string = (context as common.UIAbilityContext).filesDir as string; + let cacheDir: string = (context as common.UIAbilityContext).cacheDir as string + if (loadSrc.startsWith(fileDir) || loadSrc.startsWith(cacheDir)) { + return true; + } + } + return false; + } } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/networkmanage/HttpDownloadClient.ets b/library/src/main/ets/components/imageknife/networkmanage/HttpDownloadClient.ets index 1806806..c54f6e5 100644 --- a/library/src/main/ets/components/imageknife/networkmanage/HttpDownloadClient.ets +++ b/library/src/main/ets/components/imageknife/networkmanage/HttpDownloadClient.ets @@ -70,6 +70,7 @@ export class HttpDownloadClient implements IDataFetch { } else { result.error = `HttpDownloadClient has error, http code = ` + JSON.stringify(data); } + console.log('TestCustomDataFetch http onComplete, code = ' + data + ',length = ' + result.data?.byteLength); } catch (err) { result.error ='HttpDownloadClient catch err request uuid =' + request.uuid; } diff --git a/sharedlibrary/src/main/ets/Index.ets b/sharedlibrary/src/main/ets/Index.ets index 8597fed..30f935f 100644 --- a/sharedlibrary/src/main/ets/Index.ets +++ b/sharedlibrary/src/main/ets/Index.ets @@ -110,6 +110,8 @@ export {IDataFetch} from '@ohos/imageknife' export {ICache} from '@ohos/imageknife' export { FileTypeUtil } from '@ohos/imageknife' export { ParseImageUtil } from '@ohos/imageknife' +export { DownloadClient } from '@ohos/imageknife' +export { CustomDataFetchClient } from '@ohos/imageknife' /** * svg parse From ec861fb89fa9c30c0fe74454bd6fe21b9de8d436 Mon Sep 17 00:00:00 2001 From: chenchen2019710 <1173940354@qq.com> Date: Tue, 23 Apr 2024 17:48:45 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E4=BF=AE=E6=94=B9ImageKnife=E8=B7=B3?= =?UTF-8?q?=E8=BF=87=E7=BD=91=E7=BB=9C,=E7=82=B9=E5=87=BB=E9=BB=98?= =?UTF-8?q?=E8=AE=A4,=E5=9B=BE=E7=89=87=E6=B2=A1=E6=9C=89=E4=BC=A0?= =?UTF-8?q?=E5=85=A5=E5=AE=BD=E9=AB=98,=E6=97=A0=E6=98=BE=E7=A4=BAbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: chenchen2019710 <1173940354@qq.com> --- CHANGELOG.md | 17 +++++++++++------ .../src/main/ets/pages/testImageKnifeCache.ets | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8b8b3d..1e3318c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ -## 2.1.2-rc.13 -- 修改ImageKnife跳过网络,从内存中获取图片 cacheType参数未使用bug -- 新增WEBP图片解析能力。 -- 新增gif图片支持暂停播放功能 +## 2.2.0-rc.1 +- 修改ImageKnife跳过网络,点击默认,图片没有传入宽高,无显示bug +- ImageKnife支持根据自定义key获取已缓存的图片 +- ImageKnife加载图片支持自定义网络栈和图片加载组件 + +## 2.2.0-rc.0 - 修复自定义DataFetch接口实现不生效问题 - 修改磁盘缓存到子线程 - 更新SDK到API12 @@ -9,8 +11,11 @@ - 修改全局请求头覆盖request请求头 - imageKnife支持heic测试demo独立页面展示 - drawLifeCycle支持gif图 -- ImageKnife支持根据自定义key获取已缓存的图片 -- ImageKnife加载图片支持自定义网络栈和图片加载组件 + +## 2.1.2 +- 修改ImageKnife跳过网络,从内存中获取图片 cacheType参数未使用bug +- 新增WEBP图片解析能力。 +- 新增gif图片支持暂停播放功能 ## 2.1.2-rc.12 - 新增gif播放次数功能 diff --git a/entry/src/main/ets/pages/testImageKnifeCache.ets b/entry/src/main/ets/pages/testImageKnifeCache.ets index c1aaf57..8a787ea 100644 --- a/entry/src/main/ets/pages/testImageKnifeCache.ets +++ b/entry/src/main/ets/pages/testImageKnifeCache.ets @@ -186,7 +186,7 @@ struct testImageKnifeCache { Button('默认') .onClick(() => { this.index_ = 2; - imageKnife?.isUrlExist(this.url).then(this.loadSuccess) + imageKnife?.isUrlExist(this.url, CacheType.Default, this.comSize).then(this.loadSuccess) .catch(this.loadError); }) } From 9bc31bb138907856d441504238b1e7f18d35c2d3 Mon Sep 17 00:00:00 2001 From: zenggaofeng Date: Tue, 23 Apr 2024 20:04:51 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E9=80=82=E9=85=8D=E5=A4=8D=E7=94=A8?= =?UTF-8?q?=E5=9C=BA=E6=99=AF=E8=A7=A6=E5=8F=91=E6=87=92=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?onDataReloaded?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: zenggaofeng --- CHANGELOG.md | 1 + .../ets/pages/imageknifeTestCaseIndex.ets | 5 + .../src/main/ets/pages/testReuseAblePages.ets | 204 ++++++++++++++++++ .../resources/base/profile/main_pages.json | 3 +- .../imageknife/ImageKnifeComponent.ets | 3 + 5 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 entry/src/main/ets/pages/testReuseAblePages.ets diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e3318c..1dbda67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - 修改ImageKnife跳过网络,点击默认,图片没有传入宽高,无显示bug - ImageKnife支持根据自定义key获取已缓存的图片 - ImageKnife加载图片支持自定义网络栈和图片加载组件 +- 适配复用场景触发懒加载onDataReloaded ## 2.2.0-rc.0 - 修复自定义DataFetch接口实现不生效问题 diff --git a/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets b/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets index 6bb39fb..a951647 100644 --- a/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets +++ b/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets @@ -127,6 +127,11 @@ struct IndexFunctionDemo { console.log("测试一级内存缓存") router.pushUrl({ url: "pages/testReusePhotoPage" }); }).margin({ top: 5, left: 3 }) + Button("适配复用场景") + .onClick(() => { + console.log("测试一级内存缓存") + router.pushUrl({ url: "pages/testReuseAblePages" }); + }).margin({ top: 5, left: 3 }) }.width('100%') .height(60).backgroundColor(Color.Pink) Text("测试占位图 失败占位图 功能点").fontSize(15) diff --git a/entry/src/main/ets/pages/testReuseAblePages.ets b/entry/src/main/ets/pages/testReuseAblePages.ets new file mode 100644 index 0000000..7144c87 --- /dev/null +++ b/entry/src/main/ets/pages/testReuseAblePages.ets @@ -0,0 +1,204 @@ +/* + * 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 } from '@ohos/libraryimageknife' + +class BasicDataSource implements IDataSource { + private listeners: DataChangeListener[] = []; + private originDataArray: StringData[] = []; + + public totalCount(): number { + return 0; + } + + public getData(index: number): StringData { + return this.originDataArray[index]; + } + + registerDataChangeListener(listener: DataChangeListener): void { + if (this.listeners.indexOf(listener) < 0) { + console.info('add listener'); + this.listeners.push(listener); + } + } + + unregisterDataChangeListener(listener: DataChangeListener): void { + const pos = this.listeners.indexOf(listener); + if (pos >= 0) { + console.info('remove listener'); + this.listeners.splice(pos, 1); + } + } + + notifyDataReload(): void { + this.listeners.forEach(listener => { + listener.onDataReloaded(); + }) + } + + notifyDataAdd(index: number): void { + this.listeners.forEach(listener => { + listener.onDataAdd(index); + }) + } + + notifyDataChange(index: number): void { + this.listeners.forEach(listener => { + listener.onDataChange(index); + }) + } + + notifyDataDelete(index: number): void { + this.listeners.forEach(listener => { + listener.onDataDelete(index); + }) + } + + notifyDataMove(from: number, to: number): void { + this.listeners.forEach(listener => { + listener.onDataMove(from, to); + }) + } +} + +class MyDataSource extends BasicDataSource { + private dataArray: StringData[] = []; + + public totalCount(): number { + return this.dataArray.length; + } + + public getData(index: number): StringData { + return this.dataArray[index]; + } + + public addData(index: number, data: StringData): void { + this.dataArray.splice(index, 0, data); + this.notifyDataAdd(index); + } + + public pushData(data: StringData): void { + this.dataArray.push(data); + this.notifyDataAdd(this.dataArray.length - 1); + } + + public reloadData(): void { + this.notifyDataReload(); + } +} + +class StringData { + message: string; + imgSrc: string; + + constructor(message: string, imgSrc: string) { + this.message = message; + this.imgSrc = imgSrc; + } +} + +@Entry +@Component +struct TestReuseAbleComponent { + @State dataArray: Array = [ + "http://e.hiphotos.baidu.com/image/pic/item/a1ec08fa513d2697e542494057fbb2fb4316d81e.jpg", + "http://c.hiphotos.baidu.com/image/pic/item/30adcbef76094b36de8a2fe5a1cc7cd98d109d99.jpg", + "http://h.hiphotos.baidu.com/image/pic/item/7c1ed21b0ef41bd5f2c2a9e953da81cb39db3d1d.jpg", + "http://g.hiphotos.baidu.com/image/pic/item/55e736d12f2eb938d5277fd5d0628535e5dd6f4a.jpg", + "http://e.hiphotos.baidu.com/image/pic/item/4e4a20a4462309f7e41f5cfe760e0cf3d6cad6ee.jpg", + "http://b.hiphotos.baidu.com/image/pic/item/9d82d158ccbf6c81b94575cfb93eb13533fa40a2.jpg", + "http://e.hiphotos.baidu.com/image/pic/item/4bed2e738bd4b31c1badd5a685d6277f9e2ff81e.jpg", + "http://g.hiphotos.baidu.com/image/pic/item/0d338744ebf81a4c87a3add4d52a6059252da61e.jpg", + "http://a.hiphotos.baidu.com/image/pic/item/f2deb48f8c5494ee5080c8142ff5e0fe99257e19.jpg", + "http://f.hiphotos.baidu.com/image/pic/item/4034970a304e251f503521f5a586c9177e3e53f9.jpg", + "http://b.hiphotos.baidu.com/image/pic/item/279759ee3d6d55fbb3586c0168224f4a20a4dd7e.jpg", + "http://img2.xkhouse.com/bbs/hfhouse/data/attachment/forum/corebbs/2009-11/2009113011534566298.jpg", + "http://a.hiphotos.baidu.com/image/pic/item/e824b899a9014c087eb617650e7b02087af4f464.jpg", + "http://c.hiphotos.baidu.com/image/pic/item/9c16fdfaaf51f3de1e296fa390eef01f3b29795a.jpg", + "http://d.hiphotos.baidu.com/image/pic/item/b58f8c5494eef01f119945cbe2fe9925bc317d2a.jpg", + "http://h.hiphotos.baidu.com/image/pic/item/902397dda144ad340668b847d4a20cf430ad851e.jpg", + "http://b.hiphotos.baidu.com/image/pic/item/359b033b5bb5c9ea5c0e3c23d139b6003bf3b374.jpg", + "http://a.hiphotos.baidu.com/image/pic/item/8d5494eef01f3a292d2472199d25bc315d607c7c.jpg", + "http://b.hiphotos.baidu.com/image/pic/item/e824b899a9014c08878b2c4c0e7b02087af4f4a3.jpg", + "http://g.hiphotos.baidu.com/image/pic/item/6d81800a19d8bc3e770bd00d868ba61ea9d345f2.jpg", + "https://hbimg.huabanimg.com/cc6af25f8d782d3cf3122bef4e61571378271145735e9-vEVggB", + 'https://img-blog.csdnimg.cn/20191215043500229.png', + 'https://img-blog.csdn.net/20140514114029140', + 'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp' + ] + private data: MyDataSource = new MyDataSource(); + private index: number = 0 + private timeId: number = -1 + + aboutToAppear() { + for (let i = 0; i <= 4; i++) { + this.data.pushData(new StringData(`Hello ${i}`, this.dataArray[i])); + } + } + + build() { + List({ space: 3 }) { + LazyForEach(this.data, (item: StringData, index: number) => { + ListItem() { + Column() { + Text(item.message).fontSize(50) + .onAppear(() => { + console.info("appear:" + item.message) + }) + ReusableComponent({ url: item.imgSrc }) + .width(500) + .height(200) + }.margin({ left: 10, right: 10 }) + } + .onClick(() => { + item.message += '00'; + this.data.reloadData(); + }) + }, (item: StringData, index: number) => JSON.stringify(item)) + }.cachedCount(5) + .onReachEnd(()=>{ + clearTimeout(this.timeId) + this.timeId = setTimeout(()=>{ + this.data.reloadData() + if(this.data.totalCount() <= this.dataArray.length) { + this.index = this.data.totalCount() + for (let index = this.index; index <= this.index + 4; index++) { + this.data.addData(index,new StringData(`Hello ${index}`, this.dataArray[index])); + } + } + },1500) + }) + } +} + +@Reusable +@Component +struct ReusableComponent { + @State url: string = "" + + aboutToReuse(params: Record) { + this.url = params.url + } + + build() { + Column() { + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: this.url, + placeholderSrc: $r('app.media.icon_loading') + } + }) + }.width("100%").height("100%") + } +} \ No newline at end of file diff --git a/entry/src/main/resources/base/profile/main_pages.json b/entry/src/main/resources/base/profile/main_pages.json index 6d5811e..3be410f 100644 --- a/entry/src/main/resources/base/profile/main_pages.json +++ b/entry/src/main/resources/base/profile/main_pages.json @@ -55,6 +55,7 @@ "pages/testImageKnifeDataFetch", "pages/testImageKnifeHeic", "pages/testImageKnifeNetPlaceholder", - "pages/testCustomDataFetchClientWithPage" + "pages/testCustomDataFetchClientWithPage", + "pages/testReuseAblePages" ] } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/ImageKnifeComponent.ets b/library/src/main/ets/components/imageknife/ImageKnifeComponent.ets index 7265bbb..11d288a 100644 --- a/library/src/main/ets/components/imageknife/ImageKnifeComponent.ets +++ b/library/src/main/ets/components/imageknife/ImageKnifeComponent.ets @@ -724,6 +724,9 @@ export struct ImageKnifeComponent { aboutToRecycle(){ this.resetGifData() } + aboutToReuse(params: ESObject) { + this.context.clearRect(0,0,this.context.width,this.context.height) + } aboutToAppear() { LogUtil.log('ImageKnifeComponent aboutToAppear happened!') this.canvasHasReady = false; From 87f3dab4fa7d3e64610b9fb3043e6c7e8c6d73f9 Mon Sep 17 00:00:00 2001 From: chongtiantian Date: Wed, 24 Apr 2024 10:05:23 +0800 Subject: [PATCH 4/4] =?UTF-8?q?ImageKnife=E6=8E=A7=E5=88=B6=E9=87=8D?= =?UTF-8?q?=E8=A6=81=E5=9B=BE=E7=89=87=E8=AF=B7=E6=B1=82=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E4=BC=98=E5=85=88=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: chongtiantian --- CHANGELOG.md | 1 + README.md | 22 +- .../main/ets/pages/testPriorityComponent.ets | 236 +++++++++++----- .../ets/test/DefaultJobQueueTest.test.ets | 64 +++++ entry/src/ohosTest/ets/test/List.test.ets | 2 + .../ets/components/imageknife/ImageKnife.ets | 260 +++++------------- .../components/imageknife/RequestOption.ets | 14 +- .../imageknife/utils/DefaultJobQueue.ets | 48 ++++ .../components/imageknife/utils/IJobQueue.ets | 36 +++ 9 files changed, 410 insertions(+), 273 deletions(-) create mode 100644 entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets create mode 100644 library/src/main/ets/components/imageknife/utils/DefaultJobQueue.ets create mode 100644 library/src/main/ets/components/imageknife/utils/IJobQueue.ets diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dbda67..bab7ab2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ - ImageKnife支持根据自定义key获取已缓存的图片 - ImageKnife加载图片支持自定义网络栈和图片加载组件 - 适配复用场景触发懒加载onDataReloaded +- ImageKnife控制重要图片请求加载优先级 ## 2.2.0-rc.0 - 修复自定义DataFetch接口实现不生效问题 diff --git a/README.md b/README.md index dbe0121..5cd4314 100644 --- a/README.md +++ b/README.md @@ -337,13 +337,14 @@ request.skipMemoryCache(true) ### ImageKnife 启动器/门面类 -| 方法名 | 入参 | 接口描述 | -| ------------------------------- | ---------------------- | ---------------------------------- | -| call(request: RequestOption) | request: RequestOption | 根据用户配置参数具体执行加载流程 | -| preload(request: RequestOption) | request: RequestOption | 根据用户配置参数具体执行预加载流程 | -| pauseRequests() | | 全局暂停请求 | -| resumeRequests() | | 全局恢复暂停 | -| isUrlExist(url, cacheType, size)| url, CacheType, Size | 判断图片是否在 缓存和磁盘中存在,如果入参是缓存,需要传入值图片大小,参数 CacheType, Size(可选)| +| 方法名 | 入参 | 接口描述 | +|----------------------------------| ---------------------- | ------------------------------------------------------------ | +| call(request: RequestOption) | request: RequestOption | 根据用户配置参数具体执行加载流程 | +| preload(request: RequestOption) | request: RequestOption | 根据用户配置参数具体执行预加载流程 | +| pauseRequests() | | 全局暂停请求 | +| resumeRequests() | | 全局恢复暂停 | +| isUrlExist(url, cacheType, size) | url, CacheType, Size | 判断图片是否在 缓存和磁盘中存在,如果入参是缓存,需要传入值图片大小,参数 CacheType, Size(可选) | +| setMaxRequests(count: number) | count | 设置请求的最大并发数量 | ### 缓存策略相关 @@ -448,6 +449,13 @@ export default class EntryAbility extends UIAbility { } ``` +### Queue + +| 方法名 | 入参 | 接口描述 | +| ----------------------------------- | ----------------------- | ------------------------------ | +| getQueueLength(): number | | 获取队列总长度 | +| add(request: RequestOption) | request:RequestOption | 在队列尾部插入元素 | +| pop(): RequestOption | undefined | | 删除队列头元素并返回该删除元素 | ## 约束与限制 diff --git a/entry/src/main/ets/pages/testPriorityComponent.ets b/entry/src/main/ets/pages/testPriorityComponent.ets index 5630f3c..e496e8c 100644 --- a/entry/src/main/ets/pages/testPriorityComponent.ets +++ b/entry/src/main/ets/pages/testPriorityComponent.ets @@ -12,83 +12,177 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ImageKnifeComponent , Priority , NONE } from '@ohos/libraryimageknife' +import { ImageKnifeComponent, ImageKnifeOption, Priority, NONE, ImageKnifeGlobal } from '@ohos/libraryimageknife'; +const dataBak: ImageKnifeOption[] = [ + { + loadSrc: "http://b.hiphotos.baidu.com/image/pic/item/9d82d158ccbf6c81b94575cfb93eb13533fa40a2.jpg", + priority: Priority.HIGH + }, + { + loadSrc: "http://e.hiphotos.baidu.com/image/pic/item/4bed2e738bd4b31c1badd5a685d6277f9e2ff81e.jpg", + priority: Priority.HIGH + }, + { + loadSrc: "http://g.hiphotos.baidu.com/image/pic/item/0d338744ebf81a4c87a3add4d52a6059252da61e.jpg", + priority: Priority.HIGH + }, + { + loadSrc: "http://a.hiphotos.baidu.com/image/pic/item/f2deb48f8c5494ee5080c8142ff5e0fe99257e19.jpg", + priority: Priority.HIGH + }, + { + loadSrc: "http://f.hiphotos.baidu.com/image/pic/item/4034970a304e251f503521f5a586c9177e3e53f9.jpg", + priority: Priority.HIGH + }, + { + loadSrc: "https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", + priority: Priority.MEDIUM + }, + { + loadSrc: "http://h.hiphotos.baidu.com/image/pic/item/7c1ed21b0ef41bd5f2c2a9e953da81cb39db3d1d.jpg", + priority: Priority.MEDIUM + }, + { + loadSrc: "http://a.hiphotos.baidu.com/image/pic/item/8d5494eef01f3a292d2472199d25bc315d607c7c.jpg", + priority: Priority.MEDIUM + }, + { + loadSrc: "https://img-blog.csdn.net/20140514114029140", + priority: Priority.MEDIUM + }, + { + loadSrc: "http://g.hiphotos.baidu.com/image/pic/item/6d81800a19d8bc3e770bd00d868ba61ea9d345f2.jpg", + priority: Priority.MEDIUM + }, + { + loadSrc: "http://b.hiphotos.baidu.com/image/pic/item/e824b899a9014c08878b2c4c0e7b02087af4f4a3.jpg", + priority: Priority.LOW + }, + { + loadSrc: "http://b.hiphotos.baidu.com/image/pic/item/359b033b5bb5c9ea5c0e3c23d139b6003bf3b374.jpg", + priority: Priority.LOW + }, + { + loadSrc: "http://h.hiphotos.baidu.com/image/pic/item/902397dda144ad340668b847d4a20cf430ad851e.jpg", + priority: Priority.LOW + }, + { + loadSrc: "http://d.hiphotos.baidu.com/image/pic/item/b58f8c5494eef01f119945cbe2fe9925bc317d2a.jpg", + priority: Priority.LOW + }, + { + loadSrc:"http://img2.xkhouse.com/bbs/hfhouse/data/attachment/forum/corebbs/2009-11/2009113011534566298.jpg", + priority: Priority.LOW + } +]; @Entry @Component struct TestPriorityComponent { - private data: string[] = [ - "http://e.hiphotos.baidu.com/image/pic/item/a1ec08fa513d2697e542494057fbb2fb4316d81e.jpg", - "http://c.hiphotos.baidu.com/image/pic/item/30adcbef76094b36de8a2fe5a1cc7cd98d109d99.jpg", - "http://h.hiphotos.baidu.com/image/pic/item/7c1ed21b0ef41bd5f2c2a9e953da81cb39db3d1d.jpg", - "http://g.hiphotos.baidu.com/image/pic/item/55e736d12f2eb938d5277fd5d0628535e5dd6f4a.jpg", - "http://e.hiphotos.baidu.com/image/pic/item/4e4a20a4462309f7e41f5cfe760e0cf3d6cad6ee.jpg", - "http://b.hiphotos.baidu.com/image/pic/item/9d82d158ccbf6c81b94575cfb93eb13533fa40a2.jpg", - "http://e.hiphotos.baidu.com/image/pic/item/4bed2e738bd4b31c1badd5a685d6277f9e2ff81e.jpg", - "http://g.hiphotos.baidu.com/image/pic/item/0d338744ebf81a4c87a3add4d52a6059252da61e.jpg", - "http://a.hiphotos.baidu.com/image/pic/item/f2deb48f8c5494ee5080c8142ff5e0fe99257e19.jpg", - "http://f.hiphotos.baidu.com/image/pic/item/4034970a304e251f503521f5a586c9177e3e53f9.jpg", - "http://b.hiphotos.baidu.com/image/pic/item/279759ee3d6d55fbb3586c0168224f4a20a4dd7e.jpg", - "http://img2.xkhouse.com/bbs/hfhouse/data/attachment/forum/corebbs/2009-11/2009113011534566298.jpg", - "http://a.hiphotos.baidu.com/image/pic/item/e824b899a9014c087eb617650e7b02087af4f464.jpg", - "http://c.hiphotos.baidu.com/image/pic/item/9c16fdfaaf51f3de1e296fa390eef01f3b29795a.jpg", - "http://d.hiphotos.baidu.com/image/pic/item/b58f8c5494eef01f119945cbe2fe9925bc317d2a.jpg", - "http://h.hiphotos.baidu.com/image/pic/item/902397dda144ad340668b847d4a20cf430ad851e.jpg", - "http://b.hiphotos.baidu.com/image/pic/item/359b033b5bb5c9ea5c0e3c23d139b6003bf3b374.jpg", - "http://a.hiphotos.baidu.com/image/pic/item/8d5494eef01f3a292d2472199d25bc315d607c7c.jpg", - "http://b.hiphotos.baidu.com/image/pic/item/e824b899a9014c08878b2c4c0e7b02087af4f4a3.jpg", - "http://g.hiphotos.baidu.com/image/pic/item/6d81800a19d8bc3e770bd00d868ba61ea9d345f2.jpg", - "https://hbimg.huabanimg.com/cc6af25f8d782d3cf3122bef4e61571378271145735e9-vEVggB", - 'https://img-blog.csdnimg.cn/20191215043500229.png', - 'https://img-blog.csdn.net/20140514114029140', - 'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp' - ] + @State data: ImageKnifeOption[] = [ + { + loadSrc: "https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", + priority: Priority.HIGH + }, + { + loadSrc: "http://c.hiphotos.baidu.com/image/pic/item/30adcbef76094b36de8a2fe5a1cc7cd98d109d99.jpg", + priority: Priority.HIGH + }, + { + loadSrc: "http://h.hiphotos.baidu.com/image/pic/item/7c1ed21b0ef41bd5f2c2a9e953da81cb39db3d1d.jpg", + priority: Priority.HIGH + }, + { + loadSrc: "http://g.hiphotos.baidu.com/image/pic/item/55e736d12f2eb938d5277fd5d0628535e5dd6f4a.jpg", + priority: Priority.HIGH + }, + { + loadSrc: "http://e.hiphotos.baidu.com/image/pic/item/4e4a20a4462309f7e41f5cfe760e0cf3d6cad6ee.jpg", + priority: Priority.HIGH + }, + { + loadSrc: "http://b.hiphotos.baidu.com/image/pic/item/279759ee3d6d55fbb3586c0168224f4a20a4dd7e.jpg", + priority: Priority.MEDIUM + }, + { + loadSrc: "http://img2.xkhouse.com/bbs/hfhouse/data/attachment/forum/corebbs/2009-11/2009113011534566298.jpg", + priority: Priority.MEDIUM + }, + { + loadSrc: "http://a.hiphotos.baidu.com/image/pic/item/e824b899a9014c087eb617650e7b02087af4f464.jpg", + priority: Priority.MEDIUM + }, + { + loadSrc: "http://c.hiphotos.baidu.com/image/pic/item/9c16fdfaaf51f3de1e296fa390eef01f3b29795a.jpg", + priority: Priority.MEDIUM + }, + { + loadSrc: "http://d.hiphotos.baidu.com/image/pic/item/b58f8c5494eef01f119945cbe2fe9925bc317d2a.jpg", + priority: Priority.MEDIUM + }, + { + loadSrc: "https://hbimg.huabanimg.com/cc6af25f8d782d3cf3122bef4e61571378271145735e9-vEVggB", + priority: Priority.LOW + }, + { + loadSrc: "https://img-blog.csdnimg.cn/20191215043500229.png", + priority: Priority.LOW + }, + { + loadSrc: "https://img-blog.csdn.net/20140514114029140", + priority: Priority.LOW + }, + { + loadSrc: "https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp", + priority: Priority.LOW + }, + { + loadSrc: "http://g.hiphotos.baidu.com/image/pic/item/6d81800a19d8bc3e770bd00d868ba61ea9d345f2.jpg", + priority: Priority.LOW + } + ]; + @State maxRequests: number | undefined = ImageKnifeGlobal?.getInstance()?.getImageKnife()?.maxRequests; - build(){ - Column(){ - Grid(){ - ForEach(this.data,(item:string,index:number)=>{ - GridItem(){ - if(index % 2 == 0){ - ImageKnifeComponent({ - imageKnifeOption:{ - loadSrc:item, - placeholderSrc:$r('app.media.icon_loading'), - strategy:new NONE(), - isCacheable:false, - priority:Priority.LOW - } - }) - } else if (index % 3 == 0) { - ImageKnifeComponent({ - imageKnifeOption:{ - loadSrc:item, - placeholderSrc:$r('app.media.icon_loading'), - strategy:new NONE(), - isCacheable:false, - priority:Priority.MEDIUM - } - }) - } else { - ImageKnifeComponent({ - imageKnifeOption:{ - loadSrc:item, - placeholderSrc:$r('app.media.icon_loading'), - strategy:new NONE(), - isCacheable:false, - priority:Priority.HIGH - } - }) + build() { + Scroll() { + Column() { + Text('最大并发数: ' + this.maxRequests).width('50%').height(30) + Button('修改最大并发数为1').width('50%').height(30) + .onClick(() => { + if (ImageKnifeGlobal?.getInstance()?.getImageKnife()) { + ImageKnifeGlobal?.getInstance()?.getImageKnife()?.setMaxRequests(1); + this.maxRequests = ImageKnifeGlobal?.getInstance()?.getImageKnife()?.maxRequests; + this.data = [...dataBak, ...dataBak, ...dataBak]; } - } - }) + }) + Grid() { + ForEach(this.data, (item: ImageKnifeOption, index: number) => { + GridItem() { + Flex() { + Text(item?.priority?.toString() || '') + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: item.loadSrc, + placeholderSrc: $r('app.media.icon_loading'), + strategy: new NONE(), + isCacheable: false, + priority: item.priority + } + }) + }.height(60) + } + }) + } + .width("100%") + .height("100%") + .columnsTemplate("1fr 1fr 1fr 1fr 1fr") + .columnsGap(10) + .rowsGap(10) + .backgroundColor(0xFAEEE0) + .maxCount(10) } - .width("100%") - .height("100%") - .columnsTemplate("1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr") - .rowsTemplate("1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr") - .columnsGap(10) - .backgroundColor(0xFAEEE0) - }.width("100%").height("100%").backgroundColor(Color.Pink) + .backgroundColor(Color.Pink) + .margin({ top: 5 }) + } } } \ No newline at end of file diff --git a/entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets b/entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets new file mode 100644 index 0000000..e1c1401 --- /dev/null +++ b/entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets @@ -0,0 +1,64 @@ +/* + * 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 { describe, it, expect } from '@ohos/hypium'; +import { ImageKnife, ImageKnifeGlobal, RequestOption } from '@ohos/imageknife/Index'; +import { DefaultJobQueue } from '@ohos/imageknife/src/main/ets/components/imageknife/utils/DefaultJobQueue'; +import { IJobQueue } from '@ohos/imageknife/src/main/ets/components/imageknife/utils/IJobQueue'; +import taskpool from '@ohos.taskpool'; +import common from '@ohos.app.ability.common'; + +export default function DefaultJobQueueTest() { + + describe('DefaultJobQueueTest', () => { + it('testJob', 0, () => { + let job: IJobQueue = new DefaultJobQueue(); + job.add(makeRequest("medium1", getContext() as common.UIAbilityContext)); + job.add(makeRequest("medium2", getContext() as common.UIAbilityContext, taskpool.Priority.MEDIUM)); + job.add(makeRequest("medium3", getContext() as common.UIAbilityContext)); + job.add(makeRequest("low1", getContext() as common.UIAbilityContext, taskpool.Priority.LOW)); + job.add(makeRequest("high1", getContext() as common.UIAbilityContext, taskpool.Priority.HIGH)); + job.add(makeRequest("low2", getContext() as common.UIAbilityContext, taskpool.Priority.LOW)); + job.add(makeRequest("medium4", getContext() as common.UIAbilityContext)); + + expect(job.getQueueLength()).assertEqual(7); + expect(job.pop()!.loadSrc).assertEqual("high1"); + expect(job.pop()!.loadSrc).assertEqual("medium1"); + expect(job.pop()!.loadSrc).assertEqual("medium2"); + expect(job.pop()!.loadSrc).assertEqual("medium3"); + expect(job.pop()!.loadSrc).assertEqual("medium4"); + expect(job.pop()!.loadSrc).assertEqual("low1"); + expect(job.pop()!.loadSrc).assertEqual("low2"); + expect(job.pop()).assertEqual(undefined); + expect(job.getQueueLength()).assertEqual(0); + }); + + it("testMaxRequests", 1, () => { + let imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife(); + if (imageKnife) { + expect(imageKnife.maxRequests).assertEqual(64); + imageKnife.setMaxRequests(10); + expect(imageKnife.maxRequests).assertEqual(10); + }; + }) + }); +} + +function makeRequest(src: string, context: common.UIAbilityContext, priority: taskpool.Priority = taskpool.Priority.MEDIUM): RequestOption { + let request: RequestOption = new RequestOption() + request.loadSrc = src; + request.priority = priority; + request.moduleContext = context; + return request; +} diff --git a/entry/src/ohosTest/ets/test/List.test.ets b/entry/src/ohosTest/ets/test/List.test.ets index 489b341..29810cc 100644 --- a/entry/src/ohosTest/ets/test/List.test.ets +++ b/entry/src/ohosTest/ets/test/List.test.ets @@ -19,6 +19,7 @@ import Transfrom from './transfrom.test' import RequestOptionTest from './requestoption.test' import ImageKnifeTest from './imageknife.test' import DiskLruCacheTest from './diskLruCache.test' +import DefaultJobQueueTest from './DefaultJobQueueTest.test'; export default function testsuite() { abilityTest() @@ -28,4 +29,5 @@ export default function testsuite() { Transfrom() RequestOptionTest() ImageKnifeTest(); + DefaultJobQueueTest(); } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/ImageKnife.ets b/library/src/main/ets/components/imageknife/ImageKnife.ets index 35fa987..3e784a1 100644 --- a/library/src/main/ets/components/imageknife/ImageKnife.ets +++ b/library/src/main/ets/components/imageknife/ImageKnife.ets @@ -35,13 +35,16 @@ import http from '@ohos.net.http' 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 { DefaultJobQueue } from './utils/DefaultJobQueue'; +import { IJobQueue } from './utils/IJobQueue'; +import LightWeightMap from '@ohos.util.LightWeightMap'; +import List from '@ohos.util.List'; import { GIFFrame } from './utils/gif/GIFFrame' import emitter from '@ohos.events.emitter'; @@ -68,9 +71,6 @@ export class ImageKnife { memoryCacheProxy: MemoryCacheProxy = new MemoryCacheProxy(new MemoryLruCache(100, 100 * 1024 * 1024)); headerMap: Map = new Map(); //定义全局map placeholderCache: string = "placeholderCache" - runningMaps: EasyLinkedHashMap; - pendingMaps: EasyLinkedHashMap; - pausedMaps: EasyLinkedHashMap; isPaused: boolean = false; mutex: MethodMutex = new MethodMutex(); fileTypeUtil: FileTypeUtil; // 通用文件格式辨别 @@ -87,14 +87,16 @@ export class ImageKnife { // 开发者可配置全局缓存 engineKeyImpl: EngineKeyInterface; private mParseImageUtil: IParseImage; + // 最大并发 + maxRequests: number = 64; + // 排队队列 + private jobQueue: IJobQueue = new DefaultJobQueue(); + // 执行中的请求 + private executingJobMap: LightWeightMap> = new LightWeightMap(); private constructor() { this.mParseImageUtil = new ParseImageUtil(); - this.runningMaps = new EasyLinkedHashMap(); - this.pendingMaps = new EasyLinkedHashMap(); - this.pausedMaps = new EasyLinkedHashMap(); - // 构造方法传入size 为保存文件个数 this.memoryCache = new MemoryLruCache(100, 100 * 1024 * 1024); @@ -204,7 +206,6 @@ export class ImageKnife { return new CompressBuilder(); } - // 设置缓存张数,缓存大小,单位字节 public setLruCacheSize(size: number, memory: number) { if (this.memoryCache.map.size() <= 0) { @@ -248,28 +249,6 @@ export class ImageKnife { async pauseRequests(): Promise { 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()) }) } @@ -277,28 +256,19 @@ export class ImageKnife { async resumeRequests(): Promise { 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 + if (this.executingJobMap.length > 0) { + this.executingJobMap.forEach((list: List) => { + this.taskpoolLoadResource(list.get(0), Constants.MAIN_HOLDER); + }) + } else { + this.dispatchNextJob(); } - 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) - } + this.executingJobMap.remove(uuid); } // 正常加载 @@ -525,111 +495,38 @@ export class ImageKnife { // 删除执行结束的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); - - 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) - 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(); + if (!this.isPaused) { + //不暂停则继续加载 + this.executingJobMap.remove(request.uuid); + this.dispatchNextJob(); } } // 启动新线程 去磁盘取 去网络取 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) - + if (this.keyNotEmpty(request)) { + if (this.executingJobMap.length > this.maxRequests) { + this.jobQueue.add(request); + return + } + let requestList: List | undefined = this.executingJobMap.get(request.uuid); + if (requestList == undefined) { + requestList = new List(); + requestList.add(request); + this.executingJobMap.set(request.uuid, requestList); + if (!this.isPaused) { + LogUtil.log("loadCacheManager start uuid : " + request.uuid + " url : " + request.loadSrc); + //暂停则不开启加载 this.taskpoolLoadResource(request, Constants.MAIN_HOLDER); } + } else { + requestList.add(request); + this.executingJobMap.set(request.uuid, requestList); + LogUtil.log("loadCacheManager reuse uuid : " + request.uuid + " url : " + request.loadSrc); + return; } - else { - LogUtil.log("key没有生成无法进入存取!") - } + } else { + LogUtil.log("key没有生成无法进入存取!"); } } @@ -726,7 +623,12 @@ export class ImageKnife { } else if (usageType == Constants.MAIN_HOLDER && mainCache) { LogUtil.info("imageknife load mainsource from MemoryCache") mainCache.waitSaveDisk = false; - request.loadComplete(mainCache); + let requestList: List | undefined = this.executingJobMap.get(request.uuid); + if(requestList != undefined) { + requestList.forEach((requestOption: RequestOption)=>{ + requestOption.loadComplete(mainCache as ImageKnifeData); + }) + } return; } //组装可以通过sendable传递的数据 @@ -772,12 +674,22 @@ export class ImageKnife { } else { if ((typeof (data as PixelMap).isEditable) == 'boolean') { let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, data as PixelMap); - request.loadComplete(imageKnifeData) - this.memoryCacheProxy.putValue(request.generateCacheKey,imageKnifeData) + let requestList: List | undefined = this.executingJobMap.get(request.uuid) + if(requestList != undefined) { + requestList.forEach((requestOption: RequestOption)=>{ + requestOption.loadComplete(imageKnifeData); + }) + } + this.memoryCacheProxy.putValue(request.generateCacheKey, imageKnifeData); } else if ((data as GIFFrame[]).length > 0) { let imageKnifeData = ImageKnifeData.createImageGIFFrame(ImageKnifeType.GIFFRAME, data as GIFFrame[]); - request.loadComplete(imageKnifeData) - this.memoryCacheProxy.putValue(request.generateCacheKey,imageKnifeData) + let requestList: List | undefined = this.executingJobMap.get(request.uuid); + if(requestList != undefined) { + requestList.forEach((requestOption: RequestOption)=>{ + requestOption.loadComplete(imageKnifeData); + }) + } + this.memoryCacheProxy.putValue(request.generateCacheKey, imageKnifeData); } else { request.loadError("request resources error") } @@ -785,7 +697,6 @@ export class ImageKnife { }).catch((err: BusinessError | string) => { request.loadError(err) }) - } private keyNotEmpty(request: RequestOption): boolean { @@ -799,40 +710,6 @@ export class ImageKnife { 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) @@ -877,6 +754,21 @@ export class ImageKnife { }) } + + // 分发下一个任务 + dispatchNextJob() { + let request: RequestOption | undefined = this.jobQueue.pop(); + if (request != undefined) { + this.loadCacheManager(request); + } + } + + // 设置请求的最大并发数量 + setMaxRequests(count: number) { + if (count > 0) { + this.maxRequests = count; + } + } } diff --git a/library/src/main/ets/components/imageknife/RequestOption.ets b/library/src/main/ets/components/imageknife/RequestOption.ets index a01a6ad..0d2b0f9 100644 --- a/library/src/main/ets/components/imageknife/RequestOption.ets +++ b/library/src/main/ets/components/imageknife/RequestOption.ets @@ -183,9 +183,6 @@ export class RequestOption { constructor() { // 初始化全局监听 this.requestListeners = new Array(); - // 初始化唯一标识,可以用这个标识找到ImageKnife中的对象 - this.uuid = this.generateUUID(); - let ctx = ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext if (ctx != undefined) { @@ -199,14 +196,7 @@ export class RequestOption { this.transformations = array; } generateUUID(): string { - const uuid = util.generateRandomUUID() - // let d = new Date().getTime(); - // const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(new RegExp("[xy]", "g"), (c) => { - // const r = (d + Math.random() * 16) % 16 | 0; - // d = Math.floor(d / 16); - // return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); - // }); - return uuid; + return SparkMD5.hashBinary(JSON.stringify(this.loadSrc)) as string; } setModuleContext(moduleCtx: common.UIAbilityContext) { @@ -244,6 +234,8 @@ export class RequestOption { load(src: string | PixelMap | Resource) { this.loadSrc = src; + // 初始化唯一标识,可以用这个标识找到ImageKnife中的对象 + this.uuid = this.generateUUID(); return this; } diff --git a/library/src/main/ets/components/imageknife/utils/DefaultJobQueue.ets b/library/src/main/ets/components/imageknife/utils/DefaultJobQueue.ets new file mode 100644 index 0000000..0009872 --- /dev/null +++ b/library/src/main/ets/components/imageknife/utils/DefaultJobQueue.ets @@ -0,0 +1,48 @@ +/* + * 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 { IJobQueue } from './IJobQueue'; +import Queue from '@ohos.util.Queue'; +import taskpool from '@ohos.taskpool'; +import { RequestOption } from '../RequestOption'; + +export class DefaultJobQueue implements IJobQueue { + highQueue: Queue = new Queue(); + normalQueue: Queue = new Queue(); + lowQueue: Queue = new Queue(); + + getQueueLength(): number { + return this.highQueue.length + this.normalQueue.length + this.lowQueue.length; + } + + add(request: RequestOption): void { + if (request.priority === undefined || request.priority === taskpool.Priority.MEDIUM) { + this.normalQueue.add(request); + } else if (request.priority === taskpool.Priority.HIGH) { + this.highQueue.add(request); + } else { + this.lowQueue.add(request); + } + } + + pop(): RequestOption | undefined { + if (this.highQueue.length > 0) { + return this.highQueue.pop(); + } else if (this.normalQueue.length > 0) { + return this.normalQueue.pop(); + } else { + return this.lowQueue.pop(); + } + } +} \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/utils/IJobQueue.ets b/library/src/main/ets/components/imageknife/utils/IJobQueue.ets new file mode 100644 index 0000000..e847744 --- /dev/null +++ b/library/src/main/ets/components/imageknife/utils/IJobQueue.ets @@ -0,0 +1,36 @@ +/* + * 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 { RequestOption } from '../../imageknife/RequestOption'; + +export interface IJobQueue { + + /** + * 获取队列长度 + * @returns + */ + getQueueLength(): number; + + /** + * 往队列中添加元素 + * @param RequestObject + */ + add(request: RequestOption): void; + + /** + * 移除并返回队列头部的元素 + * @returns + */ + pop(): RequestOption | undefined; +}