【鸿蒙】Harmony Next ImageKnife 2.1.2使用及源码阅读
二级缓存保存、数据加载流程、初始化与使用
ImageKnife - 2.1.2
初始化
ImageKnife.with(context)
主要是初始化单例模式的ImageKnife, 后续通过ImageKnifeGlobal获取单例
ImageKnifeGlobal.getInstance().getImageKnife()
初始化磁盘缓存大小和路径
ImageKnife提供自定义磁盘缓存大小和路径功能,如果不实现默认为300M,使用系统分配的当前应用缓存地址 + /diskLruCache
- 仅设置缓存大小,不设置路径,使用默认缓存路径
imageKnife?.setDiskMemoryCache(DiskLruCache.create(ImageKnifeGlobal.getInstance()
.getHapContext() as common.UIAbilityContext, DISK_CACHE_SIZE))
阅读DiskLruCache.create 源码可以发现其中默认实现了磁盘缓存的路径,为系统分配的当前应用缓存地址 + /diskLruCache
public static create(context: common.UIAbilityContext, maxSize?: number): DiskLruCache {
if (!!!context) {
throw new Error('DiskLruCache create context is empty, checking the parameter');
}
if (!!!maxSize) {
// 默认磁盘缓存大小为 300M
maxSize = DiskLruCache.DEFAULT_MAX_SIZE
}
if (maxSize <= 0) {
throw new Error("DiskLruCache create maxSize <= 0, checking the parameter");
}
// 使用默认应用在内部存储上的缓存路径,作为存储地址
let path = context.cacheDir + FileUtils.SEPARATOR + DiskLruCache.DEFAULT_NAME
if (!FileUtils.getInstance().existFolder(path)) {
FileUtils.getInstance().createFolder(path)
}
if (path.endsWith(FileUtils.SEPARATOR)) {
path = path
} else {
path = path + FileUtils.SEPARATOR
}
return new DiskLruCache(path, maxSize)
}
系统分配的当前应用缓存地址(来自CSDN:Android女王):
2. 设置缓存大小和路径
阅读DiskLruCache.create 源码,可以发现最后都是走的DiskLruCache构造方法,其中可以自定义设置Path和大小
// DiskLruCache
constructor(path: string, maxSize: number) {
this.path = path
this.maxSize = maxSize
}
初始化设置全局缓存的Key
imageKnife默认拥有为EngineKeyFactories的实现
// 其中需要实现generateMemoryCacheKey、generateTransformedDiskCacheKey
// 、generateOriginalDiskCacheKey,生成内存缓存的Key、上传原图变换后的图片的磁盘缓存Key、生成原图的磁盘缓存Key
// 三个方法
imageKnife?.setEngineKeyImpl(new EngineKeyFactories())
阅读其中源码,可以发现其中生成内存缓存的Key、上传原图变换后的图片的磁盘缓存Key、生成原图的磁盘缓存Key三个主要是根据loadSrc的地址和自定义的签名与转换格式和动画标识决定的
```javascript
```javascript
/*
* Copyright (C) 2021 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 { Key } from "../key/Key"
import { RequestOption } from '../../imageknife/RequestOption'
import { BaseTransform } from '../../imageknife/transform/BaseTransform'
import { ObjectKey } from '../../imageknife/ObjectKey';
export class EngineKey implements Key {
// 内存缓存 缓存生成规则:是否会影响图片内容,不影响则通用(strategy onlyRetrieveFromCache isCacheable)为通用项目
// 生成规则 加载数据原 各类参数(排除监听 排除 占位图 失败占位图)
public static generateMemoryCacheKey(loadSrc: string, size: string, transformed: string, dontAnimate: boolean,signature?: ObjectKey, redefine?: (loadSrc: string) => string, otherInfo?: string): string {
if (redefine) {
loadSrc = redefine(loadSrc);
}
let key = "loadSrc=" + loadSrc + ";" +
"size=" + size + ";" +
"transformations=" + transformed + ";" +
"dontAnimateFlag=" + dontAnimate + ";"
if (signature) {
key += "signature=" + signature.getKey() + ";"
}
if (otherInfo) {
key += otherInfo;
}
return key;
}
// 磁盘缓存 缓存生成规则:是否会影响图片内容,不影响则通用(strategy onlyRetrieveFromCache isCacheable)为通用项目
// 生成规则 加载数据原 各类参数(排除监听 排除 占位图 失败占位图)
public static generateTransformedDiskCacheKey(loadSrc: string, size: string, transformed: string, dontAnimate: boolean, signature?: ObjectKey, redefine?: (loadSrc: string) => string, otherInfo?: string): string {
if (redefine) {
loadSrc = redefine(loadSrc);
}
let key = "loadSrc=" + loadSrc + ";" +
"size=" + size + ";" +
"transformations=" + transformed + ";" +
"dontAnimateFlag=" + dontAnimate + ";"
if (signature) {
key += "signature=" + signature.getKey() + ";"
}
if (otherInfo) {
key += otherInfo;
}
return key;
}
// 磁盘缓存
// 生成网络加载数据 原始数据存于磁盘的key
public static generateOriginalDiskCacheKey(loadSrc: string,signature?: ObjectKey, redefine?: (loadSrc: string) => string, otherInfo?: string): string {
if (redefine) {
loadSrc = redefine(loadSrc);
}
let key = "loadSrc=" + loadSrc + ";"
if (signature) {
key += "signature=" + signature.getKey() + ";"
}
if (otherInfo) {
key += otherInfo;
}
return key;
}
updateDiskCacheKey(info: Object) {
}
}
请求加载图片
无论是ImageKnifeComponent加载图片还是我们写一个加载图片的请求最后都会调用call方法
ImageKnifeGlobal.getInstance().getImageKnife()?.call(request);
其中想要阅读ImageKnifeComponent的请求加载的过程可以阅读ImageKnifeExecute方法
imageKnifeExecute() {
if (this.imageKnifeOption == null || this.imageKnifeOption.loadSrc == null) {
// 如果数据是null或者undefined,清空图片内容和gif循环,并且不进入图片请求流程
this.resetGifData()
if (this.canvasHasReady) {
// 如果canvas已经初始化好了,清空原有的canvas内容
this.context.clearRect(0, 0, this.context.width, this.context.height)
}
return
}
this.resetGifData()
let request = new RequestOption();
this.detachFromLayout = request.detachFromLayout;
this.configNecessary(request);
this.configCacheStrategy(request);
this.configDisplay(request);
this.configHspContext(request);
this.configRenderGpu(request);
if(ImageKnifeGlobal.getInstance().getImageKnife()!=undefined) {
ImageKnifeGlobal.getInstance().getImageKnife()?.call(request);
}
}
前面主要是请求链接的一些配置(包括当前组件大小、请求源、缓存策略、多个请求平行请求策略、context设置、是否启用gpu加载、占位图资源、error资源等)
最后具体让我们看一下call方法,在其中前面主要是还是设置必要的参数和信息(包括头、监听、缓存路径等),generateDataCacheKey是之前我们初始化EngineKeyFactories工厂并解析Key给到requestOption,最后传给parseSource
// 正常加载
call(request: RequestOption): void {
// 添加全局监听
if (this.defaultListener) {
request.addListener(this.defaultListener)
}
// 每个request 公共信息补充
request.setFilesPath(this.filesPath);
if (this.headerMap.size > 0) {
request.addHeaderMap(this.headerMap)
}
this.generateDataCacheKey(request)
// 首先执行占位图 解析任务
if (request.placeholderSrc) {
this.taskpoolLoadResource(request, Constants.PLACE_HOLDER);
}
// 其次执行重试占位图 解析任务
if (request.retryholderSrc) {
this.taskpoolLoadResource(request, Constants.RETRY_HOLDER);
}
// 最后解析错误占位图
if (request.errorholderSrc) {
this.taskpoolLoadResource(request, Constants.ERROR_HOLDER);
}
return this.parseSource(request);
}
接着跟踪parseSouce重点方法,可以看到先判断是否为PixelMap,如果是就加载完成,执行loadComplete,loadComplete我们后面再看。如果为本地资源、网络链接则继续加载,如果是项目内资源则不参与磁盘缓存再继续加载
private parseSource(request: RequestOption): void {
if ((typeof (request.loadSrc as image.PixelMap).isEditable) == 'boolean') {
let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, request.loadSrc as PixelMap)
request.loadComplete(imageKnifeData);
} else if (typeof request.loadSrc == 'string') {
// 进入三级缓存模型
return this.loadCacheManager(request);
} else {
let res = request.loadSrc as Resource;
if (typeof res.id != 'undefined' && typeof res.type != 'undefined') {
// 进入三级缓存模型 本地资源不参与磁盘缓存
let none = new NONE();
request.diskCacheStrategy(none);
this.loadCacheManager(request);
} else {
LogUtil.error("输入参数有问题!")
}
}
}
跟踪loadCacheManager重点方法,前面先判断是否全局设置了暂停请求,如果设置了暂停请求则存入map等恢复之后再加载。
如果正常加载,先判断是否三个key都不为空,如果不为空,会先判断是否在正在请求的链表中,已有了一样的request(三个Key都相等为一样),如果有一样的request,则放入等待队列。否则放入运行队列,继续加载。
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)
this.taskpoolLoadResource(request, Constants.MAIN_HOLDER);
}
}
else {
LogUtil.log("key没有生成无法进入存取!")
}
}
}
继续跟踪taskpoolLoadResource方法
这个方法比较长, 原因是统一处理了实际要加载的图片、展位图、重试图、错误处理图。
由于我们外面传的位Constants.MAIN_HOLDER,所以这里只看实际要加载的图片。
private taskpoolLoadResource(request: RequestOption, usageType: string) {
// 判断是否有内存缓存
let mainCache = this.memoryCacheProxy.loadMemoryCache(request.generateCacheKey, request.isCacheable);
let placeholderCache = this.memoryCacheProxy.loadMemoryCache(request.placeholderCacheKey, request.isCacheable);
let retryholderCache = this.memoryCacheProxy.loadMemoryCache(request.retryholderCacheKey, request.isCacheable);
let errorholderCacheKey = this.memoryCacheProxy.loadMemoryCache(request.errorholderCacheKey, request.isCacheable);
if (usageType == Constants.PLACE_HOLDER && placeholderCache && !mainCache && !retryholderCache && !errorholderCacheKey) {
LogUtil.info("imageknife load placeholder from MemoryCache")
request.placeholderOnComplete(placeholderCache);
return;
} else if (usageType == Constants.RETRY_HOLDER && retryholderCache && !mainCache && !errorholderCacheKey) {
LogUtil.info("imageknife load retryholder from MemoryCache")
request.retryholderOnComplete(retryholderCache);
return;
} else if (usageType == Constants.ERROR_HOLDER && errorholderCacheKey && !mainCache) {
LogUtil.info("imageknife load errorholder from MemoryCache")
request.errorholderOnComplete(errorholderCacheKey);
return;
} else if (usageType == Constants.MAIN_HOLDER && mainCache) {
LogUtil.info("imageknife load mainsource from MemoryCache")
// 不走磁盘缓存,命中内存缓存,加载成功
mainCache.waitSaveDisk = false;
request.loadComplete(mainCache);
return;
}
let taskParams: TaskParams = this.assembleTaskParams(request, usageType);
let loadSrcJson = JSON.stringify({
loadSrc: request.loadSrc,
placeholderSrc: request.placeholderSrc,
errorholderSrc: request.errorholderSrc,
retryholderSrc: request.retryholderSrc,
});
//使用taskpool多线程执行资源下载
let task: ESObject = new taskpool.Task(taskExecute, taskParams, loadSrcJson)
task.setTransferList([])
emitter.on(Constants.PROGRESS_EMITTER as ESObject, (data: ESObject) => {
if (request.progressFunc && data?.data?.value) {
let percent = data.data.value as number;
request.progressFunc.asyncSuccess(percent);
}
});
taskpool.execute(task,request.priority).then((data: ESObject) => {
if (usageType == Constants.PLACE_HOLDER) {
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)
} else {
request.placeholderOnError("request placeholder error")
}
} else if (usageType == Constants.RETRY_HOLDER) {
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)
} else {
request.retryholderOnError("request retryholder error")
}
} else if (usageType == Constants.ERROR_HOLDER) {
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)
} else {
request.errorholderOnError("request errorholder error")
}
} 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)
this.setDiskCache(request)
} 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)
this.setDiskCache(request)
} else {
request.loadError("request resources error")
}
}
}).catch((err: BusinessError | string) => {
request.loadError(err)
})
}
首先是判断是否有内存缓存,如果命中则执行加载成功回调,关键代码:
...
let mainCache = this.memoryCacheProxy.loadMemoryCache(request.generateCacheKey, request.isCacheable);
...
...
} else if (usageType == Constants.MAIN_HOLDER && mainCache) {
LogUtil.info("imageknife load mainsource from MemoryCache")
mainCache.waitSaveDisk = false;
request.loadComplete(mainCache);
return;
}
....
其中loadMemoryCache主要是封装了一个map,并有设置最大内存缓存和当前缓存大小两个属性。
后面的代码主要是设置了磁盘缓存和网络下载执行成功之后的回调实现。
回滴中主要是设置内存缓存和磁盘缓存
我们先看Task执行内容,看磁盘缓存和网络下载
@Concurrent
async function taskExecute(taskParams: TaskParams, loadSrcJson: string): Promise<PixelMap | GIFFrame[]> {
let emitProgressPercent = (percentValue: number) => {
let eventData: emitter.EventData = {
data: {
"value": percentValue,
}
};
emitter.emit(Constants.PROGRESS_EMITTER as ESObject, eventData)
}
let transformations = taskParams.transformations;
let usageType = taskParams.usageType;
let displayProgress = taskParams.displayProgress;
//子线程构造RequestOption对象
let newRequestOption = new RequestOption();
let loadSrcObj: object = JSON.parse(loadSrcJson);
newRequestOption.priority = taskParams.priority
newRequestOption.uuid = taskParams.uuid;
newRequestOption.loadSrc = loadSrcObj["loadSrc"] as string | PixelMap | Resource;
newRequestOption.dontAnimateFlag = taskParams.dontAnimateFlag;
newRequestOption.generateCacheKey = taskParams.generateCacheKey;
newRequestOption.generateResourceKey = taskParams.generateResourceKey;
newRequestOption.generateDataKey = taskParams.generateDataKey;
newRequestOption.thumbSizeMultiplier = taskParams.thumbSizeMultiplier;
newRequestOption.thumbDelayTime = taskParams.thumbDelayTime;
newRequestOption.size = taskParams.size;
newRequestOption.diskMemoryCachePath = taskParams.diskMemoryCachePath;
newRequestOption.placeholderSrc = loadSrcObj["placeholderSrc"] as PixelMap | Resource | undefined;
newRequestOption.errorholderSrc = loadSrcObj["errorholderSrc"] as PixelMap | Resource | undefined;
newRequestOption.retryholderSrc = loadSrcObj["retryholderSrc"] as PixelMap | Resource | undefined;
newRequestOption.onlyRetrieveFromCache = taskParams.onlyRetrieveFromCache;
newRequestOption.gpuEnabled = taskParams.gpuEnabled;
newRequestOption.headers = taskParams.headers;
newRequestOption.signature = taskParams.signature;
ImageKnifeGlobal.getInstance().setHapContext(taskParams.moduleContext as common.UIAbilityContext);
newRequestOption.moduleContext = taskParams.moduleContext;
newRequestOption.isCacheable = taskParams.isCacheable;
if (displayProgress) {
newRequestOption.addProgressListener({
asyncSuccess: (percentValue: number) => {
// 如果进度条百分比 未展示大小,展示其动画
emitProgressPercent(percentValue)
}
})
}
//如果是本地图片不作磁盘缓存
if (typeof newRequestOption.loadSrc !== 'string') {
let none = new NONE();
newRequestOption.diskCacheStrategy(none);
}
if (usageType == Constants.PLACE_HOLDER) {
let manager = new PlaceHolderManager<PixelMap>(newRequestOption);
return await new Promise<PixelMap>(manager.process);
} else if (usageType == Constants.RETRY_HOLDER) {
let manager = new RetryHolderManager<PixelMap>(newRequestOption);
return await new Promise<PixelMap>(manager.process);
} else if (usageType == Constants.ERROR_HOLDER) {
let manager = new ErrorHolderManager<PixelMap>(newRequestOption);
return await new Promise<PixelMap>(manager.process);
} else {
if (transformations) {
newRequestOption.setTransformations(TransformUtils.addTransformations(transformations))
}
let newDataFetch = new DownloadClient();
let newResourceFetch = new ParseResClient();
let manager = new RequestManager(newRequestOption, newDataFetch, newResourceFetch);
return await new Promise<PixelMap | GIFFrame[]>(manager.process);
}
}
该task前半部分主要是解析request参数,并设置加载进度的回调(用于向外传递加载进度),关键代码:
...
if (transformations) {
newRequestOption.setTransformations(TransformUtils.addTransformations(transformations))
}
let newDataFetch = new DownloadClient();
let newResourceFetch = new ParseResClient();
let manager = new RequestManager(newRequestOption, newDataFetch, newResourceFetch);
return await new Promise<PixelMap | GIFFrame[]>(manager.process);
...
这里设置了全局加载的Client、ResourceClient等。
这里实际上也有一个bug,即使用imageKnife调用replaceDataFetch方法设置数据加载器,是不生效的!!!!!
因为这里写死了,并没有使用外部替换的数据加载器
我们先接着看manager.process,其中一连串的执行了
loadLeve1MemoryCache、runWrapped,最后执行到SearchLoadFromSource
private searchLoadFrom(request: RequestOption, current: Stage, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike<ImageKnifeData>, onError: (reason?: BusinessError | string) => void) {
LogUtil.log("ImageKnife RequestManager searchLoadFrom")
if (current == Stage.RESOURCE_CACHE) {
this.loadDiskFromTransform(request, onComplete, onError);
} else if (current == Stage.DATA_CACHE) {
this.loadDiskFromSource(request, onComplete, onError);
} else if (current == Stage.SOURCE) {
this.parseSource(request, onComplete, onError)
} else if (current == Stage.FINISHED) {
onError("在仅从缓存获取数据中,未获取到数据!")
} else {
throw new Error("Unrecognized stage: " + current);
}
}
这个方法根据需要按等级顺序执行等级大于当前等级的方法,由于我们调用的位loadLeve1MemoryCache,所以是从loadDiskFromTransform开始执行,这一级为加载图片变换后的缓存
private loadDiskFromTransform(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike<ImageKnifeData>, onError: (reason?: BusinessError | string) => void) {
LogUtil.log("ImageKnife RequestManager loadDiskFromTransform")
let cached = DiskLruCache.getFileCacheByFile(request.diskMemoryCachePath, request.generateResourceKey);
if (cached != null) {
LogUtil.log("ImageKnife loadDiskFromTransform load resource from DiskLruCache")
this.parseDiskTransformFile2PixelMap(request, cached, onComplete, onError)
} else {
this.mStage = Stage.DATA_CACHE;
this.searchLoadFrom(this.options, this.mStage, onComplete, onError);
}
}
其中关键方法getFileCacheByFile
/**
* 子线程中,通过文件名,直接查找是否有文件缓存
* @param context
* @param key
* @returns
*/
static getFileCacheByFile(path: string, key: string): ArrayBuffer | undefined {
// 从文件获取查看是否有缓存
if (!!!key) {
throw new Error('key is null,checking the parameter');
}
key = SparkMD5.hashBinary(key)
let filepath = path + key;
if (FileUtils.getInstance().exist(filepath)) {
let ab: ArrayBuffer = FileUtils.getInstance().readFile(filepath)
return ab
} else {
return undefined;
}
}
可以看到找的路径为:我们设置的缓存路径(或是默认缓存路径)+当前对应的Key md5编码过后的值
如果未找到,则返回执行loadDiskFromSource,该方法执行和loadDiskFromTransform差不多,不再分析。
接着走到parseSource
parseSource(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike<ImageKnifeData>, onError: (reason?: BusinessError | string) => void) {
LogUtil.log("ImageKnife RequestManager parseSource")
try {
if ((typeof (request.loadSrc as image.PixelMap).isEditable) == 'boolean') {
// PixelMap 外层捕获效率更高,不会进入这里
} else if (typeof request.loadSrc == 'string') {
this.loadSourceFromNetwork(request, onComplete, onError);
} else {
let res = request.loadSrc as Resource;
if (typeof res.id != 'undefined') {
this.loadSourceFormNative(request, onComplete, onError)
} else {
LogUtil.log("输入参数有问题!")
}
}
} catch (e) {
LogUtil.error("ImageKnife RequestManager parseSource error")
}
}
我们重点看一下loadSourceFromNetwork,即最后通过网络加载图片
// 加载网络资源
private loadSourceFromNetwork(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike<ImageKnifeData>, onError: (reason?: BusinessError | string) => void) {
try {
LogUtil.log("ImageKnife RequestManager loadSourceFromNetwork")
let success = (arraybuffer: ArrayBuffer) => {
this.downloadSuccess(request, arraybuffer, onComplete, onError)
}
let error = (errorMsg: string) => {
onError(errorMsg)
}
this.mIDataFetch.loadData(request, success, error);
} catch (e) {
LogUtil.error("ImageKnife RequestManager loadSourceFromNetwork error")
}
}
其中IDataFetch.loadData,则为具体的网络请求加载图片,官方表示支持自定义。但看流程和现象由于外部写死为默认实现DownloadClient,所以自定义Client会不生效。
内存缓存和磁盘缓存的保存
关键代码在taskpoolLoadResource中
其中imageKnifeData为PixelMap封装
loadComplete为我们自定义的回调
memoryCacheProxy.putValue 位ImageKnife自己维护的内存缓存map
setDiskCache即为加载下来的图片保存到指定的路径中,并将映射关系保存到DiskLruCache中的CacheMap里。(ImageKnife持有DiskLruCache,所以保存过多张的磁盘缓存图片,会导致内存中的cacheMap很大,内存缓存的map同理)
let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, data as PixelMap);
request.loadComplete(imageKnifeData)
this.memoryCacheProxy.putValue(request.generateCacheKey,imageKnifeData)
this.setDiskCache(request)
更多推荐
所有评论(0)