探究react-native 源碼的圖片緩存

先看js端圖片使用的三種方式,依次排序一、二、3react

<Image source={{uri:url}} style={{width:200,height:200}}/>    
一、 加載遠程圖片
 <Image source={{uri:'1.png'}} style={{width:50,height:50}}/>  
二、加載xcode中圖片
 <Image source={require('../../../Resources/Images/Contact/conact_searchIcon@3x.png')}/>  三、加載js中圖片

一、2必須設置圖片寬高,3不需設置。ios

對應的ios原生端文件是RCTImageViewManager,暴露的屬性react-native

RCT_REMAP_VIEW_PROPERTY(source, imageSources, NSArray<RCTImageSource *>);

就是js中Image組件的屬性source,在js中設置source會觸發該屬性的setter方法。進入RCTImageView的xcode

- (void)setImageSources:(NSArray<RCTImageSource *> *)imageSources { if (![imageSources isEqual:_imageSources]) { _imageSources = [imageSources copy]; [self reloadImage]; } }

經過此方法中斷點打印imageSources,依次獲得下面結果:緩存

 
可見,Image組件加載圖片都是採用URL的形式,將圖片看成網絡資源。不一樣的是URL的類型:
加載網絡上圖片         :  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 = 0; __block dispatch_block_t cancelLoad = nil; dispatch_block_t cancellationBlock = ^{ dispatch_block_t cancelLoadLocal = cancelLoad; if (cancelLoadLocal && !cancelled) { cancelLoadLocal(); } OSAtomicOr32Barrier(1, &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]; }; // 走具體的方法加載圖片,一、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 * 4; if (bytes <= RCTMaxCachableDecodedImageSizeInBytes) { [self->_decodedImageCache setObject:image forKey:cacheKey cost:bytes]; } }

RCTMaxCachableDecodedImageSizeInBytes是個常量,爲1048576,也就是隻緩存小於1MB的圖片。
問題出在cacheKey,查看緩存key的方法fetch

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是網絡請求時返回來的ui

responseDate = ((NSHTTPURLResponse *)response).allHeaderFields[@"Date"];

一、3方式每次加載都是一個網絡請求,那麼網絡請求的時間老是變化的,因而responseDate是變化的,cacheKey不惟一,因此雖然系統作了圖片的緩存,可是每次取出的都爲nil,緩存無效。url

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圖片加載
一、3狀況,沒有內存緩存
2狀況有系統默認的內存緩存
全部狀況都沒有磁盤緩存

想讓內存緩存生效,只須要改變cacheKey的生成規則便可。

補充:沙盒下面的Library/Caches/項目bunderId號/fsCachedData文件夾裏面會磁盤緩存大於必定值(測試約爲5kb)的圖片和文件,這個是NSURLSession網絡請求系統默認的緩存類NSURLCache自動生成的,非圖片的磁盤緩存。

相關文章
相關標籤/搜索