解决entry依赖冲突

Signed-off-by: zhoulisheng2 <zhoulisheng2@h-partners.com>
This commit is contained in:
zhoulisheng2 2023-12-08 02:13:15 +00:00 committed by Gitee
commit 4b5cd6de26
11 changed files with 308 additions and 80 deletions

View File

@ -1,6 +1,7 @@
## 2.1.1-rc.5
- .jpg .png .gif解码功能使用taskpool实现
- 修复了内存缓存张数设置为1时gif图片消失的问题
- 新增内存缓存策略,新增缓存张数,缓存大小设置接口

View File

@ -386,6 +386,39 @@ request.skipMemoryCache(true)
<img src="screenshot/gif4.gif" width="50%"/>
### setLruCacheSize
setLruCacheSize(size: number,memory:number): void
设置图片文件缓存的大小上限size单位为张数memory单位为字节提升再次加载同源图片的加载速度特别是对网络图源会有较明显提升。
如果不设置则默认为100张100MB。缓存采用内置的LRU策略。
size为0则代表不限制缓存张数memory为0则代表不限制缓存大小。
建议根据应用实际需求设置合理缓存上限数字过大可能导致内存占用过高可能导致OOM异常。
**参数:**
| 参数名 | 类型 | 必填 | 说明 |
| -------- | -------- | -------- |-------------------------|
| size | number | 是 | 图片文件的缓存张数单位为张。只支持正整数0 |
| memory | number | 是 | 图片文件的缓存大小单位为字节。只支持正数0 |
**示例:**
```ts
//EntryAbility.ets
import { InitImageKnife } from '...imageknife'
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage) {
InitImageKnife.init(this.context);
let imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife()
if (imageKnife != undefined) {
//设置全局内存缓存大小张数
imageKnife.setLruCacheSize(100, 100 * 1204 * 1024)
}
}
}
```
## 约束与限制
在下述版本验证通过:

View File

@ -6,13 +6,11 @@
"repository": {},
"version": "2.1.1-rc.4",
"dependencies": {
// 如果测试entry的demo需要开启以下2个依赖, 然后点击entry勾选 Edit Configurations->点击Deploy Multi Hap->勾选Deploy Multi Hap Packages
// 然后点击module栏目 把library也勾选上,这样就可以在HSP场景下测试Entry里面的HSP场景
"@ohos/libraryimageknife": "file:../sharedlibrary",
"@ohos/disklrucache": "^2.0.2-rc.0",
// 下面这个依赖是为了跑XTS用例的,需要跑XTS时 需要注释上面2个依赖单独使用imageknife依赖
// 下面这个依赖是为了跑XTS用例的需要跑XTS时,需要注释上面2个依赖单独使用imageknife依赖
"@ohos/imageknife": "file:../imageknife"
}
}

View File

