Pre Merge pull request !164 from 面条侠/master

This commit is contained in:
面条侠 2024-04-07 04:49:46 +00:00 committed by Gitee
commit 9b6de1bb8f
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
9 changed files with 424 additions and 25 deletions

View File

@ -1,5 +1,6 @@
## 2.1.2-rc.12
- 新增磁盘预加载返回文件路径接口prefetchToDiskCache
- 新增跳过网络判断缓存或者磁盘中是否存在图片接口isUrlExist
## 2.1.2-rc.11
- 修复设置磁盘容量最大值出现闪退

View File

@ -333,6 +333,7 @@ request.skipMemoryCache(true)
| preload(request: RequestOption) | request: RequestOption | 根据用户配置参数具体执行预加载流程 |
| pauseRequests() | | 全局暂停请求 |
| resumeRequests() | | 全局恢复暂停 |
| isUrlExist(request: RequestOption) | request: RequestOption | 判断图片是否在 缓存和磁盘中存在request.setCacheType(CacheType.Cache) request.setImageViewSize(this.comSize) 如果入参是缓存,需要传入值图片大小|
### 缓存策略相关
@ -352,6 +353,13 @@ request.skipMemoryCache(true)
| AntiAliasing.FIT_MEDIUM | String | 图像抗锯齿设置为中画质 |
| AntiAliasing.FIT_LOW | String | 图像抗锯齿设置为低画质 |
### CacheType类型展示效果
| 使用方法 | 类型 | 策略描述 |
|-------------------------|-----|-----------------------------------|
| CacheType.Cache| int | 缓存 |
| CacheType.Disk | int | 磁盘 |
### ScaleType类型展示效果
| 使用方法 | 类型 | 策略描述 |
@ -520,6 +528,7 @@ HSP场景适配:
- OptionTestPage.ets # 图片缓存测试
- testManyGifLoadWithPage # 测试gif加载页面
- testImageKnifeCache # 跳过网络判断图片是否存在于缓存或者磁盘中
-workers
- upngWorkerTestCase.ets # png子线程解析
- upngWorkerDepend.ts # png子线程解析具体执行

View File

@ -357,6 +357,14 @@ struct IndexFunctionDemo {
router.pushUrl({ url: 'pages/testImageKnifeHttpRequestHeader' });
}).margin({ top: 5, left: 3 })
}.width('100%').height(60).backgroundColor(Color.Pink)
Text('测试图片缓存内存').fontSize(15)
Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Button('测试图片缓存内存')
.onClick(() => {
router.pushUrl({ url: 'pages/testImageKnifeCache' });
}).margin({ top: 5, left: 3 })
}.width('100%').height(60).backgroundColor(Color.Pink)
}
}
.width('100%')

View File

