react-native源碼——啟動

rn應(yīng)用啟動時询微,利用RCTBundleURLProvider獲取到j(luò)s端入口文件index.ios.js的url地址,再生成程序根視圖RCTRootView撑毛。RCTRootView的創(chuàng)建方法

- (instancetype)initWithBundleURL:(NSURL *)bundleURL
                       moduleName:(NSString *)moduleName
                initialProperties:(NSDictionary *)initialProperties
                    launchOptions:(NSDictionary *)launchOptions
{
  RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:bundleURL
                                            moduleProvider:nil
                                             launchOptions:launchOptions];
  return [self initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties];
}

生成一個bridge书聚,在調(diào)用initWithBridge方法,在這個方法中注冊一系列通知藻雌,用來監(jiān)聽js文件的加載過程雌续,在文件加載完成后刷新視圖等。RCTRootView內(nèi)部有屬性強引用bridge對象胯杭。

bridge的創(chuàng)建方法主要調(diào)用的是如下

- (void)setUp
{
  ... 省略
  _bundleURL = [RCTConvert NSURL:_bundleURL.absoluteString];
  self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self];
  [self.batchedBridge start];
}

bridge沒做啥實事驯杜,主要作用就是生成了RCTBatchedBridge對象,這是RCTBridge的一個子類做个,并強引用了它鸽心。
具體干活的對象是RCTBatchedBridge⊥缙担看它的start方法藤肢。

- (void)start
{
  [[NSNotificationCenter defaultCenter]
    postNotificationName:RCTJavaScriptWillStartLoadingNotification
    object:_parentBridge userInfo:@{@"bridge": self}];
  RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBatchedBridge setUp]", nil);
  dispatch_queue_t bridgeQueue = dispatch_queue_create("com.facebook.react.RCTBridgeQueue", DISPATCH_QUEUE_CONCURRENT);

  ---------- 標(biāo)記@1
  dispatch_group_t initModulesAndLoadSource = dispatch_group_create();
  dispatch_group_enter(initModulesAndLoadSource);
  __weak RCTBatchedBridge *weakSelf = self;
  __block NSData *sourceCode;
    ---------- 標(biāo)記@2
  [self loadSource:^(NSError *error, NSData *source, __unused int64_t sourceLength) {
    if (error) {
      RCTLogWarn(@"Failed to load source: %@", error);
      dispatch_async(dispatch_get_main_queue(), ^{
        [weakSelf stopLoadingWithError:error];
      });
    }
    sourceCode = source;
    dispatch_group_leave(initModulesAndLoadSource);
  } onProgress:^(RCTLoadingProgress *progressData) {
#if RCT_DEV && __has_include("RCTDevLoadingView.h")
    RCTDevLoadingView *loadingView = [weakSelf moduleForClass:[RCTDevLoadingView class]];
    [loadingView updateProgress:progressData];
#endif
  }];

   ---------- 標(biāo)記@3
  [self initModulesWithDispatchGroup:initModulesAndLoadSource];

  RCTPerformanceLogger *performanceLogger = self->_performanceLogger;
  __block NSString *config;
  dispatch_group_enter(initModulesAndLoadSource);
  dispatch_async(bridgeQueue, ^{
    
     ---------- 標(biāo)記@4
    dispatch_group_t setupJSExecutorAndModuleConfig = dispatch_group_create();

     ---------- 標(biāo)記@5
    dispatch_group_async(setupJSExecutorAndModuleConfig, bridgeQueue, ^{
      [performanceLogger markStartForTag:RCTPLJSCExecutorSetup];
      [weakSelf setUpExecutor];
      [performanceLogger markStopForTag:RCTPLJSCExecutorSetup];
    });

    ---------- 標(biāo)記@6
    dispatch_group_async(setupJSExecutorAndModuleConfig, bridgeQueue, ^{
      if (weakSelf.valid) {
        RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBatchedBridge moduleConfig", nil);
        [performanceLogger markStartForTag:RCTPLNativeModulePrepareConfig];
        config = [weakSelf moduleConfig];
        [performanceLogger markStopForTag:RCTPLNativeModulePrepareConfig];
        RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
      }
    });
     ---------- 標(biāo)記@7
    dispatch_group_notify(setupJSExecutorAndModuleConfig, bridgeQueue, ^{
      [performanceLogger markStartForTag:RCTPLNativeModuleInjectConfig];
      [weakSelf injectJSONConfiguration:config onComplete:^(NSError *error) {
        [performanceLogger markStopForTag:RCTPLNativeModuleInjectConfig];
        if (error) {
          RCTLogWarn(@"Failed to inject config: %@", error);
          dispatch_async(dispatch_get_main_queue(), ^{
            [weakSelf stopLoadingWithError:error];
          });
        }
      }];
      dispatch_group_leave(initModulesAndLoadSource);
    });
  });
   ---------- 標(biāo)記@8
  dispatch_group_notify(initModulesAndLoadSource, bridgeQueue, ^{
    RCTBatchedBridge *strongSelf = weakSelf;
    if (sourceCode && strongSelf.loading) {
      [strongSelf executeSourceCode:sourceCode];
    }
  });
  RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
}

