首先来讲一下,我们必须清楚React Native本质是使用React的思想
只不过React渲染的是真实的Dom,而React Native是使用虚拟Dom通过桥接调用原生的渲染
下面就从index.js的
AppRegistry.registerComponent到AppRegistry.runApplication来分析下程序运行的原理
几个核心概念
1、AppRegistry
RN程序入口,相当于main函数
2、RCTRootView
1、创建RCTBridge
2、初始化视图 ,监听RCTJavaScriptDidLoadNotification通知,执行AppRegistry.runApplication
3、RCTBridge(主要是其父类RCTCxxBridge在操作)
使用了多线程
4、ModuleRegistry
模块注册,js通过他查找资源
5、GCD
1、创建RCTBridge(NativeToJSBridge JSToNativeBridge ModuleRegistry),
2、加载js
3、(group_notify)前两者完成后发出资源加载完成通知RCTJavaScriptDidLoadNotification)
6、RCTJavaScriptDidLoadNotification
资源加载完成,桥接创建好之后发送执行程序通知(AppRegistry.runApplication)
备注:
1、创建了两条线程
js线程 执行js 创建bridge
jsmessage线程 js原生通信 NativeToJSBridge JSToNativeBridge
2、模块注册
ModuleRegistry
3、多线程
dispatch_group
4、通知
执行程序通知,目的是调用AppRegistry.runApplication
大致流程图
React Native
index.js
AppRegistry.registerComponent(appName, () => App);
AppRegistry 是运行所有 React Native 应用程序的 JS 入口点。
应用程序根组件需要通过 AppRegistry.registerComponent 来注册它们自身,
然后本地系统就可以加载应用程序的包,
准备就绪后会自动调用AppRegistry.runApplication就可以真正的运行该应用程序了。
* `AppRegistry` is the JS entry point to running all React Native apps. App
* root components should register themselves with
* `AppRegistry.registerComponent`, then the native system can load the bundle
* for the app and then actually run the app when it's ready by invoking
* `AppRegistry.runApplication`.
iOS端
RCTRootView *rootView =
[[RCTRootView alloc] initWithBundleURL: jsCodeLocation
moduleName: sBundleView
initialProperties:
@{
@"initapp" : structjson
}
launchOptions: nil];
self.view = rootView;
React Native库
RCTRootView
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties
launchOptions:(NSDictionary *)launchOptions
{
// 创建RCTBridge 内部通过父类RCTCxxBridge(C++)创建
// NativeToJSBridge JSToNativeBridge ModuleRegistry 加载执行js资源都在该方法完成
// 会发出js资源加载完成的通知RCTJavaScriptDidLoadNotification
RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:bundleURL
moduleProvider:nil
launchOptions:launchOptions];
// 初始化视图
// 最终在这个方法中调用runApplication,即index.js中的自动调用AppRegistry.runApplication
// 本质是监听资源加载完成的通知,此通知发出时说明一切都已准备好
// 会监听RCTJavaScriptDidLoadNotification通知,然后调用runApplication,即AppRegistry.runApplication()
return [self initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties];
}
//初始化视图
- (instancetype)initWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties
{
RCTAssertMainQueue();
RCTAssert(bridge, @"A bridge instance is required to create an RCTRootView");
RCTAssert(moduleName, @"A moduleName is required to create an RCTRootView");
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTRootView init]", nil);
if (!bridge.isLoading) {
[bridge.performanceLogger markStartForTag:RCTPLTTI];
}
if (self = [super initWithFrame:CGRectZero]) {
self.backgroundColor = [UIColor whiteColor];
_bridge = bridge;
_moduleName = moduleName;
_appProperties = [initialProperties copy];
_loadingViewFadeDelay = 0.25;
_loadingViewFadeDuration = 0.25;
_sizeFlexibility = RCTRootViewSizeFlexibilityNone;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(bridgeDidReload)
name:RCTJavaScriptWillStartLoadingNotification
object:_bridge];
// 监听资源加载完成通知RCTJavaScriptDidLoadNotification 调用javaScriptDidLoad方法
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(javaScriptDidLoad:)
name:RCTJavaScriptDidLoadNotification
object:_bridge];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(hideLoadingView)
name:RCTContentDidAppearNotification
object:self];
#if TARGET_OS_TV
self.tvRemoteHandler = [RCTTVRemoteHandler new];
for (NSString *key in [self.tvRemoteHandler.tvRemoteGestureRecognizers allKeys]) {
[self addGestureRecognizer:self.tvRemoteHandler.tvRemoteGestureRecognizers[key]];
}
#endif
[self showLoadingView];
// Immediately schedule the application to be started.
// (Sometimes actual `_bridge` is already batched bridge here.)
[self bundleFinishedLoading:([_bridge batchedBridge] ?: _bridge)];
}
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
return self;
}
- (void)javaScriptDidLoad:(NSNotification *)notification
{
RCTAssertMainQueue();
// Use the (batched) bridge that's sent in the notification payload, so the
// RCTRootContentView is scoped to the right bridge
RCTBridge *bridge = notification.userInfo[@"bridge"];
if (bridge != _contentView.bridge) {
[self bundleFinishedLoading:bridge];
}
}
- (void)bundleFinishedLoading:(RCTBridge *)bridge
{
RCTAssert(bridge != nil, @"Bridge cannot be nil");
if (!bridge.valid) {
return;
}
[_contentView removeFromSuperview];
_contentView = [[RCTRootContentView alloc] initWithFrame:self.bounds
bridge:bridge
reactTag:self.reactTag
sizeFlexiblity:_sizeFlexibility];
// 运行程序
[self runApplication:bridge];
_contentView.passThroughTouches = _passThroughTouches;
[self insertSubview:_contentView atIndex:0];
if (_sizeFlexibility == RCTRootViewSizeFlexibilityNone) {
self.intrinsicContentSize = self.bounds.size;
}
}
//运行程序 AppRegistry.runApplication()
- (void)runApplication:(RCTBridge *)bridge
{
NSString *moduleName = _moduleName ?: @"";
NSDictionary *appParameters = @{
@"rootTag": _contentView.reactTag,
@"initialProps": _appProperties ?: @{},
};
RCTLogInfo(@"Running application %@ (%@)", moduleName, appParameters);
[bridge enqueueJSCall:@"AppRegistry"
method:@"runApplication"
args:@[moduleName, appParameters]
completion:NULL];
}
RCTBridge
//1
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
moduleProvider:(RCTBridgeModuleListProvider)block
launchOptions:(NSDictionary *)launchOptions
{
return [self initWithDelegate:nil
bundleURL:bundleURL
moduleProvider:block
launchOptions:launchOptions];
}
//2
- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)delegate
bundleURL:(NSURL *)bundleURL
moduleProvider:(RCTBridgeModuleListProvider)block
launchOptions:(NSDictionary *)launchOptions
{
//
if (self = [super init]) {
_delegate = delegate;
_bundleURL = bundleURL;
_moduleProvider = block;
_launchOptions = [launchOptions copy];
//
[self setUp];
}
return self;
}
- (void)setUp
{
RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBridge setUp]", nil);
_performanceLogger = [RCTPerformanceLogger new];
[_performanceLogger markStartForTag:RCTPLBridgeStartup];
[_performanceLogger markStartForTag:RCTPLTTI];
Class bridgeClass = self.bridgeClass;
#if RCT_DEV
RCTExecuteOnMainQueue(^{
RCTRegisterReloadCommandListener(self);
});
#endif
// Only update bundleURL from delegate if delegate bundleURL has changed
NSURL *previousDelegateURL = _delegateBundleURL;
_delegateBundleURL = [self.delegate sourceURLForBridge:self];
if (_delegateBundleURL && ![_delegateBundleURL isEqual:previousDelegateURL]) {
_bundleURL = _delegateBundleURL;
}
// Sanitize the bundle URL
_bundleURL = [RCTConvert NSURL:_bundleURL.absoluteString];
// 弱引用父类 RCTCxxBridge
self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self];
// RCTCxxBridge
[self.batchedBridge start];
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
}
RCTCxxBridge
- (void)start
{
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTCxxBridge start]", nil);
[[NSNotificationCenter defaultCenter]
postNotificationName:RCTJavaScriptWillStartLoadingNotification
object:_parentBridge userInfo:@{@"bridge": self}];
// 创建js线程
// Set up the JS thread early
_jsThread = [[NSThread alloc] initWithTarget:[self class]
selector:@selector(runRunLoop)
object:nil];
_jsThread.name = RCTJSThreadName;
_jsThread.qualityOfService = NSOperationQualityOfServiceUserInteractive;
#if RCT_DEBUG
_jsThread.stackSize *= 2;
#endif
[_jsThread start];
// 多线程 dispatch_group 创建队列组prepareBridge
// 1、初始化bridge ensureOnJavaScriptThread
// 2、加载资源 loadSource
dispatch_group_t prepareBridge = dispatch_group_create();
[_performanceLogger markStartForTag:RCTPLNativeModuleInit];
// 注册module 包括我们原生导出的模块RCTBridgeModule协议 RCT_EXTERN宏导出的
[self registerExtraModules];
// Initialize all native modules that cannot be loaded lazily
(void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
[self registerExtraLazyModules];
[_performanceLogger markStopForTag:RCTPLNativeModuleInit];
// This doesn't really do anything. The real work happens in initializeBridge.
// react实例对象
_reactInstance.reset(new Instance);
__weak RCTCxxBridge *weakSelf = self;
// Prepare executor factory (shared_ptr for copy into block)
// 创建执行js的工厂方法 指定js引擎
std::shared_ptr<JSExecutorFactory> executorFactory;
if (!self.executorClass) {
if ([self.delegate conformsToProtocol:@protocol(RCTCxxBridgeDelegate)]) {
id<RCTCxxBridgeDelegate> cxxDelegate = (id<RCTCxxBridgeDelegate>) self.delegate;
executorFactory = [cxxDelegate jsExecutorFactoryForBridge:self];
}
if (!executorFactory) {
executorFactory = std::make_shared<JSCExecutorFactory>(nullptr);
}
} else {
id<RCTJavaScriptExecutor> objcExecutor = [self moduleForClass:self.executorClass];
executorFactory.reset(new RCTObjcExecutorFactory(objcExecutor, ^(NSError *error) {
if (error) {
[weakSelf handleError:error];
}
}));
}
// Dispatch the instance initialization as soon as the initial module metadata has
// been collected (see initModules)
// 初始化bridge
dispatch_group_enter(prepareBridge);
[self ensureOnJavaScriptThread:^{
[weakSelf _initializeBridge:executorFactory];
dispatch_group_leave(prepareBridge);
}];
// Load the source asynchronously, then store it for later execution.
// 加载资源 RCTJavaScriptLoader加载器
dispatch_group_enter(prepareBridge);
__block NSData *sourceCode;
[self loadSource:^(NSError *error, RCTSource *source) {
if (error) {
[weakSelf handleError:error];
}
sourceCode = source.data;
dispatch_group_leave(prepareBridge);
} onProgress:^(RCTLoadingProgress *progressData) {
#if RCT_DEV && __has_include("RCTDevLoadingView.h")
// Note: RCTDevLoadingView should have been loaded at this point, so no need to allow lazy loading.
RCTDevLoadingView *loadingView = [weakSelf moduleForName:RCTBridgeModuleNameForClass([RCTDevLoadingView class])
lazilyLoadIfNecessary:NO];
[loadingView updateProgress:progressData];
#endif
}];
// Wait for both the modules and source code to have finished loading
// 多线程结束 bridge创建完成 资源加载完成
dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
RCTCxxBridge *strongSelf = weakSelf;
if (sourceCode && strongSelf.loading) {
// 执行代码
[strongSelf executeSourceCode:sourceCode sync:NO];
}
});
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
}
//初始化Bridage 在之前创建的js线程
- (void)_initializeBridge:(std::shared_ptr<JSExecutorFactory>)executorFactory
{
if (!self.valid) {
return;
}
RCTAssertJSThread();
__weak RCTCxxBridge *weakSelf = self;
// jsmessage js通信线程 用于跟js通讯 NativeToJSBridge JSToNativeBridge都在这个线程
_jsMessageThread = std::make_shared<RCTMessageThread>([NSRunLoop currentRunLoop], ^(NSError *error) {
if (error) {
[weakSelf handleError:error];
}
});
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTCxxBridge initializeBridge:]", nil);
// This can only be false if the bridge was invalidated before startup completed
if (_reactInstance) {
#if RCT_DEV
executorFactory = std::make_shared<GetDescAdapter>(self, executorFactory);
#endif
// 初始化Bridage核心方法 NativeToJSBridge JSToNativeBridge ModuleRegistry都在该方法完成
[self _initializeBridgeLocked:executorFactory];
#if RCT_PROFILE
if (RCTProfileIsProfiling()) {
_reactInstance->setGlobalVariable(
"__RCTProfileIsProfiling",
std::make_unique<JSBigStdString>("true"));
}
#endif
}
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
}
//jsMessageThread NativeToJSBridge JSToNativeBridge ModuleRegistry
- (void)_initializeBridgeLocked:(std::shared_ptr<JSExecutorFactory>)executorFactory
{
std::lock_guard<std::mutex> guard(_moduleRegistryLock);
// This is async, but any calls into JS are blocked by the m_syncReady CV in Instance
// initializeBridge是在之前创建的线程执行的
// 创建ModuleRegistry(映射表,原生模块要映射到js,可以理解为对应表) 并返回给_reactInstance
// _reactInstance利用ModuleRegistry,_jsMessageThread
// 创建nativeToJSBridge对象(后续原生向js发送消息主要靠他),
// NativeToJSBridge初始化的时候顺便把JSToNativeBridge也初始化了(后学js向原生发消息主要靠他),
// 至此NativeToJSBridge JSToNativeBridge 通信(在_jsMessageThread线程)渠道打通
// 通信、映射表全部给了_reactInstance 至此初始化bridge完成
_reactInstance->initializeBridge(
std::make_unique<RCTInstanceCallback>(self),
executorFactory,
_jsMessageThread,
[self _buildModuleRegistryUnlocked]);
_moduleRegistryCreated = YES;
}
//回到主线程 发出RCTJavaScriptDidLoadNotification通知,
- (void)executeSourceCode:(NSData *)sourceCode sync:(BOOL)sync
{
// This will get called from whatever thread was actually executing JS.
dispatch_block_t completion = ^{
// Log start up metrics early before processing any other js calls
[self logStartupFinish];
// Flush pending calls immediately so we preserve ordering
[self _flushPendingCalls];
// Perform the state update and notification on the main thread, so we can't run into
// timing issues with RCTRootView
dispatch_async(dispatch_get_main_queue(), ^{
// 发出RCTJavaScriptDidLoadNotification
[[NSNotificationCenter defaultCenter]
postNotificationName:RCTJavaScriptDidLoadNotification
object:self->_parentBridge userInfo:@{@"bridge": self}];
// Starting the display link is not critical to startup, so do it last
[self ensureOnJavaScriptThread:^{
// Register the display link to start sending js calls after everything is setup
[self->_displayLink addToRunLoop:[NSRunLoop currentRunLoop]];
}];
});
};
if (sync) {
[self executeApplicationScriptSync:sourceCode url:self.bundleURL];
completion();
} else {
[self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:completion];
}
#if RCT_DEV
if (self.devSettings.isHotLoadingAvailable && self.devSettings.isHotLoadingEnabled) {
NSString *path = [self.bundleURL.path substringFromIndex:1]; // strip initial slash
NSString *host = self.bundleURL.host;
NSNumber *port = self.bundleURL.port;
[self enqueueJSCall:@"HMRClient"
method:@"enable"
args:@[@"ios", path, host, RCTNullIfNil(port)]
completion:NULL]; }
#endif
}