react-native RCTImage源碼初解析(iOS)

簡(jiǎn)單時(shí)序圖

注:為了便于理解肺樟,把 RCTImageLoader 的幾個(gè)方法當(dāng)做獨(dú)立對(duì)象對(duì)待了(用方框框出來(lái)的是三個(gè) RCTImageLoader 對(duì)象方法)讯嫂。

RCTImage_Sequence_Diagram.jpg

本文是基于 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拴袭。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末读第,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子拥刻,更是在濱河造成了極大的恐慌怜瞒,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件般哼,死亡現(xiàn)場(chǎng)離奇詭異吴汪,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蒸眠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門漾橙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人楞卡,你說(shuō)我怎么就攤上這事霜运。” “怎么了蒋腮?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵淘捡,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我池摧,道長(zhǎng)焦除,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任作彤,我火速辦了婚禮膘魄,結(jié)果婚禮上乌逐,老公的妹妹穿的比我還像新娘。我一直安慰自己瓣距,他們只是感情好黔帕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蹈丸,像睡著了一般成黄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上逻杖,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天奋岁,我揣著相機(jī)與錄音,去河邊找鬼荸百。 笑死闻伶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的够话。 我是一名探鬼主播蓝翰,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼女嘲!你這毒婦竟也來(lái)了畜份?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤欣尼,失蹤者是張志新(化名)和其女友劉穎爆雹,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體愕鼓,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡钙态,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了菇晃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片册倒。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖磺送,靈堂內(nèi)的尸體忽然破棺而出剩失,到底是詐尸還是另有隱情,我是刑警寧澤册着,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站脾歧,受9級(jí)特大地震影響甲捏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鞭执,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一司顿、第九天 我趴在偏房一處隱蔽的房頂上張望芒粹。 院中可真熱鬧,春花似錦大溜、人聲如沸化漆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)座云。三九已至,卻和暖如春付材,著一層夾襖步出監(jiān)牢的瞬間朦拖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工厌衔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留璧帝,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓富寿,卻偏偏與公主長(zhǎng)得像睬隶,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子页徐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容