@1: 創(chuàng)建外層的隊列組A
@2: 隊列組A中的第一個任務(wù),加載我們寫的那些js源碼文件
@3: 同步的形式糯景,將OC中通過宏RCT_EXPORT_MODULE導(dǎo)出的所有模塊信息嘁圈,例如模塊名字、模塊對應(yīng)的RCTModuleData實例等添加到屬性_moduleDataByID等之中蟀淮。同時初始化所有不能懶加載的模塊實例最住。
@4: 創(chuàng)建隊列組B,該隊列組用于A的一個子任務(wù)之中灭贷。意思就是隊列組A有一個任務(wù)温学,這個任務(wù)內(nèi)部又創(chuàng)建了一個隊列組B,B隊列組又包含了兩個任務(wù)甚疟,分別是@5和@6仗岖。
@5: 隊列組B的子任務(wù),初始化js執(zhí)行器_javaScriptExecutor览妖。
@6: 將@3已經(jīng)裝載的模塊信息轧拄,按照一定格式組裝結(jié)構(gòu)。
@7: @5讽膏、@6完成檩电,將模塊信息注入js執(zhí)行上下文中,這樣js代碼就可以調(diào)用了府树。
@8: @5俐末、@6完成后,未等@7完成奄侠,隊列組即已經(jīng)退出卓箫,執(zhí)行@8操作。執(zhí)行上面加載的js代碼垄潮,通知RootView頁面刷新等烹卒。

細(xì)究部分:
@3: initModulesWithDispatchGroup方法的實現(xiàn)

- (void)initModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
{
  ... 抽取主要代碼
  for (Class moduleClass in RCTGetModuleClasses()) {
    NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);
    RCTModuleData *moduleData = moduleDataByName[moduleName];
    moduleData = [[RCTModuleData alloc] initWithModuleClass:moduleClass
                                                     bridge:self];
    moduleDataByName[moduleName] = moduleData;
    [moduleClassesByID addObject:moduleClass];
    [moduleDataByID addObject:moduleData];
  }
  _moduleDataByID = [moduleDataByID copy];
  _moduleDataByName = [moduleDataByName copy];
  _moduleClassesByID = [moduleClassesByID copy];

  [self prepareModulesWithDispatchGroup:dispatchGroup];
}

方法RCTGetModuleClasses()拿到所有OC中通過RCT_EXPORT_MODULE(js_name)導(dǎo)出的模塊名字(若宏后面沒寫名字,則默認(rèn)為文件名字)弯洗,遍歷旅急,包裝成RCTModuleData類型的數(shù)據(jù),存到RCTBatchedBridge的三個屬性_moduleDataByID牡整、_moduleDataByName藐吮、_moduleClassesByID之中。
@3內(nèi)部方法prepareModulesWithDispatchGroup實現(xiàn)