@ -15,38 +15,40 @@
import UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import window from '@ohos.window';
import {InitImageKnife, ImageKnifeGlobal,ImageKnife,ImageKnifeDrawFactory,LogUtil } from '@ohos/libraryimageknife'
import { InitImageKnife, ImageKnifeGlobal, ImageKnife, ImageKnifeDrawFactory, LogUtil } from '@ohos/libraryimageknife'
import { CustomEngineKeyImpl } from './CustomEngineKeyImpl'
import abilityAccessCtrl,{Permissions} from '@ohos.abilityAccessCtrl';
import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
import { BusinessError } from '@ohos.base'
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage) {
// Main window is created, set main page for this ability
let list : Array<Permissions> = ['ohos.permission.MEDIA_LOCATION','ohos.permission.READ_MEDIA'];
let permissionRequestResult:Object;
let list: Array<Permissions> = ['ohos.permission.MEDIA_LOCATION', 'ohos.permission.READ_MEDIA'];
let permissionRequestResult: Object;
let atManager = abilityAccessCtrl.createAtManager();
atManager.requestPermissionsFromUser(this.context, list, (err:BusinessError,result:Object)=>{
if(err){
console.log("dodo requestPermissionsFromUserError:"+JSON.stringify(err));
}else{
atManager.requestPermissionsFromUser(this.context, list, (err: BusinessError, result: Object) => {
if (err) {
console.log("dodo requestPermissionsFromUserError:" + JSON.stringify(err));
} else {
permissionRequestResult = result;
console.log("dodo permissionRequestResult:"+JSON.stringify(permissionRequestResult))
console.log("dodo permissionRequestResult:" + JSON.stringify(permissionRequestResult))
}
})
windowStage.loadContent('pages/index', (err:BusinessError, data:void) => {
windowStage.loadContent('pages/index', (err: BusinessError, data: void) => {
});
InitImageKnife.init(this.context);
let imageKnife:ImageKnife|undefined = ImageKnifeGlobal.getInstance().getImageKnife()
if(imageKnife != undefined) {
let imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife()
if (imageKnife != undefined) {
// 全局配置网络加载进度条
imageKnife
.setDefaultLifeCycle(ImageKnifeDrawFactory.createProgressLifeCycle("#10a5ff", 0.5))
// 全局配置缓存key
imageKnife.setEngineKeyImpl(new CustomEngineKeyImpl())
//设置全局内存缓存大小张数
imageKnife.setLruCacheSize(100, 100 * 1204 * 1024)
}
//开启ImageKnife所有级别日志开关
LogUtil.mLogLevel = LogUtil.ALL

View File

@ -79,7 +79,7 @@ struct IndexFunctionDemo {
Button("ImageKnife测试目录页面")
.onClick(() => {
console.log("pages/imageknifeTestCaseIndex 页面跳转")
router.replaceUrl({ url: "pages/imageknifeTestCaseIndex" });
router.pushUrl({ url: "pages/imageknifeTestCaseIndex" });
}).margin({ top: 15 })
}.width('100%').height(60).backgroundColor(Color.Pink)
}

View File

@ -14,32 +14,110 @@
*/
import { ImageKnife } from '../imageknife/ImageKnife';
import { ImageKnifeData } from '../imageknife/ImageKnifeData';
import { LogUtil } from '../imageknife/utils/LogUtil';
import { LruCache } from './LruCache';
export class MemoryLruCache extends LruCache<string, ImageKnifeData>{
constructor(maxsize:number) {
super(maxsize)
export class MemoryLruCache extends LruCache<string, ImageKnifeData> {
maxMemory: number = 0
memorySize: number = 0
constructor(maxsize: number, maxMemory: number) {
super(maxsize);
this.removeAll();
this.maxMemory = maxMemory
this.memorySize = 0;
}
// 添加缓存键值对
put(key: string, value: ImageKnifeData) {
if (key == null || value == null) {
throw new Error('key or value is invalid ');
}
let pre = this.map.get(key)
if (pre == null ) {
this.size++
this.addMemorySize(value)
}
this.entryRemoved(key, pre, value);
this.trimToSize();
}
// 移除键为key的缓存
remove(key: string): ImageKnifeData | undefined {
if (key == null) {
throw new Error('key is null,checking the parameter');
}
let preValue = this.map.get(key)
if (this.map.remove(key)) {
this.size--
if (preValue != undefined) {
this.removeMemorySize(preValue)
}
}
return preValue
}
removeAll() {
this.map.clear()
this.size = 0
this.memorySize = 0;
}
// 移除较少使用的缓存数据
trimToSize(tempsize: number) {
while (true) {
if (tempsize < 0) {
this.map.clear()
this.size = 0
break
trimToSize() {
LogUtil.info("MemoryLruCache maxSize" + this.maxsize + " maxMemory: " + this.maxMemory)
if (this.maxMemory == 0 || this.maxsize == 0 ){
return;
}
if (this.size <= tempsize || this.map.isEmpty()) {
while (true) {
if (this.size <= this.maxsize && this.memorySize <= this.maxMemory || this.map.isEmpty()) {
break
}
let delkey = this.map.getFirstKey()
let data : ImageKnifeData|undefined = this.map.get(delkey)
if(data != undefined){
let data: ImageKnifeData | undefined = this.map.get(delkey)
this.size--
if (data != undefined) {
this.removeMemorySize(data)
data.release()
}
this.map.remove(delkey)
this.size--
}
}
private removeMemorySize(value: ImageKnifeData): void {
if (value.drawPixelMap != undefined) {
LogUtil.info("MemoryLruCache removeMemorySize---- top drawPixelMap memorySize" + this.memorySize)
if (value.drawPixelMap.imagePixelMap != undefined) {
this.memorySize -= value.drawPixelMap.imagePixelMap.getPixelBytesNumber();
}
LogUtil.info("MemoryLruCache removeMemorySize---- end drawPixelMap memorySize" + this.memorySize)
}
if (value.drawGIFFrame != undefined) {
LogUtil.info("MemoryLruCache removeMemorySize---- top drawGIFFrame memorySize" + this.memorySize)
if (value.drawGIFFrame != undefined) {
this.memorySize -= value.drawGIFFrame.getGIFFramesBytesNumber();
}
LogUtil.info("MemoryLruCache removeMemorySize---- end drawGIFFrame memorySize" + this.memorySize)
}
LogUtil.info("MemoryLruCache removeMemorySize---- end mapSize" + this.map.size())
}
private addMemorySize(value: ImageKnifeData): void {
if (value.drawPixelMap != undefined) {
LogUtil.info("MemoryLruCache addMemorySize---- top drawPixelMap memorySize" + this.memorySize)
if (value.drawPixelMap.imagePixelMap != undefined) {
this.memorySize += value.drawPixelMap.imagePixelMap.getPixelBytesNumber();
}
LogUtil.info("MemoryLruCache addMemorySize---- end drawPixelMap memorySize" + this.memorySize)
}
if (value.drawGIFFrame != undefined) {
LogUtil.info("MemoryLruCache addMemorySize---- top drawGIFFrame memorySize" + this.memorySize)
if (value.drawGIFFrame != undefined) {
this.memorySize += value.drawGIFFrame.getGIFFramesBytesNumber();
}
LogUtil.info("MemoryLruCache addMemorySize---- end drawGIFFrame memorySize" + this.memorySize)
}
LogUtil.info("MemoryLruCache addMemorySize---- end mapSize" + this.map.size())
}
}

View File

@ -78,7 +78,7 @@ export class ImageKnife {
this.pausedMaps = new EasyLinkedHashMap();
// 构造方法传入size 为保存文件个数
this.memoryCache = new MemoryLruCache(100);
this.memoryCache = new MemoryLruCache(100,100*1024*1024);
// 创建disk缓存 传入的size 为多少比特 比如20KB 传入20*1024
this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext());
@ -174,12 +174,13 @@ export class ImageKnife {
return new CompressBuilder();
}
// 替代原来的LruCache
public replaceLruCache(size: number) {
// 设置缓存张数,缓存大小,单位字节
public setLruCacheSize(size: number,memory:number) {
if (this.memoryCache.map.size() <= 0) {
this.memoryCache = new MemoryLruCache(size);
this.memoryCache = new MemoryLruCache(size,memory);
} else {
let newLruCache = new MemoryLruCache(size);
let newLruCache = new MemoryLruCache(size,memory);
this.memoryCache.foreachLruCache((value: ImageKnifeData, key: string, map: Object) => {
newLruCache.put(key, value);
})

View File

@ -87,6 +87,8 @@ export struct ImageKnifeComponent {
private detachFromLayoutGIF :DetachFromLayout|undefined = undefined;
private detachFromLayoutPixelMap :DetachFromLayout|undefined = undefined;
build() {
Canvas(this.context)
@ -511,7 +513,6 @@ export struct ImageKnifeComponent {
drawMainSource(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
LogUtil.log('ImageKnifeComponent default drawMainSource start!')
if (data.isPixelMap()) {
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
let scaleType = (typeof imageKnifeOption.mainScaleType == 'number') ? imageKnifeOption.mainScaleType : ScaleType.FIT_CENTER
LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height + 'scaleType=' + scaleType)
@ -521,6 +522,10 @@ export struct ImageKnifeComponent {
context.restore();
LogUtil.log('ImageKnifeComponent default drawMainSource end!')
})
if (data.drawPixelMap != undefined) {
data.drawPixelMap.isShowOnComponent = true;
this.detachFromLayoutPixelMap = data.drawPixelMap.detachFromLayoutPixelMap;
}
} else if (data.isGIFFrame()) {
if(data.drawGIFFrame != undefined) {
data.drawGIFFrame.isShowOnComponent = true
@ -614,6 +619,9 @@ export struct ImageKnifeComponent {
if(this.detachFromLayoutGIF != undefined){
this.detachFromLayoutGIF.detach();
}
if (this.detachFromLayoutPixelMap != undefined) {
this.detachFromLayoutPixelMap.detach();
}
this.resetGifData();
}

View File

@ -14,6 +14,8 @@
*/
import { GIFFrame } from './utils/gif/GIFFrame'
import { DetachFromLayout } from './RequestOption'
import { LogUtil } from './utils/LogUtil'
import { Ellipse2 } from '@ohos/svg/src/main/ets/components/utils/SVGBase'
export enum ImageKnifeType {
PIXELMAP = 'PixelMap',
@ -24,6 +26,21 @@ export enum ImageKnifeType {
export class DrawPixelMap {
imagePixelMap: PixelMap | undefined = undefined
isShowOnComponent: boolean = false; //gif是否显示在组件上 true显示在组件上 false不显示在组件上
isLruCacheRelease: boolean = false; //当前lru是否释放gif资源true的就释放了gif资源 false就是没有释放
detachFromLayoutPixelMap: DetachFromLayout = {
detach: () => {
this.isShowOnComponent = false
if (this.isLruCacheRelease) {
let pixelMap = this.imagePixelMap;
if (pixelMap != undefined) {
pixelMap.release();
}
this.imagePixelMap = undefined;
}
}
}
}
export class DrawString {
@ -36,13 +53,13 @@ export class DrawResource {
export class DrawGIFFrame {
imageGIFFrames: GIFFrame[] | undefined = undefined
isShowOnComponent:boolean = false;//gif是否显示在组件上 true显示在组件上 false不显示在组件上
isLruCacheRelease:boolean = false;//当前lru是否释放gif资源true的就释放了gif资源 false就是没有释放
isShowOnComponent: boolean = false; //gif是否显示在组件上 true显示在组件上 false不显示在组件上
isLruCacheRelease: boolean = false; //当前lru是否释放gif资源true的就释放了gif资源 false就是没有释放
detachFromLayoutGIF :DetachFromLayout = {
detach:()=> {
detachFromLayoutGIF: DetachFromLayout = {
detach: () => {
this.isShowOnComponent = false
if(this.isLruCacheRelease) {
if (this.isLruCacheRelease) {
let gifFrames = this.imageGIFFrames;
if (gifFrames != undefined) {
for (let i = 0; i < gifFrames.length; i++) {
@ -56,6 +73,20 @@ export class DrawGIFFrame {
}
}
};
getGIFFramesBytesNumber(): number {
let pixelMapBytes: number = 0;
if (this.imageGIFFrames != undefined) {
for (let index = 0; index < this.imageGIFFrames.length; index++) {
let drawPixelMap = this.imageGIFFrames[index].drawPixelMap;
if (drawPixelMap != undefined) {
pixelMapBytes += drawPixelMap.getPixelBytesNumber();
LogUtil.info("getGIFFramesBytesNumber gif总大小为" + pixelMapBytes);
}
}
}
return pixelMapBytes;
}
}
export class ImageKnifeData {
@ -65,9 +96,7 @@ export class ImageKnifeData {
static PNG = 'png';
static BMP = 'bmp';
static WEBP = 'webp';
waitSaveDisk = false;
imageKnifeType: ImageKnifeType | undefined = undefined;
drawPixelMap: DrawPixelMap | undefined = undefined;
drawGIFFrame: DrawGIFFrame | undefined = undefined;
@ -79,6 +108,7 @@ export class ImageKnifeData {
data.imageKnifeType = type;
data.drawPixelMap = new DrawPixelMap();
data.drawPixelMap.imagePixelMap = value;
data.drawPixelMap.isShowOnComponent = true;
return data;
}
@ -87,6 +117,7 @@ export class ImageKnifeData {
data.imageKnifeType = type;
data.drawGIFFrame = new DrawGIFFrame();
data.drawGIFFrame.imageGIFFrames = value;
data.drawGIFFrame.isShowOnComponent = true;
return data;
}
@ -106,24 +137,30 @@ export class ImageKnifeData {
return ImageKnifeType.RESOURCE == this.imageKnifeType;
}
release(){
if(this.isPixelMap()){
if(this.drawPixelMap != undefined && this.drawPixelMap.imagePixelMap != undefined){
release() {
if (this.isPixelMap()) {
if (this.drawPixelMap != undefined && this.drawPixelMap.imagePixelMap != undefined) {
this.drawPixelMap.isLruCacheRelease = true;
if (this.drawPixelMap.isShowOnComponent){
return;
}else {
this.drawPixelMap.imagePixelMap.release()
.then(()=>{
if(this.drawPixelMap != undefined && this.drawPixelMap.imagePixelMap !=undefined){
.then(() => {
if (this.drawPixelMap != undefined && this.drawPixelMap.imagePixelMap != undefined) {
this.drawPixelMap.imagePixelMap = undefined;
}
})
}
LogUtil.info("MemoryLruCache removeMemorySize---- end 释放普通图片:")
}
if(this.isGIFFrame()){
}
if (this.isGIFFrame()) {
if (this.drawGIFFrame != undefined) {
this.drawGIFFrame.isLruCacheRelease = true;
if(this.drawGIFFrame.isShowOnComponent){
if (this.drawGIFFrame.isShowOnComponent) {
return;
}else {
} else {
let gifFrames = this.drawGIFFrame.imageGIFFrames;
if (gifFrames != undefined) {
for (let i = 0; i < gifFrames.length; i++) {
@ -132,6 +169,7 @@ export class ImageKnifeData {
tempFrame.drawPixelMap.release()
}
}
LogUtil.info("MemoryLruCache removeMemorySize---- end 释放GIF图片")
this.drawGIFFrame.imageGIFFrames = undefined
}
this.drawGIFFrame.imageGIFFrames = undefined
@ -139,18 +177,17 @@ export class ImageKnifeData {
}
}
if(this.isString()){
if(this.drawString != undefined && this.drawString.imageString!=undefined){
if (this.isString()) {
if (this.drawString != undefined && this.drawString.imageString != undefined) {
this.drawString.imageString = undefined
}
}
if(this.isResource()){
if(this.drawResource != undefined && this.drawResource.imageResource != undefined){
if (this.isResource()) {
if (this.drawResource != undefined && this.drawResource.imageResource != undefined) {
this.drawResource.imageResource = undefined
}
}
}
}

View File

@ -26,12 +26,42 @@ export class ParseImageUtil implements IParseImage<PixelMap> {
// scale(0,1)
parseImageThumbnail(scale: number, imageinfo: ArrayBuffer, onCompleteFunction: (value: PixelMap) => void | PromiseLike<PixelMap>, onErrorFunction: (reason?: BusinessError | string) => void) {
taskPoolExecutePixelMap(imageinfo,scale,onCompleteFunction,onErrorFunction);
// taskPoolExecutePixelMap(imageinfo,scale,onCompleteFunction,onErrorFunction);
let imageSource:image.ImageSource = image.createImageSource(imageinfo); // 步骤一文件转为pixelMap 然后变换 给Image组件
imageSource.getImageInfo((err, value) => {
if (err) {
onErrorFunction(err);
return;
}
let hValue = Math.round(value.size.height * scale);
let wValue = Math.round(value.size.width * scale);
let defaultSize:image.Size = {
height: hValue,
width: wValue
};
let opts:image.DecodingOptions = {
editable: true,
desiredSize: defaultSize
};
imageSource.createPixelMap(opts, (err, pixelmap) => {
if (err) {
onErrorFunction(err);
} else {
onCompleteFunction(pixelmap);
}
imageSource.release()
})
})
}
}
// let TAG:string = "ParseImageUtil_TASK"
@Concurrent
async function taskParseImage(arrayBuffer: ArrayBuffer,scale: number): Promise<PixelMap> {

View File

@ -35,7 +35,47 @@ export interface gifBackData {
export class GIFParseImpl implements IParseGif {
parseGifs(imageinfo: ArrayBuffer, callback: (data?: GIFFrame[], err?: BusinessError | string) => void) {
taskPoolExecutePixelMapList(imageinfo,callback);
// taskPoolExecutePixelMapList(imageinfo,callback);
// oh解码流程
let imageSource = image.createImageSource(imageinfo);
let decodeOpts: image.DecodingOptions = {
sampleSize: 1,
editable: true,
rotate: 0
}
let data:GIFFrame[] = [];
imageSource.createPixelMapList(decodeOpts).then((pixelList: Array<PixelMap>) => {
//sdk的api接口发生变更从.getDelayTime() 变为.getDelayTimeList()
imageSource.getDelayTimeList().then(delayTimes => {
if (pixelList.length > 0) {
let pixelmap = pixelList[0];
pixelmap.getImageInfo().then(imageInfo => {
for (let i = 0; i < pixelList.length; i++) {
let frame = new GIFFrame();
frame.drawPixelMap = pixelList[i];
frame.dims = { width: imageInfo.size.width, height: imageInfo.size.height, top: 0, left: 0 }
if (i < delayTimes.length) {
frame.delay = delayTimes[i];
} else {
frame.delay = delayTimes[delayTimes.length - 1]
}
data.push(frame)
}
callback(data,undefined)
imageSource.release();
}).catch((err: string) => {
imageSource.release();
callback(undefined,err)
})
}
}).catch((err: string) => {
imageSource.release();
callback(undefined,err)
})
}).catch((err: string) => {
imageSource.release();
callback(undefined,err)
})
}
}
@ -50,8 +90,8 @@ async function taskParseGif(arrayBuffer: ArrayBuffer): Promise<GIFFrame[]> {
}
let pixelList = await imageSource.createPixelMapList(decodeOpts);
if (pixelList.length > 0) {
let pixelmap1 = pixelList[0];
let imageInfo = await pixelmap1.getImageInfo();
let pixelmap = pixelList[0];
let imageInfo = await pixelmap.getImageInfo();
let delayTimes = await imageSource.getDelayTimeList();
for (let i = 0; i < pixelList.length; i++) {
let frame = new GIFFrame();