!219 ImageKnife控制重要图片请求加载优先级

Merge pull request !219 from 17792399693/master
This commit is contained in:
openharmony_ci 2024-04-24 06:20:29 +00:00 committed by Gitee
commit befcd414ac
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
9 changed files with 410 additions and 273 deletions

View File

@ -3,6 +3,7 @@
- ImageKnife支持根据自定义key获取已缓存的图片
- ImageKnife加载图片支持自定义网络栈和图片加载组件
- 适配复用场景触发懒加载onDataReloaded
- ImageKnife控制重要图片请求加载优先级
## 2.2.0-rc.0
- 修复自定义DataFetch接口实现不生效问题

View File

@ -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 | | 删除队列头元素并返回该删除元素 |
## 约束与限制

View File

@ -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 })
}
}
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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<string, ImageKnifeData> = new MemoryCacheProxy(new MemoryLruCache(100, 100 * 1024 * 1024));
headerMap: Map<string, Object> = new Map<string, Object>(); //定义全局map
placeholderCache: string = "placeholderCache"
runningMaps: EasyLinkedHashMap<string, RequestOption>;
pendingMaps: EasyLinkedHashMap<string, RequestOption>;
pausedMaps: EasyLinkedHashMap<string, RequestOption>;
isPaused: boolean = false;
mutex: MethodMutex = new MethodMutex();
fileTypeUtil: FileTypeUtil; // 通用文件格式辨别
@ -87,14 +87,16 @@ export class ImageKnife {
// 开发者可配置全局缓存
engineKeyImpl: EngineKeyInterface;
private mParseImageUtil: IParseImage<PixelMap>;
// 最大并发
maxRequests: number = 64;
// 排队队列
private jobQueue: IJobQueue = new DefaultJobQueue();
// 执行中的请求
private executingJobMap: LightWeightMap<string, List<RequestOption>> = 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<void> {
await this.mutex.lock(async () => {
this.isPaused = true;
// 将未删除的所有request [run pend] 放入 [pause]
LogUtil.log('dodo pauseRequests start1 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
// 将run存入pause
let runNode = this.runningMaps.getHead();
while (runNode) {
let request = runNode.value;
this.pausedMaps.put(request.uuid, request)
runNode = runNode.next;
}
this.runningMaps.clear();
LogUtil.log('dodo pauseRequests start2 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
let pendNode = this.pendingMaps.getHead();
while (pendNode) {
let request = pendNode.value;
this.pausedMaps.put(request.uuid, request)
pendNode = pendNode.next
}
this.pendingMaps.clear()
LogUtil.log('dodo pauseRequests start3 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
})
}
@ -277,28 +256,19 @@ export class ImageKnife {
async resumeRequests(): Promise<void> {
await this.mutex.lock(async () => {
this.isPaused = false;
LogUtil.log('dodo resumeRequest start1 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
// 重启了之后需要把paused 里面的所有request重新发送
let headNode = this.pausedMaps.getHead();
while (headNode) {
let request = headNode.value
this.loadCacheManager(request)
headNode = headNode.next
if (this.executingJobMap.length > 0) {
this.executingJobMap.forEach((list: List<RequestOption>) => {
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<RequestOption> | 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<RequestOption> | 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<RequestOption> | 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<RequestOption> | 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;
}
}
}

View File

@ -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;
}

View File

@ -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<RequestOption> = new Queue();
normalQueue: Queue<RequestOption> = new Queue();
lowQueue: Queue<RequestOption> = 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();
}
}
}

View File

@ -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;
}