- (void)prepareModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
{
  ... 抽取主要代碼
  for (RCTModuleData *moduleData in _moduleDataByID) {
    if (moduleData.requiresMainQueueSetup || moduleData.hasConstantsToExport) {
      dispatch_block_t block = ^{
        if (self.valid) {
          [self->_performanceLogger appendStartForTag:RCTPLNativeModuleMainThread];
          (void)[moduleData instance];             --------- 標(biāo)記p
          [moduleData gatherConstants];            --------- 標(biāo)記q
          [self->_performanceLogger appendStopForTag:RCTPLNativeModuleMainThread];
        }
      };
      dispatch_group_async(dispatchGroup, dispatch_get_main_queue(), block);
}}

意思是找出所有模塊中需要預(yù)先實例化的模塊,初始化它炎码。具體是如果當(dāng)前模塊實現(xiàn)了init或者constantsToExport方法盟迟,則上面屬性requiresMainQueueSetup和hasConstantsToExport屬性會為YES秋泳,進(jìn)入標(biāo)記p和標(biāo)記q處潦闲,實例化模塊。
一般模塊采用的是懶加載模式迫皱,當(dāng)js中導(dǎo)入時才加載歉闰,但是上面的這兩種情況需要在程序啟動時就加載,文檔意思說是防止死鎖卓起,但我沒想到應(yīng)用場景和敬,自己項目中注釋掉p和q,也是能正常工作的戏阅。這樣發(fā)現(xiàn)的問題就是一些系統(tǒng)自己使用的模塊沒有初始化而無法工作昼弟,例如RCTDevLoadingView。

2020.7.23新增:0.61版本
1奕筐、初始化js線程舱痘,啟動runloop
2、加載jsbundle源碼
3离赫、宏導(dǎo)出的模塊在runtime階段調(diào)用的+load方法中將所有模塊加載進(jìn)了一個數(shù)組芭逝。在這里遍歷數(shù)組,初始化模塊渊胸,生成配置表
4旬盯、創(chuàng)建js引擎_javaScriptExecutor
5、將配置表注入到j(luò)s引擎翎猛,雙端一致
模塊內(nèi)部導(dǎo)出的方法胖翰,那個宏定義是多生成了一個特殊前綴的+號方法,在調(diào)用模塊方法時懶加載模塊的所有導(dǎo)出方法(利用runtime查找模塊的所有+號方法切厘,抽出特殊前綴的那些方法的集合)萨咳。例如導(dǎo)出了一個test方法,實際上會生成-test和+__rct_export__test這樣的兩個方法迂卢,前者是為了在該方法中還可以調(diào)用類中的其他方法某弦,后者是專門為了標(biāo)示,方便runtime時查找的而克,+號方法而不是-號方法靶壮,猜測是一般類中+號方法較少,運行時查找更方便员萍,也可以減少方法同名沖突腾降。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市碎绎,隨后出現(xiàn)的幾起案子螃壤,更是在濱河造成了極大的恐慌抗果,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奸晴,死亡現(xiàn)場離奇詭異冤馏,居然都是意外死亡,警方通過查閱死者的電腦和手機寄啼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門逮光,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人墩划,你說我怎么就攤上這事涕刚。” “怎么了乙帮?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵杜漠,是天一觀的道長。 經(jīng)常有香客問我察净,道長驾茴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任塞绿,我火速辦了婚禮沟涨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘异吻。我一直安慰自己裹赴,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布诀浪。 她就那樣靜靜地躺著棋返,像睡著了一般。 火紅的嫁衣襯著肌膚如雪雷猪。 梳的紋絲不亂的頭發(fā)上睛竣,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天,我揣著相機與錄音求摇,去河邊找鬼射沟。 笑死,一個胖子當(dāng)著我的面吹牛与境,可吹牛的內(nèi)容都是我干的验夯。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼摔刁,長吁一口氣:“原來是場噩夢啊……” “哼挥转!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤绑谣,失蹤者是張志新(化名)和其女友劉穎党窜,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體借宵,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡幌衣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了暇务。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片泼掠。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡怔软,死狀恐怖垦细,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情挡逼,我是刑警寧澤括改,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站家坎,受9級特大地震影響嘱能,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜虱疏,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一惹骂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧做瞪,春花似錦对粪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至牍帚,卻和暖如春儡遮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背暗赶。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工鄙币, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蹂随。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓十嘿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親糙及。 傳聞我的和親對象是個殘疾皇子详幽,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,486評論 2 348

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,754評論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器唇聘,智...
    卡卡羅2017閱讀 134,629評論 18 139
  • ? React Native(以下簡稱RN)的目標(biāo)是用基于react的JavaScript寫代碼版姑,在iOS/A...
    Iceguest閱讀 3,548評論 0 10
  • 今早出門快走到公交站時,看見一只小泰迪被一位步履蹣跚的老人牽著迟郎,老人自顧自地拽著繩子順著馬路牙子走剥险,但是小狗并沒有...
    攝小影閱讀 316評論 1 1
  • 本周我們就輕松一下,看一個小動畫宪肖,大家猜猜是如何實現(xiàn)的表制?
    南瓜土豆餅閱讀 483評論 0 2