您好,登錄后才能下訂單哦!
小編給大家分享一下react-native源碼圖片緩存問題的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
為xcode模擬器測試,rn版本0.44.3
突然想學習下RN是如何封裝ios中的UIImage的,看著看著發現圖片的緩存問題是個坑。。。
先看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,依次得到下面結果:
可見,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]; }; // 走具體的方法加載圖片,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 * 4; 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源碼圖片緩存問題的示例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。