探究react-native 源码的图片缓存
先看js端图片使用的三种方式,依次排序1、2、3
<Image source={{uri:url}} style={{width:200,height:200}}/>
1、 加载远程图片
<Image source={{uri:'1.png'}} style={{width:50,height:50}}/>
2、加载xcode中图片
<Image source={require('../../../Resources/Images/Contact/conact_searchIcon@3x.png')}/> 3、加载js中图片
1、2必须设置图片宽高,3不需设置。
对应的ios原生端文件是RCTImageViewManager,暴露的属性
RCT_REMAP_VIEW_PROPERTY(source, imageSources, NSArray<RCTImageSource *>);
就是js中Image组件的属性source,在js中设置source会触发该属性的setter方法。进入RCTImageView的
- (void)setImageSources:(NSArray<RCTImageSource *> *)imageSources
{
if (![imageSources isEqual:_imageSources]) {
_imageSources = [imageSources copy];
[self reloadImage];
}
}
通过此方法中断点打印imageSources,依次得到下面结果:

加载网络上图片 : http://
加载xcode资源 : file://
加载js中图片 : http://localhost:8081
追踪setter方法,到RCTImageLoader.m中的如下方法
- (RCTImageLoaderCancellationBlock)loadImageWithURLRequest:(NSURLRequest *)imageURLRequest
size:(CGSize)size
scale:(CGFloat)scale
clipped:(BOOL)clipped
resizeMode:(RCTResizeMode)resizeMode
progressBlock:(RCTImageLoaderProgressBlock)progressBlock
partialLoadBlock:(RCTImageLoaderPartialLoadBlock)partialLoadBlock
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
{
__block volatile uint32_t cancelled = ;
__block dispatch_block_t cancelLoad = nil;
dispatch_block_t cancellationBlock = ^{
dispatch_block_t cancelLoadLocal = cancelLoad;
if (cancelLoadLocal && !cancelled) {
cancelLoadLocal();
}
OSAtomicOr32Barrier(, &cancelled);
};
// 下载图片完成后回调
__weak RCTImageLoader *weakSelf = self;
void (^completionHandler)(NSError *, id, BOOL, NSString *) = ^(NSError *error, id imageOrData, BOOL cacheResult, NSString *fetchDate) {
__typeof(self) strongSelf = weakSelf;
if (cancelled || !strongSelf) {
return;
}
// 如果imageOrData是图片类型,则直接回调
// 此处,如果是第二种情况,则会满足,其他情况继续走下面方法
if (!imageOrData || [imageOrData isKindOfClass:[UIImage class]]) {
cancelLoad = nil;
completionBlock(error, imageOrData);
return;
} // 在内存中查看是否存在该url对应的字节码图片
if (cacheResult) {
UIImage *image = [[strongSelf imageCache] imageForUrl:imageURLRequest.URL.absoluteString
size:size
scale:scale
resizeMode:resizeMode
responseDate:fetchDate];
if (image) {
cancelLoad = nil;
completionBlock(nil, image);
return;
}
} // 若没有缓存,则将图片解压,再将解压后图片缓存block
RCTImageLoaderCompletionBlock decodeCompletionHandler = ^(NSError *error_, UIImage *image) {
if (cacheResult && image) {
// Store decoded image in cache
[[strongSelf imageCache] addImageToCache:image
URL:imageURLRequest.URL.absoluteString
size:size
scale:scale
resizeMode:resizeMode
responseDate:fetchDate];
} cancelLoad = nil;
completionBlock(error_, image);
};
// 具体的解压过程
cancelLoad = [strongSelf decodeImageData:imageOrData
size:size
scale:scale
clipped:clipped
resizeMode:resizeMode
completionBlock:decodeCompletionHandler];
};
// 走具体的方法加载图片,1、3种情况用网络请求下载,2情况加载本地文件
cancelLoad = [self _loadImageOrDataWithURLRequest:imageURLRequest
size:size
scale:scale
resizeMode:resizeMode
progressBlock:progressBlock
partialLoadBlock:partialLoadBlock
completionBlock:completionHandler];
return cancellationBlock;
}
具体的缓存类是RCTImageCache,采用NSCache缓存,方法
- (void)addImageToCache:(UIImage *)image
forKey:(NSString *)cacheKey
{
if (!image) {
return;
}
CGFloat bytes = image.size.width * image.size.height * image.scale * image.scale * ;
if (bytes <= RCTMaxCachableDecodedImageSizeInBytes) {
[self->_decodedImageCache setObject:image
forKey:cacheKey
cost:bytes];
}
}
RCTMaxCachableDecodedImageSizeInBytes是个常量,为1048576,也就是只缓存小于1MB的图片。
问题出在cacheKey,查看缓存key的方法
static NSString *RCTCacheKeyForImage(NSString *imageTag, CGSize size, CGFloat scale,
RCTResizeMode resizeMode, NSString *responseDate)
{
return [NSString stringWithFormat:@"%@|%g|%g|%g|%zd|%@",
imageTag, size.width, size.height, scale, resizeMode, responseDate];
}
缓存key的生成方法中包含了responseDate,responseDate是网络请求时返回来的
responseDate = ((NSHTTPURLResponse *)response).allHeaderFields[@"Date"];
1、3方式每次加载都是一个网络请求,那么网络请求的时间总是变化的,于是responseDate是变化的,cacheKey不唯一,所以虽然系统做了图片的缓存,但是每次取出的都为nil,缓存无效。
2方式加载具体方法在RCTLocalAssetImageLoader.m中,其调用的是RCTUtils的RCTImageFromLocalAssetURL方法
UIImage *__nullable RCTImageFromLocalAssetURL(NSURL *imageURL)
{
// .....省略各种处理
UIImage *image = nil;
if (bundle) {
image = [UIImage imageNamed:imageName inBundle:bundle compatibleWithTraitCollection:nil];
} else {
image = [UIImage imageNamed:imageName];
}
// .....省略各种处理
return image;
}
可见是采用[UIImage imageNamed:imageName]的方式加载xcode自带的图片,这个是有内存缓存的。
综上,对react-native图片加载
1、3情况,没有内存缓存
2情况有系统默认的内存缓存
所有情况都没有磁盘缓存
想让内存缓存生效,只需要改变cacheKey的生成规则即可。
补充:沙盒下面的Library/Caches/项目bunderId号/fsCachedData文件夹里面会磁盘缓存大于一定值(测试约为5kb)的图片和文件,这个是NSURLSession网络请求系统默认的缓存类NSURLCache自动生成的,非图片的磁盘缓存。
探究react-native 源码的图片缓存的更多相关文章
- 《React Native 精解与实战》书籍连载「React Native 源码学习方法及其他资源」
此系列文章将整合我的 React 视频教程与 React Native 书籍中的精华部分,给大家介绍 React Native 源码学习方法及其他资源. 最后的章节给大家介绍 React Native ...
- jQuery1.9.1源码分析--数据缓存Data模块
jQuery1.9.1源码分析--数据缓存Data模块 阅读目录 jQuery API中Data的基本使用方法介绍 jQuery.acceptData(elem)源码分析 jQuery.data(el ...
- lodash源码分析之缓存使用方式的进一步封装
在世界上所有的民族之中,支配着他们的喜怒选择的并不是天性,而是他们的观点. --卢梭<社会与契约论> 本文为读 lodash 源码的第九篇,后续文章会更新到这个仓库中,欢迎 star:po ...
- React Fiber源码分析 (介绍)
写了分析源码的文章后, 总觉得缺少了什么, 在这里补一个整体的总结,输出个人的理解~ 文章的系列标题为Fiber源码分析, 那么什么是Fiber,官方给出的解释是: React Fiber是对核心算法 ...
- 【转】MaBatis学习---源码分析MyBatis缓存原理
[原文]https://www.toutiao.com/i6594029178964673027/ 源码分析MyBatis缓存原理 1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 ...
- 高级运维(六):源码安装Redis缓存服务、常用Redis数据库操作指令、配置Redis主从服务器
一.源码安装Redis缓存服务 目标: 本案例要求先快速搭建好一台Redis服务器,并测试该缓存服务器: 1> 设置变量test,值为123 2> 查看变量test的值 3> 设置计 ...
- React的React.createContext()源码解析(四)
一.产生context原因 从父组件直接传值到孙子组件,而不必一层一层的通过props进行传值,相比较以前的那种传值更加的方便.简介. 二.context的两种实现方式 1.老版本(React16.x ...
- React的React.createElement源码解析(一)
一.什么是jsx jsx是语法糖 它是js和html的组合使用 二.为什么用jsx语法 高效定义模版,编译后使用 不会带来性能问题 三.jsx语法转化为js语法 jsx语法通过babel转化为 ...
- Mybatis源码手记-从缓存体系看责任链派发模式与循环依赖企业级实践
一.缓存总览 Mybatis在设计上处处都有用到的缓存,而且Mybatis的缓存体系设计上遵循单一职责.开闭原则.高度解耦.及其精巧,充分的将缓存分层,其独到之处可以套用到很多类似的业务上.这里将主要 ...
随机推荐
- html中去掉文本框(input type="text")的边框或只显示下边框
去掉: <input type="text" name="textfield" style="border:0px;"&g ...
- mybatis注解使用
spring整合mybatis时,要使用mybatis的注解,需要spring-config.xml文件中添加下述配置: <!--下述配置指明了我们的Mapper,即Dao,在相应的包里也就可以 ...
- Photon3Unity3D.dll 解析二——EventData
EventData 包含Photon事件的所有内容 Code 用于表示事件,相当于主键ID,LiteEventCode定义了一部分服务端普遍事件事件: Parameters 事 ...
- poj 2420(模拟退火)
A Star not a Tree? Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 6066 Accepted: 285 ...
- 教你如何更改android应用的包名
Android 源码自带了很多应用程序,想改个包名方便修改?很简单,两步搞定,以packages/apps/Settings为例: 1.打开AndroidManifest.xml,把 <mani ...
- HTML常用标签及其属性
基本 <html>…</html> 定义 HTML 文档 <head>…</head> 文档的信息 <meta> HTML 文档的元信息 & ...
- fatal error LNK1104: 无法打开文件“libc.lib”的问题
如果将用低版本的VC开发的项目,拿到高版本的VC开发环境上去编译,链接时也许会触发LNK1104错误.解决方案是链接时忽略此库,在此提供三种解决方案: 1.解决如下:项目->属性中->配置 ...
- day3修改配置文件
有如下配置文件,在指定文件位置添加一条新的记录: global log 127.0.0.1 local2 daemon maxconn log 127.0.0.1 local2 info defaul ...
- Technology share: VR is coming,are you ready?
►Date 2016-10-18 ►Address 上海市浦东新区严家桥1号宏慧音悦湾3号楼5楼 VR SPACE ►Events 品牌如何抢先一步,借玩VR吸引眼球,如何让客户作为VR买单? 如何结 ...
- Java 中的静态嵌套类和非静态嵌套类
Java 中的静态嵌套类和非静态嵌套类 术语:嵌套类分为两类:静态嵌套类和非静态嵌套类.声明 static 的嵌套类称为静态嵌套类,非静态嵌套类也称为内部类. class OuterClass { p ...