探究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的缓存体系设计上遵循单一职责.开闭原则.高度解耦.及其精巧,充分的将缓存分层,其独到之处可以套用到很多类似的业务上.这里将主要 ...
随机推荐
- windows和linux修改python的pip源
python的pip安装包非常方便,然而其默认的镜像源在国外,下载的速度非常慢,推荐改成国内的镜像源. window平台修改pip源 找到系统盘下C:\C:\Users\用户名\AppData\Roa ...
- Mutex, semaphore, spinlock的深度解析
Mutex是一把钥匙,一个人拿了就可进入一个房间,出来的时候把钥匙交给队列的第一个.一般的用法是用于串行化对critical section代码的访问,保证这段代码不会被并行的运行. Semaphor ...
- Android 反编译神器jadx的使用
一.前言 今天介绍一个非常好用的反编译的工具 jadx .jadx 的功能非常的强大,对我而言,基本上满足日常反编译需求. jadx 优点: 图形化的界面. 拖拽式的操作. 反编译输出 Java 代码 ...
- 121.Best Time to Buy and Sell Stock---dp
题目链接:https://leetcode.com/problems/best-time-to-buy-and-sell-stock/description/ 题目大意:给出一串数组,找到差值最大的差 ...
- python是如何进行内存管理的?
Python内存管理机制 Python内存管理机制主要包括以下三个方面: 引用计数机制 垃圾回收机制 内存池机制 引用计数 举个例子说明引用是什么: 1 如上为一个简单的赋值语句,1就是对象,a就是引 ...
- [ python ] 线程的操作
目录 (见右侧目录栏导航) - 1. 前言 - 1.1 进程 - 1.2 有了进程为什么要有线程 - 1.3 线程的出现 - 1.4 进程和线程的关系 - 1.5 线程的 ...
- Linux Supervisor的安装与使用入门---Ubuntun
Linux Supervisor的安装与使用入门 在linux或者unix操作系统中,守护进程(Daemon)是一种运行在后台的特殊进程,它独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事 ...
- hive学习(八)hive优化
Hive 优化 1.核心思想: 把Hive SQL 当做Mapreduce程序去优化 以下SQL不会转为Mapreduce来执行 select仅查询本表字段 where仅对本表字段做条件过滤 Ex ...
- Pylint在项目中的使用
需求背景: Pylint 是一个 Python 代码分析工具,它分析 Python 代码中的错误,查找不符合代码风格标准和有潜在问题的代码. Pylint 是一个 Python 工具,除了平常代码分析 ...
- Base64的好处
1. 昨天的<MIME笔记>中提到,MIME主要使用两种编码转换方式----Quoted-printable和Base64----将8位的非英语字符转化为7位的ASCII字符. 虽然这样的 ...