簡(jiǎn)單時(shí)序圖
注:為了便于理解肺樟,把 RCTImageLoader 的幾個(gè)方法當(dāng)做獨(dú)立對(duì)象對(duì)待了(用方框框出來(lái)的是三個(gè) RCTImageLoader 對(duì)象方法)讯嫂。
本文是基于 react-native 0.37.0 版本進(jìn)行分析的哥攘。
RCTImageView
- 1虱颗、初始化
- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
- 2罗丰、通過(guò) setProps(這個(gè) props 就是我們?cè)?JS 里為 <Image> 標(biāo)簽設(shè)定的屬性值椎眯,其中就包括 URI) 調(diào)到了setResizeMode:
- (void)setResizeMode:(RCTResizeMode)resizeMode
- 3挠将、setResizeMode: 調(diào)到了 reloadImage
- (void)reloadImage
- 4、reloadImage 先做了兩件事:1编整、cancelImageLoad舔稀。2、imageSourceForSize掌测。
- (void)cancelImageLoad
- cancelImageLoad里如果有之前保存的 _reloadImageCancellationBlock内贮,則執(zhí)行該 block,并且置為 nil汞斧,同時(shí)將 _pendingImageSource 置為 nil夜郁。
- imageSourceForSize,如果傳進(jìn)來(lái)的 size 是(0,0)断箫,直接返回 nil拂酣,如果不是,則找一個(gè)最合適的 RCTImageSource仲义。
- 然后 reloadImage 如果獲取到了合適的 RCTImageSource 則進(jìn)入下載流程婶熬,否則 clearImage剑勾。
if (source && self.frame.size.width > 0 && self.frame.size.height > 0) {
// 篇幅原因隱藏了接下來(lái)要講的代碼
}
- 定義了三個(gè)回調(diào):progressHandler、partialLoadHandler赵颅、completionHandler虽另,用于下載進(jìn)度、部分下載饺谬、下載完成調(diào)用捂刺。將三個(gè) block 作為參數(shù)和 source、size募寨、resizeMode 一起通過(guò) imageLoader 的 loadImageVithURLRequest:傳遞給 imageLoader族展。并且用 _reloadImageCancellationBlock 接收該方法的返回值(上面提到的 cancelImageLoad 里調(diào)用的就是這個(gè) block)
RCTImageLoaderProgressBlock progressHandler
RCTImageLoaderPartialLoadBlock partialLoadHandler
RCTImageLoaderCompletionBlock completionHandler
_reloadImageCancellationBlock =
[_bridge.imageLoader loadImageWithURLRequest:source.request
size:imageSize
scale:imageScale
clipped:NO
resizeMode:_resizeMode
progressBlock:progressHandler
partialLoadBlock:partialLoadHandler
completionBlock:completionHandler];
注:這里還不知道我們要加載的圖片是來(lái)自網(wǎng)絡(luò)還是來(lái)自本地(這里是 RN 所有圖片的入口)。
RCTImageLoader
- 5拔鹰、loadImageWithURLRequest:先定義一個(gè) cancelLoad 回調(diào)用于接收 _loadImageOrDataWithURLRequest: 或 decodeImageData: 的返回值仪缸。 cancellationBlock 返回給調(diào)用方,再定義一個(gè) completionHandler 傳遞給 _loadImageOrDataWithURLRequest:
__block dispatch_block_t cancelLoad
dispatch_block_t cancellationBlock
void (^completionHandler)(NSError *, id, BOOL, NSString *)
- cancellationBlock 里調(diào)用了上面定義的 cancelLoad 回調(diào)列肢,并且在線程鎖內(nèi)修改表示變量 cancelled 的值恰画。
- completionHandler 里先判斷是否被取消或者 self 有沒有被釋放,然后判斷如果 imageData為nil 或者 imageData 已經(jīng)是 UIImage 類型就直接回調(diào)給調(diào)用方并且return瓷马。completionHandler 接受 4 個(gè)參數(shù):
^(NSError *error, id imageOrData, BOOL cacheResult, NSString *fetchDate) {}
- cacheResult 是 _loadImageOrDataWithURLRequest: 回傳過(guò)來(lái)的表示圖片(本地圖片)是否有緩存拴还,如果有則查找緩存并對(duì)調(diào)給調(diào)用方并且return,沒有則定義一個(gè) decodeCompletionHandler 并且執(zhí)行 decodeImageData(后文再講)欧聘,用剛才定義的 cancelLoad 接收返回值片林。
cancelLoad = [self _loadImageOrDataWithURLRequest:imageURLRequest
size:size
scale:scale
resizeMode:resizeMode
progressBlock:progressBlock
partialLoadBlock:partialLoadBlock
completionBlock:completionHandler];
- 6、_loadImageOrDataWithURLRequest 是一個(gè) 返回值為 RCTImageLoaderCancellationBlock 的方法怀骤。
- _loadImageOrDataWithURLRequest拇厢,先根據(jù) URL 通過(guò) imageURLLoaderForURL: 查找最合適的 id<RCTImageURLLoader> loadHandler (loadHandler 為本地圖片加載器,后文講它是怎么來(lái)的)晒喷,然后也定義了一個(gè) completionHandler,這個(gè) block 接收3個(gè)參數(shù):error访敌、imageOrData凉敲、fetchDate,在 completionHandler 里判斷如果當(dāng)前線程為主線程并且 imageData 不是 UIImage 類型則扔到全局隊(duì)列里回調(diào)給調(diào)用方(即loadImageWithURLRequest:方法的 completionHandler)寺旺,否則直接回調(diào)給調(diào)用方爷抓。
id<RCTImageURLLoader> loadHandler = [self imageURLLoaderForURL:request.URL];
void (^completionHandler)(NSError *, id, NSString *)
- 7、接下來(lái)開始加載圖片阻塑,如果有 loadHandler 則執(zhí)行本地圖片加載蓝撇,否則執(zhí)行網(wǎng)絡(luò)下載。
if (loadHandler) {
cancelLoad = [loadHandler loadImageForURL:request.URL
size:size
scale:scale
resizeMode:resizeMode
progressHandler:progressHandler
partialLoadHandler:partialLoadHandler
completionHandler:^(NSError *error, UIImage *image) {
completionHandler(error, image, nil);
}];
} else {
// Use networking module to load image
cancelLoad = [strongSelf _loadURLRequest:request
progressBlock:progressHandler
completionBlock:completionHandler];
}
- 8陈莽、看網(wǎng)絡(luò)圖片加載部分渤昌,先定義一個(gè)
RCTURLRequestCompletionBlock processResponse
它接收三個(gè)參數(shù):
^(NSURLResponse *response, NSData *data, NSError *error)
processResponse 里面判斷網(wǎng)絡(luò)狀態(tài)碼并且把 data 回調(diào)給調(diào)用方(_loadImageOrDataWithURLRequest)虽抄,然后 RCTNetworking 開始網(wǎng)絡(luò)請(qǐng)求圖片。下載完成后就開始了層層回調(diào)独柑。
- 一直回調(diào)到步驟 5 這里執(zhí)行 decodeImageData:
- 9迈窟、completionHandler,先定義一個(gè) completionHandler 接受兩個(gè)參數(shù)(error,image)并且在非主線程隊(duì)列回調(diào)給調(diào)用方忌栅,然后去查找一個(gè)(本地)圖片 decoder车酣,如果沒有則在 _URLRequestQueue 隊(duì)列里進(jìn)行解碼,解碼完成 completionHandler(error,image)索绪。
- 現(xiàn)在回到步驟 7湖员,看下 loadHandler 是個(gè)什么鬼。
// Find suitable image URL loader
id<RCTImageURLLoader> loadHandler = [self imageURLLoaderForURL:request.URL];
可以看到 loadHandler 是這么來(lái)的瑞驱,進(jìn)去 imageURLLoaderForURL: 方法里發(fā)現(xiàn) loadHandler 是從一個(gè) _loaders 的數(shù)組里挑選出來(lái)的娘摔,_loaders 數(shù)組是怎么來(lái)的呢,繼續(xù)看代碼:
_loaders = [[_bridge modulesConformingToProtocol:@protocol(RCTImageURLLoader)] sortedArrayUsingComparator:^NSComparisonResult(id<RCTImageURLLoader> a, id<RCTImageURLLoader> b) {
float priorityA = [a respondsToSelector:@selector(loaderPriority)] ? [a loaderPriority] : 0;
float priorityB = [b respondsToSelector:@selector(loaderPriority)] ? [b loaderPriority] : 0;
if (priorityA > priorityB) {
return NSOrderedAscending;
} else if (priorityA < priorityB) {
return NSOrderedDescending;
} else {
return NSOrderedSame;
}
}];
關(guān)鍵代碼:
[_bridge modulesConformingToProtocol:@protocol(RCTImageURLLoader)]
繼續(xù)往里跟:
- (NSArray *)modulesConformingToProtocol:(Protocol *)protocol
{
NSMutableArray *modules = [NSMutableArray new];
for (Class moduleClass in self.moduleClasses) {
if ([moduleClass conformsToProtocol:protocol]) {
id module = [self moduleForClass:moduleClass];
if (module) {
[modules addObject:module];
}
}
}
return [modules copy];
}
畫重點(diǎn):conformsToProtocol:
方法是檢查某個(gè)對(duì)象以及它的祖先有沒有實(shí)現(xiàn)某個(gè)方法钱烟。self.moduleClasses 是個(gè)什么東西呢晰筛,它是所有遵守了 RCTBridgeModule 協(xié)議及 RCTBridgeModule 子孫協(xié)議的 Class 的集合。這個(gè)集合里面遵守了 RCTImageURLLoader 協(xié)議的有兩個(gè) RCTLocalAssetImageLoader 和 RCTPhotoLibraryImageLoader拴袭。