Fabric: Polishing RCTSurfacePresenter

Summary:
Now RCTSurfacePresenter is uniquely responsible for:
* Starting and stopping JS apps;
* Restarting JS apps during hot-reload;
* Recreating Scheduler during hot-reload.

Reviewed By: mdvacca

Differential Revision: D9931318

fbshipit-source-id: a6a3fb58814222f71cc6cb2caad620ed6319089d
This commit is contained in:
Valentin Shergin 2018-09-26 10:01:42 -07:00 коммит произвёл Facebook Github Bot
Родитель 24ffd8f6fb
Коммит bce94dc8c3
3 изменённых файлов: 124 добавлений и 38 удалений

Просмотреть файл

@ -37,6 +37,9 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)registerSurface:(RCTFabricSurface *)surface;
- (void)unregisterSurface:(RCTFabricSurface *)surface;
- (void)setProps:(NSDictionary *)props
surface:(RCTFabricSurface *)surface;
- (nullable RCTFabricSurface *)surfaceForRootTag:(ReactTag)rootTag;
/**
@ -57,6 +60,9 @@ NS_ASSUME_NONNULL_BEGIN
@interface RCTSurfacePresenter (Deprecated)
@property (nonatomic) std::function<facebook::react::UIManagerInstaller> uiManagerInstaller;
@property (nonatomic) std::function<facebook::react::UIManagerUninstaller> uiManagerUninstaller;
/**
* We need to expose `uiManager` for registration
* purposes. Eventually, we will move this down to C++ side.

Просмотреть файл

@ -38,11 +38,12 @@ using namespace facebook::react;
@end
@implementation RCTSurfacePresenter {
RCTScheduler *_scheduler;
RCTScheduler *_Nullable _scheduler;
RCTMountingManager *_mountingManager;
RCTBridge *_bridge;
RCTBridge *_batchedBridge;
RCTSurfaceRegistry *_surfaceRegistry;
SharedContextContainer _contextContainer;
}
- (instancetype)initWithBridge:(RCTBridge *)bridge
@ -69,10 +70,10 @@ using namespace facebook::react;
void *imageLoader = (__bridge void *)[[RCTBridge currentBridge] imageLoader];
contextContainer->registerInstance(std::make_shared<ImageManager>(imageLoader));
_scheduler = [[RCTScheduler alloc] initWithContextContainer:contextContainer];
_scheduler.delegate = self;
_contextContainer = contextContainer;
_surfaceRegistry = [[RCTSurfaceRegistry alloc] init];
_mountingManager = [[RCTMountingManager alloc] init];
_mountingManager.delegate = self;
@ -94,18 +95,19 @@ using namespace facebook::react;
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark - RCTSchedulerDelegate
- (void)schedulerDidFinishTransaction:(facebook::react::ShadowViewMutationList)mutations
rootTag:(ReactTag)rootTag
- (void)createSchedulerIfNeeded
{
[_mountingManager performTransactionWithMutations:mutations
rootTag:rootTag];
if (_scheduler) {
return;
}
_scheduler = [[RCTScheduler alloc] initWithContextContainer:_contextContainer];
_scheduler.delegate = self;
}
- (void)schedulerDidRequestPreliminaryViewAllocationWithComponentName:(NSString *)componentName
- (void)ensureSchedulerDoesExist
{
[_mountingManager preliminaryCreateComponentViewWithName:componentName];
RCTAssert(_scheduler, @"RCTSurfacePresenter: RCTScheduler instance must be already instantiated at this point.");
}
#pragma mark - Internal Surface-dedicated Interface
@ -113,20 +115,25 @@ using namespace facebook::react;
- (void)registerSurface:(RCTFabricSurface *)surface
{
[_surfaceRegistry registerSurface:surface];
[_scheduler registerRootTag:surface.rootTag];
[self runSurface:surface];
// FIXME: mutation MUST produce instruction for root node.
[_mountingManager.componentViewRegistry dequeueComponentViewWithName:@"Root" tag:surface.rootTag];
[self startSurface:surface];
}
- (void)unregisterSurface:(RCTFabricSurface *)surface
{
[self stopSurface:surface];
[_scheduler unregisterRootTag:surface.rootTag];
[_surfaceRegistry unregisterSurface:surface];
}
- (void)setProps:(NSDictionary *)props
surface:(RCTFabricSurface *)surface
{
// This implementation is suboptimal indeed but still better than nothing for now.
[self stopSurface:surface];
[self startSurface:surface];
}
- (RCTFabricSurface *)surfaceForRootTag:(ReactTag)rootTag
{
return [_surfaceRegistry surfaceForRootTag:rootTag];
@ -136,6 +143,8 @@ using namespace facebook::react;
maximumSize:(CGSize)maximumSize
surface:(RCTFabricSurface *)surface
{
[self ensureSchedulerDoesExist];
LayoutContext layoutContext;
layoutContext.pointScaleFactor = RCTScreenScale();
LayoutConstraints layoutConstraints = {};
@ -151,6 +160,8 @@ using namespace facebook::react;
maximumSize:(CGSize)maximumSize
surface:(RCTFabricSurface *)surface
{
[self ensureSchedulerDoesExist];
LayoutContext layoutContext;
layoutContext.pointScaleFactor = RCTScreenScale();
LayoutConstraints layoutConstraints = {};
@ -162,53 +173,101 @@ using namespace facebook::react;
rootTag:surface.rootTag];
}
- (void)runSurface:(RCTFabricSurface *)surface
- (void)startSurface:(RCTFabricSurface *)surface
{
[_mountingManager.componentViewRegistry dequeueComponentViewWithName:@"Root" tag:surface.rootTag];
[self createSchedulerIfNeeded];
[_scheduler registerRootTag:surface.rootTag];
[self setMinimumSize:surface.minimumSize
maximumSize:surface.maximumSize
surface:surface];
// TODO: Move this down to Scheduler.
NSDictionary *applicationParameters = @{
@"rootTag": @(surface.rootTag),
@"initialProps": surface.properties,
};
[_batchedBridge enqueueJSCall:@"AppRegistry" method:@"runApplication" args:@[surface.moduleName, applicationParameters] completion:NULL];
[self->_batchedBridge enqueueJSCall:@"AppRegistry" method:@"runApplication" args:@[surface.moduleName, applicationParameters] completion:NULL];
}
- (void)stopSurface:(RCTFabricSurface *)surface
{
// TODO: Move this down to Scheduler.
[_batchedBridge enqueueJSCall:@"ReactFabric" method:@"unmountComponentAtNode" args:@[@(surface.rootTag)] completion:NULL];
[self ensureSchedulerDoesExist];
[_scheduler unregisterRootTag:surface.rootTag];
UIView<RCTComponentViewProtocol> *rootView = [_mountingManager.componentViewRegistry componentViewByTag:surface.rootTag];
[_mountingManager.componentViewRegistry enqueueComponentViewWithName:@"Root" tag:surface.rootTag componentView:rootView];
[surface _unsetStage:(RCTSurfaceStagePrepared | RCTSurfaceStageMounted)];
}
- (void)startAllSurfaces
{
for (RCTFabricSurface *surface in _surfaceRegistry.enumerator) {
[self startSurface:surface];
}
}
- (void)stopAllSurfaces
{
for (RCTFabricSurface *surface in _surfaceRegistry.enumerator) {
[self stopSurface:surface];
}
}
#pragma mark - RCTSchedulerDelegate
- (void)schedulerDidFinishTransaction:(facebook::react::ShadowViewMutationList)mutations
rootTag:(ReactTag)rootTag
{
RCTFabricSurface *surface = [_surfaceRegistry surfaceForRootTag:rootTag];
[surface _setStage:RCTSurfaceStagePrepared];
[_mountingManager performTransactionWithMutations:mutations
rootTag:rootTag];
}
- (void)schedulerDidRequestPreliminaryViewAllocationWithComponentName:(NSString *)componentName
{
[_mountingManager preliminaryCreateComponentViewWithName:componentName];
}
#pragma mark - RCTMountingManagerDelegate
- (void)mountingManager:(RCTMountingManager *)mountingManager willMountComponentsWithRootTag:(ReactTag)rootTag
{
RCTIsMainQueue();
// TODO: Propagate state change to Surface.
RCTAssertMainQueue();
// Does nothing.
}
- (void)mountingManager:(RCTMountingManager *)mountingManager didMountComponentsWithRootTag:(ReactTag)rootTag
{
RCTIsMainQueue();
RCTAssertMainQueue();
RCTFabricSurface *surface = [_surfaceRegistry surfaceForRootTag:rootTag];
// FIXME: Implement proper state propagation mechanism.
[surface _setStage:RCTSurfaceStageSurfaceDidInitialRendering];
[surface _setStage:RCTSurfaceStageSurfaceDidInitialLayout];
[surface _setStage:RCTSurfaceStageSurfaceDidInitialMounting];
UIView *rootComponentView = [_mountingManager.componentViewRegistry componentViewByTag:rootTag];
surface.view.rootView = (RCTSurfaceRootView *)rootComponentView;
RCTSurfaceStage stage = surface.stage;
if (stage & RCTSurfaceStagePrepared) {
// We have to progress the stage only if the preparing phase is done.
if ([surface _setStage:RCTSurfaceStageMounted]) {
UIView *rootComponentView = [_mountingManager.componentViewRegistry componentViewByTag:rootTag];
surface.view.rootView = (RCTSurfaceRootView *)rootComponentView;
}
}
}
#pragma mark - Bridge events
- (void)handleBridgeWillReloadNotification:(NSNotification *)notification
{
// TODO: Define a lifecycle contract for the pieces involved here including the scheduler, mounting manager, and
// the surface registry. For now simply recreate the scheduler on reload.
// The goal is to deallocate the Scheduler and its underlying references before the JS runtime is destroyed.
_scheduler = [[RCTScheduler alloc] init];
_scheduler.delegate = self;
[self stopAllSurfaces];
_scheduler = nil;
}
- (void)handleJavaScriptDidLoadNotification:(NSNotification *)notification
@ -216,6 +275,8 @@ using namespace facebook::react;
RCTBridge *bridge = notification.userInfo[@"bridge"];
if (bridge != _batchedBridge) {
_batchedBridge = bridge;
[self startAllSurfaces];
}
}
@ -233,6 +294,26 @@ using namespace facebook::react;
return _bridge;
}
- (void)setUiManagerInstaller:(std::function<facebook::react::UIManagerInstaller>)uiManagerInstaller
{
_contextContainer->registerInstance(uiManagerInstaller, "uimanager-installer");
}
- (std::function<facebook::react::UIManagerInstaller>)uiManagerInstaller
{
return _contextContainer->getInstance<std::function<facebook::react::UIManagerInstaller>>("uimanager-installer");
}
- (void)setUiManagerUninstaller:(std::function<facebook::react::UIManagerUninstaller>)uiManagerUninstaller
{
_contextContainer->registerInstance(uiManagerUninstaller, "uimanager-uninstaller");
}
- (std::function<facebook::react::UIManagerUninstaller>)uiManagerUninstaller
{
return _contextContainer->getInstance<std::function<facebook::react::UIManagerUninstaller>>("uimanager-uninstaller");
}
@end
@implementation RCTBridge (RCTSurfacePresenter)

Просмотреть файл

@ -162,8 +162,7 @@
_properties = [properties copy];
}
// TODO: Implement this in RCTSurfacePresenter.
// [_surfacePresenter setProps:properties surface:self];
[_surfacePresenter setProps:properties surface:self];
}
#pragma mark - Layout