From a34e20acfa448be5f57d454eadbd49d14ba56f94 Mon Sep 17 00:00:00 2001 From: zgf Date: Thu, 24 Oct 2024 17:42:38 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AD=90=E7=BA=BF=E7=A8=8B=E7=BD=91=E7=BB=9C?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E5=92=8C=E8=87=AA=E5=AE=9A=E4=B9=89=E7=BD=91?= =?UTF-8?q?=E7=BB=9C=E6=94=B9=E4=B8=BA=E5=BC=82=E6=AD=A5=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E3=80=81=E5=A4=8D=E7=94=A8=E5=9C=BA=E6=99=AF=E6=B8=85=E7=A9=BA?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: zgf --- CHANGELOG.md | 9 + README.md | 62 +++---- README_zh.md | 62 +++---- .../src/main/ets/pages/CustomNetImagePage.ets | 78 ++++++++ entry/src/main/ets/pages/Index.ets | 10 + entry/src/main/ets/pages/LoadStatePage.ets | 2 +- entry/src/main/ets/pages/MaxRequest1.ets | 55 ++++++ entry/src/main/ets/pages/MaxRequest2.ets | 55 ++++++ entry/src/main/ets/pages/MaxRequest3.ets | 55 ++++++ .../src/main/ets/pages/SetMaxRequestPage.ets | 23 +++ entry/src/main/ets/pages/SingleImage.ets | 5 +- .../main/ets/pages/TestSetCustomImagePage.ets | 5 +- .../main/ets/pages/model/CommonDataSource.ets | 6 + .../main/resources/base/element/string.json | 8 + .../resources/base/profile/main_pages.json | 7 +- .../main/resources/zh_CN/element/string.json | 8 + library/oh-package.json5 | 2 +- library/src/main/ets/ImageKnife.ets | 6 +- library/src/main/ets/ImageKnifeDispatcher.ets | 65 +++---- library/src/main/ets/ImageKnifeLoader.ets | 174 +++++++++++------- .../ets/components/ImageKnifeComponent.ets | 3 +- library/src/main/ets/model/ImageKnifeData.ets | 2 +- .../src/main/ets/model/ImageKnifeOption.ets | 2 +- library/src/main/ets/utils/Constants.ets | 1 + 24 files changed, 527 insertions(+), 178 deletions(-) create mode 100644 entry/src/main/ets/pages/CustomNetImagePage.ets create mode 100644 entry/src/main/ets/pages/MaxRequest1.ets create mode 100644 entry/src/main/ets/pages/MaxRequest2.ets create mode 100644 entry/src/main/ets/pages/MaxRequest3.ets create mode 100644 entry/src/main/ets/pages/SetMaxRequestPage.ets diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ff5214..cae97f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## 3.2.0-rc.0 +- Rollback the old version V1 decorator. V2 decorator will be provided in version 4.x +- The sub-thread network request is changed to asynchronous, thereby increasing the number of concurrent sub-thread network requests +- Set the concurrency through the setMaxRequests interface under the ImageKnife class +- aboutToRecycle life cycle clear image content + +## 3.0.3 +- Released version 3.0.3 + ## 3.0.3-rc.0 - Custom network method to add request header parameters diff --git a/README.md b/README.md index bf787e0..c1a3ab4 100644 --- a/README.md +++ b/README.md @@ -56,12 +56,12 @@ await ImageKnife.getInstance().initFileCache(context, 256, 256 * 1024 * 1024) ``` ImageKnifeComponent({ - ImageKnifeOption: new ImageKnifeOption({ + ImageKnifeOption:{ loadSrc: $r("app.media.app_icon"), placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Auto - }) + } }).width(100).height(100) ``` @@ -69,12 +69,12 @@ ImageKnifeComponent({ ``` ImageKnifeComponent({ - ImageKnifeOption: new ImageKnifeOption({ + ImageKnifeOption: { loadSrc: this.localFile, placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Auto - }) + } }).width(100).height(100) ``` @@ -82,12 +82,12 @@ ImageKnifeComponent({ ``` ImageKnifeComponent({ - ImageKnifeOption: new ImageKnifeOption({ + ImageKnifeOption: { loadSrc:"https://www.openharmony.cn/_nuxt/img/logo.dcf95b3.png", placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Auto - }) + } }).width(100).height(100) ``` @@ -95,13 +95,13 @@ ImageKnifeComponent({ ``` ImageKnifeComponent({ - ImageKnifeOption: new ImageKnifeOption({ + ImageKnifeOption: { loadSrc: "https://file.atomgit.com/uploads/user/1704857786989_8994.jpeg", placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Auto, customGetImage: custom - }) + } }).width(100).height(100) // Custom implementation of the image acquisition method, such as custom network download。 @@ -117,33 +117,33 @@ async function custom(context: Context, src: string | PixelMap | Resource): Prom ``` ImageKnifeComponent({ - ImageKnifeOption: new ImageKnifeOption({ + ImageKnifeOption: { loadSrc:"https://www.openharmony.cn/_nuxt/img/logo.dcf95b3.png", progressListener:(progress:number)=>{console.info("ImageKinfe:: call back progress = " + progress)} - }) + } }).width(100).height(100) ``` #### 6. Setting Border Options ``` -ImageKnifeComponent({ ImageKnifeOption: new ImageKnifeOption( +ImageKnifeComponent({ ImageKnifeOption: { loadSrc: $r("app.media.rabbit"), border: {radius:50} - }) + } }).width(100).height(100) ``` #### 7. Setting Image Transformation Options ``` -ImageKnifeComponent({ ImageKnifeOption: new ImageKnifeOption( +ImageKnifeComponent({ ImageKnifeOption: { loadSrc: $r("app.media.rabbit"), border: {radius:50}, transformation: new BlurTransformation(3) - }) + } }).width(100).height(100) ``` Multiple combined transformation usages: @@ -153,14 +153,14 @@ let transformations: collections.Array = new collections transformations.push(new BlurTransformation(5)); transformations.push(new BrightnessTransformation(0.2)); ImageKnifeComponent({ - imageKnifeOption: new ImageKnifeOption({ + imageKnifeOption: { loadSrc: $r('app.media.pngSample'), placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Contain, border: { radius: { topLeft: 50, bottomRight: 50 } }, // Rounded corner settings transformation: transformations.length > 0 ? new MultiTransTransformation(transformations) : undefined // Graphic transformation group -}) +} }).width(300) .height(300) .rotate ({angle: 90}) // Rotate by 90 degrees. @@ -171,12 +171,12 @@ Other transformation-related properties can be stacked to achieve combined trans Example of circular cropping transformation: ``` -ImageKnifeComponent({ ImageKnifeOption:new ImageKnifeOption( +ImageKnifeComponent({ ImageKnifeOption: { loadSrc: $r('app.media.pngSample'), objectFit: ImageFit.Cover, border: { radius: 150 } -}) +} }).width(300) .height(300) ``` @@ -184,12 +184,12 @@ ImageKnifeComponent({ ImageKnifeOption:new ImageKnifeOption( Example of Circular cropping with border transformation: ``` -ImageKnifeComponent({ ImageKnifeOption:new ImageKnifeOption( +ImageKnifeComponent({ ImageKnifeOption: { loadSrc: $r('app.media.pngSample'), objectFit: ImageFit.Cover, border: { radius: 150, color: Color.Red, width: 5 } -}) +} }).width(300) .height(300) ``` @@ -198,9 +198,9 @@ Example of contrast filtering transformation: ``` ImageKnifeComponent({ - imageKnifeOption: new ImageKnifeOption({ + imageKnifeOption: { loadSrc: $r('app.media.pngSample') - }) + } }).width(300) .height(300) .contrast(12) @@ -210,9 +210,9 @@ Example of rotation transformation: ``` ImageKnifeComponent({ - imageKnifeOption: new ImageKnifeOption({ + imageKnifeOption:({ loadSrc: $r('app.media.pngSample') - }) + } }).width(300) .height(300) .rotate({angle:90}) @@ -222,7 +222,7 @@ ImageKnifeComponent({ #### 8. Listening for Image Loading Success and Failure ``` -ImageKnifeComponent({ ImageKnifeOption: new ImageKnifeOption( +ImageKnifeComponent({ ImageKnifeOption: { loadSrc: $r("app.media.rabbit"), onLoadListener:{ @@ -241,27 +241,27 @@ ImageKnifeComponent({ ImageKnifeOption: new ImageKnifeOption( console.info(err) } } - }) + } }).width(100).height(100) ``` #### 9. Use of syncLoad **syncLoad** sets whether to load the image synchronously. By default, the image is loaded asynchronously. When loading a small image, you are advised to set **syncLoad** to **true** so that the image loading can be quickly completed on the main thread. ``` ImageKnifeComponent({ - imageKnifeOption:new ImageKnifeOption({ + imageKnifeOption:{ loadSrc:$r("app.media.pngSample"), placeholderSrc:$r("app.media.loading") - }),syncLoad:true + },syncLoad:true }) ``` #### 10. Use of ImageKnifeAnimatorComponent ``` ImageKnifeAnimatorComponent({ - imageKnifeOption:new ImageKnifeOption({ + imageKnifeOption:{ loadSrc:"https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", placeholderSrc:$r('app.media.loading'), errorholderSrc:$r('app.media.failed') - }),animatorOption:this.animatorOption + },animatorOption:this.animatorOption }).width(300).height(300).backgroundColor(Color.Orange).margin({top:30}) ``` #### Reuse Scenario @@ -359,7 +359,7 @@ Method 2: Set the third-party HAR as a dependency in the **oh-package.json5** fi This project has been verified in the following version: -DevEco Studio: 5.0 Canary3 (5.0.3.502), SDK: API 12 (5.0.0.31) +DevEco Studio: NEXT Beta1-5.0.3.806, SDK: API12 Release(5.0.0.66) ## How to Contribute diff --git a/README_zh.md b/README_zh.md index 5068e66..53bed8f 100644 --- a/README_zh.md +++ b/README_zh.md @@ -56,12 +56,12 @@ await ImageKnife.getInstance().initFileCache(context, 256, 256 * 1024 * 1024) ``` ImageKnifeComponent({ - ImageKnifeOption: new ImageKnifeOption({ + ImageKnifeOption: { loadSrc: $r("app.media.app_icon"), placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Auto - }) + } }).width(100).height(100) ``` @@ -69,12 +69,12 @@ ImageKnifeComponent({ ``` ImageKnifeComponent({ - ImageKnifeOption: new ImageKnifeOption({ + ImageKnifeOption: { loadSrc: this.localFile, placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Auto - }) + } }).width(100).height(100) ``` @@ -82,12 +82,12 @@ ImageKnifeComponent({ ``` ImageKnifeComponent({ - ImageKnifeOption: new ImageKnifeOption({ + ImageKnifeOption: { loadSrc:"https://www.openharmony.cn/_nuxt/img/logo.dcf95b3.png", placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Auto - }) + } }).width(100).height(100) ``` @@ -95,13 +95,13 @@ ImageKnifeComponent({ ``` ImageKnifeComponent({ - ImageKnifeOption: new ImageKnifeOption({ + ImageKnifeOption: { loadSrc: "https://file.atomgit.com/uploads/user/1704857786989_8994.jpeg", placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Auto, customGetImage: custom - }) + } }).width(100).height(100) // 自定义实现图片获取方法,如自定义网络下载 @@ -117,33 +117,33 @@ async function custom(context: Context, src: string | PixelMap | Resource): Prom ``` ImageKnifeComponent({ - ImageKnifeOption: new ImageKnifeOption({ + ImageKnifeOption: { loadSrc:"https://www.openharmony.cn/_nuxt/img/logo.dcf95b3.png", progressListener:(progress:number)=>{console.info("ImageKinfe:: call back progress = " + progress)} - }) + } }).width(100).height(100) ``` #### 6.支持option传入border,设置边框,圆角 ``` -ImageKnifeComponent({ ImageKnifeOption: new ImageKnifeOption( +ImageKnifeComponent({ ImageKnifeOption: { loadSrc: $r("app.media.rabbit"), border: {radius:50} - }) + } }).width(100).height(100) ``` #### 7.支持option图片变换 ``` -ImageKnifeComponent({ ImageKnifeOption: new ImageKnifeOption( +ImageKnifeComponent({ ImageKnifeOption: { loadSrc: $r("app.media.rabbit"), border: {radius:50}, transformation: new BlurTransformation(3) - }) + } }).width(100).height(100) ``` 多种组合变换用法 @@ -153,14 +153,14 @@ let transformations: collections.Array = new collections transformations.push(new BlurTransformation(5)); transformations.push(new BrightnessTransformation(0.2)); ImageKnifeComponent({ - imageKnifeOption: new ImageKnifeOption({ + imageKnifeOption: { loadSrc: $r('app.media.pngSample'), placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Contain, border: { radius: { topLeft: 50, bottomRight: 50 } }, // 圆角设置 transformation: transformations.length > 0 ? new MultiTransTransformation(transformations) : undefined // 图形变换组 -}) +} }).width(300) .height(300) .rotate({ angle: 90 }) // 旋转90度 @@ -171,12 +171,12 @@ ImageKnifeComponent({ 圆形裁剪变换示例 ``` -ImageKnifeComponent({ ImageKnifeOption:new ImageKnifeOption( +ImageKnifeComponent({ ImageKnifeOption: { loadSrc: $r('app.media.pngSample'), objectFit: ImageFit.Cover, border: { radius: 150 } -}) +} }).width(300) .height(300) ``` @@ -184,12 +184,12 @@ ImageKnifeComponent({ ImageKnifeOption:new ImageKnifeOption( 圆形裁剪带边框变换示例 ``` -ImageKnifeComponent({ ImageKnifeOption:new ImageKnifeOption( +ImageKnifeComponent({ ImageKnifeOption: { loadSrc: $r('app.media.pngSample'), objectFit: ImageFit.Cover, border: { radius: 150, color: Color.Red, width: 5 } -}) +} }).width(300) .height(300) ``` @@ -198,9 +198,9 @@ ImageKnifeComponent({ ImageKnifeOption:new ImageKnifeOption( ``` ImageKnifeComponent({ - imageKnifeOption: new ImageKnifeOption({ + imageKnifeOption: { loadSrc: $r('app.media.pngSample') - }) + } }).width(300) .height(300) .contrast(12) @@ -210,9 +210,9 @@ ImageKnifeComponent({ ``` ImageKnifeComponent({ - imageKnifeOption: new ImageKnifeOption({ + imageKnifeOption: { loadSrc: $r('app.media.pngSample') - }) + } }).width(300) .height(300) .rotate({angle:90}) @@ -222,7 +222,7 @@ ImageKnifeComponent({ #### 8.监听图片加载成功与失败 ``` -ImageKnifeComponent({ ImageKnifeOption: new ImageKnifeOption( +ImageKnifeComponent({ ImageKnifeOption: { loadSrc: $r("app.media.rabbit"), onLoadListener:{ @@ -241,27 +241,27 @@ ImageKnifeComponent({ ImageKnifeOption: new ImageKnifeOption( console.info(err) } } - }) + } }).width(100).height(100) ``` #### 9.ImageKnifeComponent - syncLoad 设置是否同步加载图片,默认是异步加载。建议加载尺寸较小的Resource图片时将syncLoad设为true,因为耗时较短,在主线程上执行即可 ``` ImageKnifeComponent({ - imageKnifeOption:new ImageKnifeOption({ + imageKnifeOption:{ loadSrc:$r("app.media.pngSample"), placeholderSrc:$r("app.media.loading") - }),syncLoad:true + },syncLoad:true }) ``` #### 10.ImageKnifeAnimatorComponent 示例 ``` ImageKnifeAnimatorComponent({ - imageKnifeOption:new ImageKnifeOption({ + imageKnifeOption: { loadSrc:"https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", placeholderSrc:$r('app.media.loading'), errorholderSrc:$r('app.media.failed') - }),animatorOption:this.animatorOption + },animatorOption:this.animatorOption }).width(300).height(300).backgroundColor(Color.Orange).margin({top:30}) ``` #### 复用场景 @@ -361,7 +361,7 @@ ImageKnifeAnimatorComponent({ ## 约束与限制 在下述版本验证通过: -DevEco Studio 5.0 Canary3(5.0.3.502)--SDK:API12 (5.0.0.31) +DevEco Studio: NEXT Beta1-5.0.3.806, SDK: API12 Release(5.0.0.66) ## 贡献代码 diff --git a/entry/src/main/ets/pages/CustomNetImagePage.ets b/entry/src/main/ets/pages/CustomNetImagePage.ets new file mode 100644 index 0000000..8d286fd --- /dev/null +++ b/entry/src/main/ets/pages/CustomNetImagePage.ets @@ -0,0 +1,78 @@ +/* + * 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 { InfoItem } from './model/DataSourcePrefetching'; +import { PageViewModel } from './model/PageViewModel'; +import { ImageKnifeComponent, ImageKnife } from '@ohos/libraryimageknife'; +import { CommonDataSource, GetSession } from './model/CommonDataSource'; +import { rcp } from '@kit.RemoteCommunicationKit'; +import { BusinessError } from '@kit.BasicServicesKit'; + +@Entry +@Component +export struct CustomNetImagePage { + @State hotCommendList:CommonDataSource = new CommonDataSource([]) + aboutToAppear(): void { + ImageKnife.getInstance().setMaxRequests(32) + this.hotCommendList.addData(this.hotCommendList.totalCount(),PageViewModel.getItems()) + } + aboutToDisappear(): void { + ImageKnife.getInstance().setMaxRequests(8) + } + build() { + Column() { + WaterFlow() { + LazyForEach(this.hotCommendList,(item: InfoItem)=>{ + FlowItem() { + Column(){ + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: item.albumUrl, + placeholderSrc: $r("app.media.loading"), + errorholderSrc: $r("app.media.failed"), + customGetImage:custom + } + }).width("50%").height(200) + } + }.height(200) + .backgroundColor("#95efd2") + },(item: string) => item) + }.columnsTemplate("1fr 1fr") + .columnsGap(10) + .rowsGap(5) + .backgroundColor(0xFAEEE0) + .width("100%").height("100%") + } + } +} +// 自定义下载方法 +@Concurrent +async function custom(context: Context, src: string | PixelMap | Resource,headers?: Record): Promise { + return new Promise((resolve,reject)=>{ + if (typeof src == "string") { + let session = GetSession.session + let req = new rcp.Request(src,"GET"); + session.fetch(req).then((response)=>{ + if(response.statusCode == 200) { + let buffer = response.body + resolve(buffer) + } else { + reject("rcp code:"+response.statusCode) + } + }).catch((err:BusinessError)=>{ + reject("error rcp src:"+src+",err:"+JSON.stringify(err)) + }) + } + }) +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets index 45d7e95..c6606ed 100644 --- a/entry/src/main/ets/pages/Index.ets +++ b/entry/src/main/ets/pages/Index.ets @@ -45,11 +45,21 @@ struct Index { uri: 'pages/PrefetchAndCacheCount', }); }) + Button($r('app.string.request_concurrency')).margin({top:10}).onClick(()=>{ + router.push({ + uri: 'pages/SetMaxRequestPage', + }); + }) Button($r('app.string.Test_multiple_images')).margin({top:10}).onClick(()=>{ router.push({ uri: 'pages/TestCommonImage', }); }) + Button($r('app.string.Customize_RCP_network')).margin({top:10}).onClick(()=>{ + router.push({ + uri: 'pages/CustomNetImagePage', + }); + }) Button($r('app.string.Test_Task_error')).margin({top:10}).onClick(()=>{ router.push({ uri: 'pages/TestTaskResourcePage', diff --git a/entry/src/main/ets/pages/LoadStatePage.ets b/entry/src/main/ets/pages/LoadStatePage.ets index ceeae99..5f0906e 100644 --- a/entry/src/main/ets/pages/LoadStatePage.ets +++ b/entry/src/main/ets/pages/LoadStatePage.ets @@ -109,7 +109,7 @@ struct LoadStatePage { } // 自定义下载方法 @Concurrent -async function custom(context: Context, src: string | PixelMap | Resource): Promise { +async function custom(context: Context, src: string | PixelMap | Resource,headers?: Record): Promise { console.info("ImageKnife:: custom download:" + src) // 举例写死从本地文件读取,也可以自己请求网络图片 return undefined diff --git a/entry/src/main/ets/pages/MaxRequest1.ets b/entry/src/main/ets/pages/MaxRequest1.ets new file mode 100644 index 0000000..9d103d1 --- /dev/null +++ b/entry/src/main/ets/pages/MaxRequest1.ets @@ -0,0 +1,55 @@ +/* + * 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,ImageKnife } from '@ohos/libraryimageknife'; +import { CommonDataSource } from './model/CommonDataSource'; + +@Entry +@Component +struct MaxRequest1 { + @State hotCommendList:CommonDataSource = new CommonDataSource([]) + private data: Array = [] + aboutToAppear(): void { + ImageKnife.getInstance().setMaxRequests(8) + for (let index = 0; index < 200; index++) { + this.data.push(`https://img-blog.csdn.net/20140514114029140?${index}`) + } + this.hotCommendList.addData(this.hotCommendList.totalCount(),this.data) + } + build() { + Column() { + WaterFlow() { + LazyForEach(this.hotCommendList,(item: string)=>{ + FlowItem() { + Column(){ + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: item, + placeholderSrc: $r("app.media.loading"), + errorholderSrc: $r("app.media.failed"), + } + }).width("50%").height(160) + } + }.height(200) + .backgroundColor("#95efd2") + },(item: string) => item) + }.columnsTemplate("1fr 1fr") + .cachedCount(8) + .columnsGap(10) + .rowsGap(5) + .backgroundColor(0xFAEEE0) + .width("100%").height("100%") + } + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/MaxRequest2.ets b/entry/src/main/ets/pages/MaxRequest2.ets new file mode 100644 index 0000000..5599811 --- /dev/null +++ b/entry/src/main/ets/pages/MaxRequest2.ets @@ -0,0 +1,55 @@ +/* + * 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,ImageKnife } from '@ohos/libraryimageknife'; +import { CommonDataSource } from './model/CommonDataSource'; + +@Entry +@Component +struct MaxRequest2 { + @State hotCommendList:CommonDataSource = new CommonDataSource([]) + private data: Array = [] + aboutToAppear(): void { + ImageKnife.getInstance().setMaxRequests(20) + for (let index = 200; index < 400; index++) { + this.data.push(`https://img-blog.csdn.net/20140514114029140?${index}`) + } + this.hotCommendList.addData(this.hotCommendList.totalCount(),this.data) + } + build() { + Column() { + WaterFlow() { + LazyForEach(this.hotCommendList,(item: string)=>{ + FlowItem() { + Column(){ + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: item, + placeholderSrc: $r("app.media.loading"), + errorholderSrc: $r("app.media.failed"), + } + }).width("50%").height(160) + } + }.height(200) + .backgroundColor("#95efd2") + },(item: string) => item) + }.columnsTemplate("1fr 1fr") + .cachedCount(20) + .columnsGap(10) + .rowsGap(5) + .backgroundColor(0xFAEEE0) + .width("100%").height("100%") + } + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/MaxRequest3.ets b/entry/src/main/ets/pages/MaxRequest3.ets new file mode 100644 index 0000000..0813da5 --- /dev/null +++ b/entry/src/main/ets/pages/MaxRequest3.ets @@ -0,0 +1,55 @@ +/* + * 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,ImageKnife } from '@ohos/libraryimageknife'; +import { CommonDataSource } from './model/CommonDataSource'; + +@Entry +@Component +struct MaxRequest3 { + @State hotCommendList:CommonDataSource = new CommonDataSource([]) + private data: Array = [] + aboutToAppear(): void { + ImageKnife.getInstance().setMaxRequests(32) + for (let index = 400; index < 600; index++) { + this.data.push(`https://img-blog.csdn.net/20140514114029140?${index}`) + } + this.hotCommendList.addData(this.hotCommendList.totalCount(),this.data) + } + build() { + Column() { + WaterFlow() { + LazyForEach(this.hotCommendList,(item: string)=>{ + FlowItem() { + Column(){ + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: item, + placeholderSrc: $r("app.media.loading"), + errorholderSrc: $r("app.media.failed"), + } + }).width("50%").height(160) + } + }.height(200) + .backgroundColor("#95efd2") + },(item: string) => item) + }.columnsTemplate("1fr 1fr") + .cachedCount(40) + .columnsGap(10) + .rowsGap(5) + .backgroundColor(0xFAEEE0) + .width("100%").height("100%") + } + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/SetMaxRequestPage.ets b/entry/src/main/ets/pages/SetMaxRequestPage.ets new file mode 100644 index 0000000..25622df --- /dev/null +++ b/entry/src/main/ets/pages/SetMaxRequestPage.ets @@ -0,0 +1,23 @@ +import { router } from '@kit.ArkUI' + +@Entry +@Component +struct SetMaxRequestPage { + build() { + Column() { + Button("maxRequest = 8") + .onClick(()=>{ + router.pushUrl({url:"pages/MaxRequest1"}) + }) + Button("maxRequest = 20") + .onClick(()=>{ + router.pushUrl({url:"pages/MaxRequest2"}) + }).margin({top:20}) + Button("maxRequest = 32") + .onClick(()=>{ + router.pushUrl({url:"pages/MaxRequest2"}) + }).margin({top:20}) + }.width("100%") + .height("100%") + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/SingleImage.ets b/entry/src/main/ets/pages/SingleImage.ets index c0bd805..1c2c8af 100644 --- a/entry/src/main/ets/pages/SingleImage.ets +++ b/entry/src/main/ets/pages/SingleImage.ets @@ -128,10 +128,11 @@ struct SingleImage { // 自定义下载方法 @Concurrent -async function custom(context: Context, src: string | PixelMap | Resource,headers?: Record): Promise { +async function custom(context: Context, src: string | PixelMap | Resource,headers?: Record): Promise { let refer = headers!["refer"] as string console.info("ImageKnife:: custom download:" + src,"refer:"+refer) // 举例写死从本地文件读取,也可以自己请求网络图片 - return context.resourceManager.getMediaContentSync($r("app.media.startIcon").id).buffer as ArrayBuffer + let buffer = context.resourceManager.getMediaContentSync($r("app.media.startIcon").id).buffer as ArrayBuffer + return buffer } diff --git a/entry/src/main/ets/pages/TestSetCustomImagePage.ets b/entry/src/main/ets/pages/TestSetCustomImagePage.ets index 79b4561..371a889 100644 --- a/entry/src/main/ets/pages/TestSetCustomImagePage.ets +++ b/entry/src/main/ets/pages/TestSetCustomImagePage.ets @@ -60,8 +60,9 @@ struct TestSetCustomImagePage { } } @Concurrent -async function custom(context: Context, src: string | PixelMap | Resource): Promise { +async function custom(context: Context, src: string | PixelMap | Resource,headers?: Record): Promise { console.info("ImageKnife:: custom download:" + src) // 举例写死从本地文件读取,也可以自己请求网络图片 - return context.resourceManager.getMediaContentSync($r("app.media.pngSample").id).buffer as ArrayBuffer + let buffer = context.resourceManager.getMediaContentSync($r("app.media.pngSample").id).buffer as ArrayBuffer + return buffer } \ No newline at end of file diff --git a/entry/src/main/ets/pages/model/CommonDataSource.ets b/entry/src/main/ets/pages/model/CommonDataSource.ets index d6b16c0..c6b61cd 100644 --- a/entry/src/main/ets/pages/model/CommonDataSource.ets +++ b/entry/src/main/ets/pages/model/CommonDataSource.ets @@ -12,6 +12,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { statfs } from '@kit.CoreFileKit' +import { rcp } from '@kit.RemoteCommunicationKit' + export class CommonDataSource implements IDataSource { private dataArray: T[] = [] private listeners: DataChangeListener[] = [] @@ -51,4 +54,7 @@ export class CommonDataSource implements IDataSource { listener.onDataAdd(index) }) } +} +export class GetSession { + static session:rcp.Session = rcp.createSession() } \ No newline at end of file diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json index e5303d5..f4950aa 100644 --- a/entry/src/main/resources/base/element/string.json +++ b/entry/src/main/resources/base/element/string.json @@ -503,6 +503,14 @@ { "name": "disk", "value": "Disk" + }, + { + "name": "Customize_RCP_network", + "value": "Customize RCP network request" + }, + { + "name": "request_concurrency", + "value": "Set request concurrency" } ] } \ 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 981612c..0b15726 100644 --- a/entry/src/main/resources/base/profile/main_pages.json +++ b/entry/src/main/resources/base/profile/main_pages.json @@ -31,6 +31,11 @@ "pages/PrefetchAndPreload", "pages/TestCacheDataPage", "pages/TestChangeColorPage", - "pages/TestLoadCancelListenerPage" + "pages/TestLoadCancelListenerPage", + "pages/CustomNetImagePage", + "pages/SetMaxRequestPage", + "pages/MaxRequest1", + "pages/MaxRequest2", + "pages/MaxRequest3" ] } \ No newline at end of file diff --git a/entry/src/main/resources/zh_CN/element/string.json b/entry/src/main/resources/zh_CN/element/string.json index da35ecd..01fa780 100644 --- a/entry/src/main/resources/zh_CN/element/string.json +++ b/entry/src/main/resources/zh_CN/element/string.json @@ -499,6 +499,14 @@ { "name": "disk", "value": "磁盘" + }, + { + "name": "Customize_RCP_network", + "value": "自定义rcp网络请求" + }, + { + "name": "request_concurrency", + "value": "设置请求并发度" } ] } \ No newline at end of file diff --git a/library/oh-package.json5 b/library/oh-package.json5 index d750e5c..4d5208b 100644 --- a/library/oh-package.json5 +++ b/library/oh-package.json5 @@ -14,7 +14,7 @@ "main": "index.ets", "repository": "https://gitee.com/openharmony-tpc/ImageKnife", "type": "module", - "version": "3.0.3-rc.0", + "version": "3.2.0-rc.0", "dependencies": { "@ohos/gpu_transform": "^1.0.2" }, diff --git a/library/src/main/ets/ImageKnife.ets b/library/src/main/ets/ImageKnife.ets index c655e44..41038ee 100644 --- a/library/src/main/ets/ImageKnife.ets +++ b/library/src/main/ets/ImageKnife.ets @@ -38,7 +38,7 @@ export class ImageKnife { private _isRequestInSubThread: boolean = true; //定义全局网络请求header map headerMap: Map = new Map(); - customGetImage: ((context: Context, src: string | PixelMap | Resource) => Promise) | undefined = undefined + customGetImage: ((context: Context, src: string | PixelMap | Resource,headers?: Record) => Promise) | undefined = undefined public static getInstance(): ImageKnife { if (!ImageKnife.instance) { ImageKnife.instance = new ImageKnife(); @@ -482,10 +482,10 @@ export class ImageKnife { * 全局设置自定义下载 * @param customGetImage 自定义请求函数 */ - setCustomGetImage(customGetImage?: (context: Context, src: string | PixelMap | Resource,headers?: Record) => Promise) { + setCustomGetImage(customGetImage?: (context: Context, src: string | PixelMap | Resource,headers?: Record) => Promise) { this.customGetImage = customGetImage } - getCustomGetImage(): undefined | ((context: Context, src: string | PixelMap | Resource,headers?: Record) => Promise){ + getCustomGetImage(): undefined | ((context: Context, src: string | PixelMap | Resource,headers?: Record) => Promise){ return this.customGetImage } } \ No newline at end of file diff --git a/library/src/main/ets/ImageKnifeDispatcher.ets b/library/src/main/ets/ImageKnifeDispatcher.ets index e99d621..a6a2af6 100644 --- a/library/src/main/ets/ImageKnifeDispatcher.ets +++ b/library/src/main/ets/ImageKnifeDispatcher.ets @@ -189,6 +189,16 @@ export class ImageKnifeDispatcher { if(request.customGetImage == undefined) { request.customGetImage = ImageKnife.getInstance().getCustomGetImage() } + emitter.on(Constants.CALLBACK_EMITTER + memoryKey,(data)=>{ + emitter.off(Constants.CALLBACK_EMITTER + memoryKey) + let res = data?.data?.value as RequestJobResult | undefined + this.doTaskCallback(res, requestList!, currentRequest, memoryKey, imageSrc, requestSource,isAnimator); + if (isWatchProgress){ + emitter.off(Constants.PROGRESS_EMITTER + memoryKey) + } + LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.end:"+currentRequest.imageKnifeOption.loadSrc) + LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:"+currentRequest.imageKnifeOption.loadSrc) + }) if (ImageKnife.getInstance().isRequestInSubThread){ // 启动线程下载和解码主图 LogUtil.log("ImageKnife_DataTime_getAndShowImage_Task.start:" + currentRequest.imageKnifeOption.loadSrc) @@ -202,13 +212,14 @@ export class ImageKnifeDispatcher { LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.start(subthread):" + currentRequest.imageKnifeOption.loadSrc) taskpool.execute(task).then((res: Object) => { - this.doTaskCallback(res as RequestJobResult | undefined, requestList!, currentRequest, memoryKey, imageSrc, requestSource,isAnimator); - if (isWatchProgress){ - emitter.off(Constants.PROGRESS_EMITTER + memoryKey) - } - LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.end:"+currentRequest.imageKnifeOption.loadSrc) - LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:"+currentRequest.imageKnifeOption.loadSrc) + // this.doTaskCallback(res as RequestJobResult | undefined, requestList!, currentRequest, memoryKey, imageSrc, requestSource,isAnimator); + // if (isWatchProgress){ + // emitter.off(Constants.PROGRESS_EMITTER + memoryKey) + // } + // LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.end:"+currentRequest.imageKnifeOption.loadSrc) + // LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:"+currentRequest.imageKnifeOption.loadSrc) }).catch((err: BusinessError) => { + emitter.off(Constants.CALLBACK_EMITTER + memoryKey) LogUtil.error("Fail to requestJob in sub thread src=" + imageSrc + " err=" + err) LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:" + currentRequest.imageKnifeOption.loadSrc) if (isWatchProgress){ @@ -219,11 +230,12 @@ export class ImageKnifeDispatcher { }) } else { //主线程请求 LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.start(mainthread):" + currentRequest.imageKnifeOption.loadSrc) - requestJob(request, requestList).then((res: RequestJobResult | undefined) => { - this.doTaskCallback(res, requestList!, currentRequest, memoryKey, imageSrc, requestSource,isAnimator); - LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.end:"+currentRequest.imageKnifeOption.loadSrc) - LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:"+currentRequest.imageKnifeOption.loadSrc) + requestJob(request, requestList).then(() => { + // this.doTaskCallback(res, requestList!, currentRequest, memoryKey, imageSrc, requestSource,isAnimator); + // LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.end:"+currentRequest.imageKnifeOption.loadSrc) + // LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:"+currentRequest.imageKnifeOption.loadSrc) }).catch((err: BusinessError) => { + emitter.off(Constants.CALLBACK_EMITTER + memoryKey) LogUtil.error("Fail to requestJob in main thread src=" + imageSrc + " err=" + err) LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:" + currentRequest.imageKnifeOption.loadSrc) this.executingJobMap.remove(memoryKey); @@ -392,44 +404,15 @@ export class ImageKnifeDispatcher { * @returns */ @Concurrent -async function requestJob(request: RequestJobRequest, requestList?: List): Promise { +async function requestJob(request: RequestJobRequest, requestList?: List) { LogUtil.log("ImageKnife_DataTime_requestJob.start:" + request.src + " requestSource=" + request.requestSource) let src = typeof request.src == "number" ? request.resName != undefined ? request.resName : request.src + "" : request.src // 生成文件缓存key let fileKey = request.engineKey.generateFileKey(src, request.signature, request.isAnimator) //获取图片资源 - let resBuf: ArrayBuffer - try { - LogUtil.log("ImageKnife_DataTime_requestJob.getImageArrayBuffer.start:" + request.src) - resBuf = await ImageKnifeLoader.getImageArrayBuffer(request, requestList, fileKey) - LogUtil.log("ImageKnife_DataTime_requestJob.getImageArrayBuffer.end:" + request.src) - } catch (error) { - LogUtil.error("ImageKnife_DataTime_requestJob.end: getImageArrayBuffer error " + request.src + " err=" + error) - return ImageKnifeLoader.makeEmptyResult(error) - } + ImageKnifeLoader.execute(request,requestList,fileKey) - // 获取图片类型 - let typeValue = new FileTypeUtil().getFileType(resBuf); - if(typeValue == null) { - LogUtil.log("ImageKnife_DataTime_requestJob.end: getFileType is null " + request.src) - return ImageKnifeLoader.makeEmptyResult("request is not a valid image source") - } - - // 解析图片 - LogUtil.log("ImageKnife_DataTime_requestJob.parseImage.start:" + request.src) - let result: RequestJobResult = await ImageKnifeLoader.parseImage(resBuf, typeValue, fileKey, request) - LogUtil.log("ImageKnife_DataTime_requestJob.parseImage.end:" + request.src) - - // 图形变化 - if (request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined && result?.pixelMap !== undefined && typeof result.pixelMap !== 'string') { - LogUtil.log("ImageKnife_DataTime_requestJob.transform.start:" + request.src) - result.pixelMap = await request.transformation?.transform(request.context, result.pixelMap, request.componentWidth, request.componentHeight); - LogUtil.log("ImageKnife_DataTime_requestJob.transform.end:" + request.src) - } - - LogUtil.log("ImageKnife_DataTime_requestJob.end:" + request.src) - return result } diff --git a/library/src/main/ets/ImageKnifeLoader.ets b/library/src/main/ets/ImageKnifeLoader.ets index a85a51a..f8cd230 100644 --- a/library/src/main/ets/ImageKnifeLoader.ets +++ b/library/src/main/ets/ImageKnifeLoader.ets @@ -28,6 +28,7 @@ import emitter from '@ohos.events.emitter'; import image from '@ohos.multimedia.image'; import { RequestJobResult } from './model/ImageKnifeData' import util from '@ohos.util'; +import { FileTypeUtil } from './utils/FileTypeUtil'; class RequestData { receiveSize: number = 2000 @@ -38,38 +39,52 @@ class RequestData { * ImageKnifeDispatcher 抽取出来的方法,因@Concurrent只能import方法,故抽取到另一个类 */ export class ImageKnifeLoader { - static async parseImage(resBuf: ArrayBuffer, typeValue: string, fileKey: string, - request: RequestJobRequest): Promise { + static execute(request: RequestJobRequest, requestList: List | undefined, fileKey: string){ + ImageKnifeLoader.getImageArrayBuffer(request,requestList,fileKey) + } + static async parseImage(resBuf: ArrayBuffer, fileKey: string, + request: RequestJobRequest) { + let typeValue = new FileTypeUtil().getFileType(resBuf); + if(typeValue == null) { + LogUtil.log("ImageKnife_DataTime_requestJob.end: getFileType is null " + request.src) + ImageKnifeLoader.makeEmptyResult(request,"request is not a valid image source") + return + } if(request.isAnimator) { - return ImageKnifeLoader.parseForAnimatorComponent(resBuf ,typeValue ,fileKey, request) + ImageKnifeLoader.parseForAnimatorComponent(resBuf ,typeValue ,fileKey, request) + return } if (typeValue === 'gif' || typeValue === 'webp') { - return ImageKnifeLoader.parseAnimatorImage(resBuf ,typeValue ,fileKey , request) + ImageKnifeLoader.parseAnimatorImage(resBuf ,typeValue ,fileKey , request) + return } else if(typeValue == "svg") { - return ImageKnifeLoader.parseSvgImage(resBuf ,typeValue ,fileKey , request) + ImageKnifeLoader.parseSvgImage(resBuf ,typeValue ,fileKey , request) + return } - return ImageKnifeLoader.parseNormalImage(resBuf, typeValue, fileKey, request) + ImageKnifeLoader.parseNormalImage(resBuf, typeValue, fileKey, request) } - static makeEmptyResult(error: string): RequestJobResult{ - return { + static makeEmptyResult(request:RequestJobRequest,error: string){ + let res: RequestJobResult = { pixelMap: undefined, bufferSize: 0, fileKey: '', loadFail: error, } + emitter.emit(Constants.CALLBACK_EMITTER + request.memoryKey, { data: { "value": res } }) } - static async parseNormalImage(resBuf: ArrayBuffer, typeValue: string, fileKey: string, request: RequestJobRequest):Promise { + static async parseNormalImage(resBuf: ArrayBuffer, typeValue: string, fileKey: string, request: RequestJobRequest) { let resPixelmap: PixelMap | undefined = undefined let decodingOptions: image.DecodingOptions = { editable: request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined ? true : false, } let imageSource: image.ImageSource = image.createImageSource(resBuf) if (imageSource === undefined){ - return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed") + ImageKnifeLoader.makeEmptyResult(request,"image.createImageSource failed") + return } let size = (await imageSource.getImageInfo()).size @@ -79,23 +94,35 @@ export class ImageKnifeLoader { imageSource.release() }).catch((error: BusinessError) => { imageSource.release() - return ImageKnifeLoader.makeEmptyResult(JSON.stringify(error)) + ImageKnifeLoader.makeEmptyResult(request,JSON.stringify(error)) + return }) - - return { + if (request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined && resPixelmap !== undefined) { + LogUtil.log("ImageKnife_DataTime_requestJob.transform.start:" + request.src) + resPixelmap = await request.transformation?.transform(request.context, resPixelmap, request.componentWidth, request.componentHeight); + LogUtil.log("ImageKnife_DataTime_requestJob.transform.end:" + request.src) + } + try { + resPixelmap?.setTransferDetached(true) + } catch (e) { + LogUtil.error("PixelMap setTransferDetached err:"+JSON.stringify(e)) + } + let res: RequestJobResult = { pixelMap: resPixelmap, bufferSize: resBuf.byteLength, fileKey: fileKey, size:size, type:typeValue - }; + } + emitter.emit(Constants.CALLBACK_EMITTER + request.memoryKey, { data: { "value": res } }) } static async parseSvgImage(resBuf: ArrayBuffer, typeValue: string, fileKey: string, - request: RequestJobRequest): Promise { + request: RequestJobRequest) { let resPixelmap: PixelMap | undefined = undefined let imageSource: image.ImageSource = image.createImageSource(resBuf) if (imageSource === undefined){ - return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed") + ImageKnifeLoader.makeEmptyResult(request,"image.createImageSource failed") + return } let size = (await imageSource.getImageInfo()).size @@ -114,23 +141,30 @@ export class ImageKnifeLoader { .then((pixelmap: PixelMap) => { resPixelmap = pixelmap imageSource.release() + try { + resPixelmap.setTransferDetached(true) + } catch (e) { + LogUtil.error("PixelMap setTransferDetached err:"+JSON.stringify(e)) + } }).catch((error: BusinessError) => { imageSource.release() - return ImageKnifeLoader.makeEmptyResult(JSON.stringify(error)) + ImageKnifeLoader.makeEmptyResult(request,JSON.stringify(error)) + return }) - - return { + let res: RequestJobResult = { pixelMap: resPixelmap, bufferSize: resBuf.byteLength, fileKey: fileKey, type:typeValue - }; + } + emitter.emit(Constants.CALLBACK_EMITTER + request.memoryKey, { data: { "value": res } }) } static async parseAnimatorImage(resBuf: ArrayBuffer, typeValue: string, - fileKey: string,request: RequestJobRequest): Promise { + fileKey: string,request: RequestJobRequest) { let imageSource: image.ImageSource = image.createImageSource(resBuf) if (imageSource === undefined){ - return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed") + ImageKnifeLoader.makeEmptyResult(request,"image.createImageSource failed") + return } let frameCount = await imageSource.getFrameCount() @@ -140,22 +174,25 @@ export class ImageKnifeLoader { if(frameCount == undefined || frameCount == 1) { } else { let base64str = "data:image/" + typeValue + ";base64," + new util.Base64Helper().encodeToStringSync(new Uint8Array(resBuf)) - return { + let res: RequestJobResult = { pixelMap: base64str, bufferSize: resBuf.byteLength, fileKey: fileKey, size:size, type:typeValue - }; + } + emitter.emit(Constants.CALLBACK_EMITTER + request.memoryKey, { data: { "value": res } }) + return } - return ImageKnifeLoader.parseNormalImage(resBuf, typeValue, fileKey, request) + ImageKnifeLoader.parseNormalImage(resBuf, typeValue, fileKey, request) } // 为AnimatorComponent解析动图 - static async parseForAnimatorComponent(resBuf: ArrayBuffer, typeValue: string, fileKey: string,request: RequestJobRequest): Promise { + static async parseForAnimatorComponent(resBuf: ArrayBuffer, typeValue: string, fileKey: string,request: RequestJobRequest) { if (typeValue === 'gif' || typeValue === 'webp') { let imageSource: image.ImageSource = image.createImageSource(resBuf); if (imageSource === undefined){ - return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed") + ImageKnifeLoader.makeEmptyResult(request,"image.createImageSource failed") + return } let decodingOptions: image.DecodingOptions = { editable: request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined ? true : false, @@ -179,9 +216,10 @@ export class ImageKnifeLoader { }) }).catch((error: BusinessError) => { imageSource.release() - return ImageKnifeLoader.makeEmptyResult(JSON.stringify(error)) + ImageKnifeLoader.makeEmptyResult(request,JSON.stringify(error)) + return }) - return { + let res: RequestJobResult = { pixelMap: "", bufferSize: resBuf.byteLength, fileKey: fileKey, @@ -189,8 +227,9 @@ export class ImageKnifeLoader { pixelMapList, delayList } + emitter.emit(Constants.CALLBACK_EMITTER + request.memoryKey, { data: { "value": res } }) } else { - return ImageKnifeLoader.makeEmptyResult("ImageKnifeAnimatorComponent组件仅支持动态图") + ImageKnifeLoader.makeEmptyResult(request,"ImageKnifeAnimatorComponent组件仅支持动态图") } } static getHeaderObj(request:RequestJobRequest){ @@ -206,32 +245,44 @@ export class ImageKnifeLoader { } return headerObj } + static FileCacheParseImage(request:RequestJobRequest,resBuf:ArrayBuffer,fileKey:string){ + // 保存文件缓存 + if (resBuf !== undefined && request.writeCacheStrategy !== CacheStrategy.Memory) { + LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.start:"+request.src) + FileCache.saveFileCacheOnlyFile(request.context, fileKey, resBuf , request.fileCacheFolder) + LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.end:"+request.src) + } + ImageKnifeLoader.parseImage(resBuf,fileKey,request) + } // 获取图片资源 - static async getImageArrayBuffer(request: RequestJobRequest, requestList: List | undefined,fileKey:string): Promise { + static async getImageArrayBuffer(request: RequestJobRequest, requestList: List | undefined,fileKey:string) { let resBuf: ArrayBuffer | undefined - + let loadError: string = "" // 判断自定义下载 if (request.customGetImage !== undefined && request.requestSource == ImageKnifeRequestSource.SRC && typeof request.src == "string") { // 先从文件缓存获取 resBuf = FileCache.getFileCacheByFile(request.context, fileKey , request.fileCacheFolder) if (resBuf === undefined) { LogUtil.log("start customGetImage src=" + request.src) + const headerObj: Record = ImageKnifeLoader.getHeaderObj(request) try { - const headerObj: Record = ImageKnifeLoader.getHeaderObj(request) - resBuf = await request.customGetImage(request.context, request.src,headerObj) - LogUtil.log("end customGetImage src=" + request.src) - } catch (err) { - throw new Error('customGetImage loadFile failed! err = ' + err) - } - if (resBuf === undefined) { - throw new Error('customGetImage loadFile failed!') - } - // 保存文件缓存 - if (request.writeCacheStrategy !== CacheStrategy.Memory) { - LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.start:" + request.src) - FileCache.saveFileCacheOnlyFile(request.context, fileKey, resBuf, request.fileCacheFolder) - LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.end:" + request.src) + request.customGetImage(request.context, request.src, headerObj) + .then((buffer)=>{ + if(buffer != undefined) { + ImageKnifeLoader.FileCacheParseImage(request,buffer,fileKey) + } else { + loadError = "customGetImage loadFail undefined" + ImageKnifeLoader.makeEmptyResult(request,loadError) + } + }).catch((err:string)=>{ + ImageKnifeLoader.makeEmptyResult(request,err) + }) + } catch (e) { + loadError = "customGetImage loadFail failed" + ImageKnifeLoader.makeEmptyResult(request,loadError + e) } + LogUtil.log("end customGetImage src=" + request.src) + return } } else { @@ -284,25 +335,23 @@ export class ImageKnifeLoader { // header: new Header('application/json') }); - await promise.then((data: number) => { + promise.then((data: number) => { if (data == 200 || data == 206 || data == 204) { resBuf = combineArrayBuffers(arrayBuffers) + ImageKnifeLoader.FileCacheParseImage(request,resBuf,fileKey) } else { - throw new Error("HttpDownloadClient has error, http code =" + JSON.stringify(data)) + loadError = "HttpDownloadClient has error, http code =" + JSON.stringify(data) + ImageKnifeLoader.makeEmptyResult(request,loadError) } }).catch((err: Error) => { - throw new Error("HttpDownloadClient download ERROR : err = " + JSON.stringify(err)) + loadError = "HttpDownloadClient download ERROR : err = " + JSON.stringify(err) + ImageKnifeLoader.makeEmptyResult(request,loadError) }); LogUtil.log("HttpDownloadClient.end:" + request.src) - // 保存文件缓存 - if (resBuf !== undefined && request.writeCacheStrategy !== CacheStrategy.Memory) { - LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.start:"+request.src) - FileCache.saveFileCacheOnlyFile(request.context, fileKey, resBuf , request.fileCacheFolder) - LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.end:"+request.src) - } + return } else { - throw new Error('onlyRetrieveFromCache,do not fetch image src = ' + request.src) + loadError = 'onlyRetrieveFromCache,do not fetch image src = ' + request.src } } else if (request.src.startsWith('datashare://') || request.src.startsWith('file://')) { await fs.open(request.src, fs.OpenMode.READ_ONLY).then(async (file) => { @@ -312,13 +361,13 @@ export class ImageKnifeLoader { resBuf = buf; fs.closeSync(file.fd); }).catch((err:BusinessError) => { - throw new Error('LoadDataShareFileClient fs.read err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code) + loadError = 'LoadDataShareFileClient fs.read err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code }) }).catch((err:BusinessError) => { - throw new Error('LoadDataShareFileClient fs.stat err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code) + loadError = 'LoadDataShareFileClient fs.stat err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code }) }).catch((err:BusinessError) => { - throw new Error('LoadDataShareFileClient fs.open err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code) + loadError = 'LoadDataShareFileClient fs.open err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code }) } else { //从本地文件获取 try { @@ -330,7 +379,7 @@ export class ImageKnifeLoader { fs.closeSync(file); } } catch (err) { - throw new Error(err) + loadError = err } } } else if (typeof request.src == "number") { //从资源文件获取 @@ -354,8 +403,9 @@ export class ImageKnifeLoader { } if (resBuf === undefined){ - throw new Error('getImageArrayBuffer undefined') + ImageKnifeLoader.makeEmptyResult(request,loadError) + return } - return resBuf + ImageKnifeLoader.parseImage(resBuf,fileKey,request) } } \ No newline at end of file diff --git a/library/src/main/ets/components/ImageKnifeComponent.ets b/library/src/main/ets/components/ImageKnifeComponent.ets index 030a668..4ae8ce0 100644 --- a/library/src/main/ets/components/ImageKnifeComponent.ets +++ b/library/src/main/ets/components/ImageKnifeComponent.ets @@ -24,7 +24,7 @@ import { DefaultEngineKey } from '../key/DefaultEngineKey'; @Component export struct ImageKnifeComponent { @Watch('watchImageKnifeOption') @ObjectLink imageKnifeOption: ImageKnifeOption; - @State pixelMap: PixelMap | string | undefined = undefined + @State pixelMap: PixelMap | string | ImageContent | undefined = undefined @State syncLoad: boolean = false @State adaptiveWidth: Length = '100%' @State adaptiveHeight: Length = '100%' @@ -68,6 +68,7 @@ export struct ImageKnifeComponent { } aboutToRecycle() { + this.pixelMap = ImageContent.EMPTY this.clearLastRequest() } /** diff --git a/library/src/main/ets/model/ImageKnifeData.ets b/library/src/main/ets/model/ImageKnifeData.ets index e98836d..45b4635 100644 --- a/library/src/main/ets/model/ImageKnifeData.ets +++ b/library/src/main/ets/model/ImageKnifeData.ets @@ -91,7 +91,7 @@ export interface RequestJobRequest { allHeaders: Map, componentWidth: number, componentHeight: number, - customGetImage?: (context: Context, src: string | PixelMap | Resource ,headers?: Record) => Promise, + customGetImage?: (context: Context, src: string | PixelMap | Resource,headers?: Record) => Promise, onlyRetrieveFromCache?: boolean requestSource: ImageKnifeRequestSource transformation?: PixelMapTransformation diff --git a/library/src/main/ets/model/ImageKnifeOption.ets b/library/src/main/ets/model/ImageKnifeOption.ets index 871fa0e..b34da66 100644 --- a/library/src/main/ets/model/ImageKnifeOption.ets +++ b/library/src/main/ets/model/ImageKnifeOption.ets @@ -61,7 +61,7 @@ export class ImageKnifeOption { placeholderObjectFit?: ImageFit // 错误图填充效果 errorholderObjectFit?: ImageFit - customGetImage?: (context: Context, src: string | PixelMap | Resource,headers?: Record) => Promise + customGetImage?: (context: Context, src: string | PixelMap | Resource,headers?: Record) => Promise border?: BorderOptions // 缓存策略 writeCacheStrategy?: CacheStrategy diff --git a/library/src/main/ets/utils/Constants.ets b/library/src/main/ets/utils/Constants.ets index d187451..d1ed230 100644 --- a/library/src/main/ets/utils/Constants.ets +++ b/library/src/main/ets/utils/Constants.ets @@ -14,4 +14,5 @@ */ export class Constants { public static PROGRESS_EMITTER: string = "progressEmitter" + public static CALLBACK_EMITTER: string = "callBackEmitter" } \ No newline at end of file