@ -0,0 +1,257 @@
/*
* Copyright (C) 2022 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,
ImageKnifeOption,
ImageKnifeGlobal,
ImageKnife,
ImageKnifeData,
RequestOption,
Size,
CacheType
} from '@ohos/libraryimageknife'
import image from '@ohos.multimedia.image';
import { BusinessError } from '@ohos.base';
let imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife();
@Entry
@Component
struct testImageKnifeCache {
@State url: string = '';
@State urlGif: string = 'https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658';
@State urlSvg: string = 'http://1.94.37.200:700/downloadSvgIamge';
@State urlPng: string = 'https://img-blog.csdnimg.cn/20191215043500229.png';
@State urlJpg: string = 'https://hbimg.huabanimg.com/cc6af25f8d782d3cf3122bef4e61571378271145735e9-vEVggB';
@State urlBmp: string = 'https://img-blog.csdn.net/20140514114029140';
@State urlWebp: string = 'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp';
@State imagePixelMap: PixelMap | undefined = undefined;
@State imagePixelMap_: PixelMap | undefined = undefined;
private index_: number = -1;
private tempSize: number = 200;
private timeId = -1;
private comSize: Size = {
width: this.tempSize,
height: this.tempSize,
}
@State imageKnifeOption: ImageKnifeOption =
{
loadSrc: $r('app.media.icon'),
placeholderSrc: $r('app.media.icon_loading'),
errorholderSrc: $r('app.media.icon_failed')
};
hasUrlCache(url: string, cacheType: CacheType = CacheType.Default, size: Size = {
width: 0,
height: 0
}) {
imageKnife?.isUrlExist(url, cacheType, size).then((data: ImageKnifeData) => {
clearTimeout(this.timeId);
if (data.isPixelMap()) {
if (data.drawPixelMap) {
let pixelmap = data.drawPixelMap.imagePixelMap
if (pixelmap) {
if (this.index_ == 1) {
this.imagePixelMap = pixelmap;
} else if (this.index_ == 2) {
this.imagePixelMap_ = pixelmap;
}
}
}
}
if (data.isGIFFrame()) {
let index: number = 0
if (data.drawGIFFrame) {
if (data.drawGIFFrame.imageGIFFrames) {
let renderGif = () => {
if (data.drawGIFFrame) {
if (data.drawGIFFrame.imageGIFFrames) {
let pixelmap = data.drawGIFFrame.imageGIFFrames[index].drawPixelMap
let delay = data.drawGIFFrame.imageGIFFrames[index].delay
if (pixelmap) {
if (this.index_ == 1) {
this.imagePixelMap = pixelmap;
} else if (this.index_ == 2) {
this.imagePixelMap_ = pixelmap;
}
}
index++;
if (index == data.drawGIFFrame.imageGIFFrames.length - 1) {
index = 0
}
this.timeId = setTimeout(renderGif, data!.drawGIFFrame!.imageGIFFrames![index].delay)
}
}
}
renderGif()
}
}
}
}).catch((err: BusinessError) => {
});
}
build() {
Scroll() {
Column() {
Text('图片内存和磁盘读取').fontSize(30);
Text('加载的缓存时候关闭掉网络').fontSize(15);
Row() {
Button('png')
.onClick(() => {
this.index_ = 0;
this.url = this.urlPng;
this.imageKnifeOption =
{
loadSrc: this.url,
placeholderSrc: $r('app.media.icon_loading'),
errorholderSrc: $r('app.media.icon_failed')
}
})
Button('bmp')
.onClick(() => {
this.index_ = 0;
this.url = this.urlBmp;
this.imageKnifeOption =
{
loadSrc: this.url,
placeholderSrc: $r('app.media.icon_loading'),
errorholderSrc: $r('app.media.icon_failed')
}
})
Button('webp')
.onClick(() => {
this.index_ = 0;
this.url = this.urlWebp;
this.imageKnifeOption =
{
loadSrc: this.url,
placeholderSrc: $r('app.media.icon_loading'),
errorholderSrc: $r('app.media.icon_failed')
}
})
Button('jpg')
.onClick(() => {
this.index_ = 0;
this.url = this.urlJpg;
this.imageKnifeOption =
{
loadSrc: this.url,
placeholderSrc: $r('app.media.icon_loading'),
errorholderSrc: $r('app.media.icon_failed')
}
})
Button('gif')
.onClick(() => {
this.index_ = 0;
this.url = this.urlGif;
this.imageKnifeOption =
{
loadSrc: this.url,
placeholderSrc: $r('app.media.icon_loading'),
errorholderSrc: $r('app.media.icon_failed')
}
})
Button('svg')
.onClick(() => {
this.index_ = 0;
this.url = this.urlSvg;
this.imageKnifeOption =
{
loadSrc: this.url,
placeholderSrc: $r('app.media.icon_loading'),
errorholderSrc: $r('app.media.icon_failed')
}
})
}
Row() {
Button('缓存图片')
.onClick(() => {
this.index_ = 1;
this.hasUrlCache(this.url, CacheType.Cache, this.comSize);
})
Button('磁盘图片')
.onClick(() => {
this.index_ = 2;
this.hasUrlCache(this.url, CacheType.Disk, this.comSize);
})
Button('默认')
.onClick(() => {
this.index_ = 2;
this.hasUrlCache(this.url);
})
}
Text('网络图').fontSize(15);
ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption })
.width(200)
.height(200)
.backgroundColor(Color.Orange)
Text('缓存图').fontSize(15);
ImageKnifeComponent({
imageKnifeOption: {
loadSrc: this.imagePixelMap as image.PixelMap
}
})
.width(200)
.height(200)
.backgroundColor(Color.Orange)
Text('磁盘图').fontSize(15);
ImageKnifeComponent({
imageKnifeOption: {
loadSrc: this.imagePixelMap_ as image.PixelMap
}
})
.width(200)
.height(200)
.backgroundColor(Color.Orange)
}
.alignItems(HorizontalAlign.Center)
.width('100%')
}
.width('100%')
.height('100%')
}
aboutToAppear() {
}
aboutToDisappear() {
}
onBackPress() {
}
}

View File

@ -46,6 +46,7 @@
"pages/testImageKnifeAutoWidthPage",
"pages/testImageKnifeAutoHeightPage",
"pages/testPriorityComponent",
"pages/testReusePhotoPage"
"pages/testReusePhotoPage",
"pages/testImageKnifeCache"
]
}

View File

@ -98,7 +98,7 @@ export { UPNG } from './src/main/ets/components/3rd_party/upng/UPNG'
export { ImageKnife } from './src/main/ets/components/imageknife/ImageKnife'
export { ImageKnifeGlobal } from './src/main/ets/components/imageknife/ImageKnifeGlobal'
export { ObjectKey } from './src/main/ets/components/imageknife/ObjectKey'
export {RequestOption,Size,DetachFromLayout,Priority} from './src/main/ets/components/imageknife/RequestOption'
export {RequestOption,Size,DetachFromLayout,Priority,CacheType} from './src/main/ets/components/imageknife/RequestOption'
export { ImageKnifeComponent, ScaleType, ScaleTypeHelper, AntiAliasing} from './src/main/ets/components/imageknife/ImageKnifeComponent'
export { ImageKnifeDrawFactory } from './src/main/ets/components/imageknife/ImageKnifeDrawFactory'
export {ImageKnifeOption,CropCircleWithBorder,Crop,GifOptions,TransformOptions,HeaderOptions} from './src/main/ets/components/imageknife/ImageKnifeOption'

View File

@ -16,7 +16,7 @@
import { DiskLruCache } from "../cache/DiskLruCache"
import { EngineKeyFactories } from "../cache/key/EngineKeyFactories"
import { EngineKeyInterface } from "../cache/key/EngineKeyInterface"
import { RequestOption, Size } from "../imageknife/RequestOption"
import { CacheType, RequestOption, Size } from "../imageknife/RequestOption"
import { AsyncCallback } from "../imageknife/interface/AsyncCallback"
import { PlaceHolderManager } from "../imageknife/holder/PlaceHolderManager"
import { RetryHolderManager } from "../imageknife/holder/RetryHolderManager"
@ -49,6 +49,10 @@ import { ObjectKey } from './ObjectKey'
import { TaskParams } from './TaskParams'
import { Constants } from './constants/Constants'
import { TransformUtils } from './transform/TransformUtils'
import { GIFParseImpl } from './utils/gif/GIFParseImpl'
import { IParseImage } from './interface/IParseImage'
import { ParseImageUtil } from './utils/ParseImageUtil'
import { SVGParseImpl } from './utils/svg/SVGParseImpl'
export class ImageKnife {
static readonly SEPARATOR: string = '/'
@ -78,8 +82,10 @@ export class ImageKnife {
defaultLifeCycle: IDrawLifeCycle | undefined = undefined;
// 开发者可配置全局缓存
engineKeyImpl: EngineKeyInterface;
private mParseImageUtil: IParseImage<PixelMap>;
private constructor() {
this.mParseImageUtil = new ParseImageUtil();
this.runningMaps = new EasyLinkedHashMap();
this.pendingMaps = new EasyLinkedHashMap();
@ -322,6 +328,104 @@ export class ImageKnife {
return this.parseSource(request);
}
public isUrlExist(url: string, cacheType: CacheType = CacheType.Default, size: Size = {
width: 0,
height: 0
}): Promise<ImageKnifeData> {
return new Promise((resolve, reject) => {
let request = new RequestOption();
request.load(url)
.setImageViewSize(size)
.setCacheType(cacheType);
this.generateDataCacheKey(request);
let loadComplete = (imageKnifeData: ImageKnifeData) => {
resolve(imageKnifeData);
}
let loadError = (err ?: BusinessError | string) => {
if (request.cacheType == CacheType.Default) {
this.loadMemoryDiskIsUrl(request, loadComplete, loadError);
}
reject(err);
}
if (request.cacheType == CacheType.Cache) {
this.loadMemoryCacheIsUrl(request, loadComplete, loadError);
} else if (request.cacheType == CacheType.Disk) {
this.loadMemoryDiskIsUrl(request, loadComplete, loadError);
}
else if (request.cacheType == CacheType.Default) {
this.loadMemoryCacheIsUrl(request, loadComplete, loadError);
}
})
}
loadMemoryCacheIsUrl(request: RequestOption, onComplete: (imageKnifeData: ImageKnifeData) => void | PromiseLike<ImageKnifeData>, onError: (err?: BusinessError | string) => void) {
let cache = this.memoryCacheProxy.loadMemoryCache(request.generateCacheKey, true);
if (cache == null || typeof cache == 'undefined') {
onError("No data in cache!")
} else {
cache.waitSaveDisk = false;
//2.网络缓存有数据,返回
onComplete(cache);
}
}
loadMemoryDiskIsUrl(request: RequestOption, onComplete: (imageKnifeData: ImageKnifeData) => void | PromiseLike<ImageKnifeData>, onError: (err?: BusinessError | string) => void) {
let cached: ArrayBuffer = DiskLruCache.getFileCacheByFile(this.diskMemoryCache.getPath() as string, request.generateDataKey) as ArrayBuffer;
if (cached != null) {
let filetype: string | null = this.fileTypeUtil.getFileType(cached);
if (filetype == null) {
onError('请检查数据源');
return;
}
if (!this.fileTypeUtil.isImage(cached)) {
onError('暂不支持的类型!类型=' + filetype);
}
if (ImageKnifeData.GIF == filetype && !request.dontAnimateFlag) {
let gifParseImpl = new GIFParseImpl()
gifParseImpl.parseGifs(cached, (data?: GIFFrame[], err?: BusinessError | string) => {
if (err) {
onError(err)
}
LogUtil.log("gifProcess data is null:" + (data == null));
if (!!data) {
let imageKnifeData = ImageKnifeData.createImageGIFFrame(ImageKnifeType.GIFFRAME, data);
LogUtil.log('gifProcess 生成gif 返回数据类型')
onComplete(imageKnifeData)
} else {
onError('Parse GIF callback data is null, you need check callback data!')
}
})
} else if (ImageKnifeData.SVG == filetype) {
let svgParseImpl = new SVGParseImpl()
let success = (value: PixelMap) => {
let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, value);
onComplete(imageKnifeData);
}
svgParseImpl.parseSvg(request, cached, success, onError)
} else {
//5.磁盘有数据解析错误返回onError
let success = (value: PixelMap) => {
let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, value);
onComplete(imageKnifeData);
}
this.mParseImageUtil.parseImage(cached, success, onError)
}
} else {
//6.磁盘无数据返回onError
onError("No data in disk cache!")
}
}
generateDataCacheKey(request: RequestOption) {
let factories: EngineKeyInterface;
let cacheKey: string;
@ -605,7 +709,7 @@ export class ImageKnife {
if ((typeof (data as PixelMap).isEditable) == 'boolean') {
let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, data as PixelMap);
request.placeholderOnComplete(imageKnifeData)
this.memoryCacheProxy.putValue(request.placeholderCacheKey,imageKnifeData)
this.memoryCacheProxy.putValue(request.placeholderCacheKey, imageKnifeData)
} else {
request.placeholderOnError("request placeholder error")
}
@ -613,7 +717,7 @@ export class ImageKnife {
if ((typeof (data as PixelMap).isEditable) == 'boolean') {
let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, data as PixelMap);
request.retryholderOnComplete(imageKnifeData)
this.memoryCacheProxy.putValue(request.retryholderCacheKey,imageKnifeData)
this.memoryCacheProxy.putValue(request.retryholderCacheKey, imageKnifeData)
} else {
request.retryholderOnError("request retryholder error")
}
@ -621,7 +725,7 @@ export class ImageKnife {
if ((typeof (data as PixelMap).isEditable) == 'boolean') {
let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, data as PixelMap);
request.errorholderOnComplete(imageKnifeData)
this.memoryCacheProxy.putValue(request.errorholderCacheKey,imageKnifeData)
this.memoryCacheProxy.putValue(request.errorholderCacheKey, imageKnifeData)
} else {
request.errorholderOnError("request errorholder error")
}
@ -630,13 +734,13 @@ export class ImageKnife {
let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, data as PixelMap);
imageKnifeData.needSaveDisk = true;
request.loadComplete(imageKnifeData)
this.memoryCacheProxy.putValue(request.generateCacheKey,imageKnifeData)
this.memoryCacheProxy.putValue(request.generateCacheKey, imageKnifeData)
this.setDiskCache(request)
} else if ((data as GIFFrame[]).length > 0) {
let imageKnifeData = ImageKnifeData.createImageGIFFrame(ImageKnifeType.GIFFRAME, data as GIFFrame[]);
imageKnifeData.needSaveDisk = true;
request.loadComplete(imageKnifeData)
this.memoryCacheProxy.putValue(request.generateCacheKey,imageKnifeData)
this.memoryCacheProxy.putValue(request.generateCacheKey, imageKnifeData)
this.setDiskCache(request)
} else {
request.loadError("request resources error")
@ -648,7 +752,7 @@ export class ImageKnife {
}
private setDiskCache(request: RequestOption):void{
private setDiskCache(request: RequestOption): void {
try {
// let diskMemoryCache = ImageKnifeGlobal.getInstance().getImageKnife()?.getDiskMemoryCache();
let dataArraybuffer: ArrayBuffer = DiskLruCache.getFileCacheByFile(this.diskMemoryCache.getPath() as string, request.generateDataKey) as ArrayBuffer;
@ -739,15 +843,16 @@ export class ImageKnife {
if (cachedPath == null || cachedPath == "" || cachedPath == undefined) {
let request = new RequestOption();
request.load(url)
.addListener({ callback: (err: BusinessError | string, data: ImageKnifeData) => {
if (err) {
reject(err)
} else {
let cachedPath = this.getDiskMemoryCache().getFileToPath(key);
resolve(cachedPath);
.addListener({
callback: (err: BusinessError | string, data: ImageKnifeData) => {
if (err) {
reject(err)
} else {
let cachedPath = this.getDiskMemoryCache().getFileToPath(key);
resolve(cachedPath);
}
return false;
}
return false;
}
})
this.call(request);
} else {

View File

@ -64,14 +64,24 @@ export interface Size {
height: number
}
export enum CacheType {
Default,
//缓存
Cache,
//磁盘
Disk
}
export interface DetachFromLayout {
detach: () => void
}
export enum Priority {
HIGH = 0,
MEDIUM = 1,
LOW = 2
}
export class RequestOption {
// 遍历添加图片http请求头
headers: Map<string, Object> = new Map<string, Object>();
@ -85,6 +95,8 @@ export class RequestOption {
addHeaderMap(map: Map<string, Object>) {
this.headers = map;
}
cacheType: CacheType = CacheType.Disk;
// 优先级
priority: Priority = Priority.MEDIUM;
uuid: string = '' // 唯一标识
@ -129,7 +141,7 @@ export class RequestOption {
// 下载原始文件地址
downloadFilePath: string = "";
//磁盘缓存文件路径
diskMemoryCachePath: string ="";
diskMemoryCachePath: string = "";
// 网络文件下载统一存放
networkCacheFolder: string = "ImageKnifeNetworkFolder"
// 主线图片 状态变化 是否加载完成
@ -164,12 +176,15 @@ export class RequestOption {
this.moduleContext = ctx;
}
}
setPriority(priority: Priority) {
this.priority = priority
}
setTransformations( array:Array<BaseTransform<PixelMap>>){
setTransformations(array: Array<BaseTransform<PixelMap>>) {
this.transformations = array;
}
generateUUID(): string {
let d = new Date().getTime();
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(new RegExp("[xy]", "g"), (c) => {
@ -197,6 +212,11 @@ export class RequestOption {
return this;
}
setCacheType(cacheType: CacheType) {
this.cacheType = cacheType;
return this;
}
getFilesPath() {
return this.filesPath;
}
@ -477,7 +497,7 @@ export class RequestOption {
}
// 占位图解析成功
placeholderOnComplete = (imageKnifeData:ImageKnifeData) => {
placeholderOnComplete = (imageKnifeData: ImageKnifeData) => {
LogUtil.log("placeholderOnComplete has called!");
LogUtil.log("Main Image is Ready:" + this.loadMainReady);
if (!this.loadMainReady && !(this.loadErrorReady || this.loadRetryReady) && !this.loadThumbnailReady) {
@ -511,7 +531,7 @@ export class RequestOption {
LogUtil.log("缩略图解析失败 error =" + error)
}
// 加载失败 占位图解析成功
errorholderOnComplete = (imageKnifeData:ImageKnifeData) => {
errorholderOnComplete = (imageKnifeData: ImageKnifeData) => {
// 如果有错误占位图 先解析并保存在RequestOption中 等到加载失败时候进行调用
this.errorholderData = imageKnifeData;
if (this.loadErrorReady) {
@ -524,7 +544,7 @@ export class RequestOption {
errorholderOnError = (error: BusinessError | string) => {
LogUtil.log("失败占位图解析失败 error =" + error)
}
retryholderOnComplete = (imageKnifeData:ImageKnifeData) => {
retryholderOnComplete = (imageKnifeData: ImageKnifeData) => {
this.retryholderData = imageKnifeData;
if (this.loadRetryReady) {
if (this.retryholderFunc != undefined) {
@ -535,7 +555,6 @@ export class RequestOption {
retryholderOnError = (error: BusinessError | string) => {
LogUtil.log("重试占位图解析失败 error =" + error)
}
loadComplete = (imageKnifeData: ImageKnifeData) => {
this.loadMainReady = true;
// 三级缓存数据加载成功
@ -586,7 +605,6 @@ export class RequestOption {
}
}
}
// 图片文件落盘之后会自动去寻找下一个数据加载
removeCurrentAndSearchNext = () => {
if (ImageKnifeGlobal.getInstance().getImageKnife() != undefined) {

View File

@ -97,7 +97,7 @@ export { UPNG } from '@ohos/imageknife'
*/
export { ImageKnife } from '@ohos/imageknife'
export { ImageKnifeGlobal } from '@ohos/imageknife'
export {RequestOption,Size} from '@ohos/imageknife'
export {RequestOption,Size,CacheType} from '@ohos/imageknife'
export {ObjectKey} from '@ohos/imageknife'
export { ImageKnifeComponent, ScaleType, ScaleTypeHelper, AntiAliasing, Priority} from '@ohos/imageknife'
export { ImageKnifeDrawFactory } from '@ohos/imageknife'