9292import com .facebook .react .modules .core .DeviceEventManagerModule ;
9393import com .facebook .react .modules .core .ReactChoreographer ;
9494import com .facebook .react .modules .debug .interfaces .DeveloperSettings ;
95- import com .facebook .react .modules .fabric .ReactFabric ;
9695import com .facebook .react .packagerconnection .RequestHandler ;
9796import com .facebook .react .surface .ReactStage ;
9897import com .facebook .react .turbomodule .core .TurboModuleManager ;
102101import com .facebook .react .uimanager .ReactRoot ;
103102import com .facebook .react .uimanager .UIManagerHelper ;
104103import com .facebook .react .uimanager .ViewManager ;
104+ import com .facebook .react .uimanager .common .UIManagerType ;
105105import com .facebook .react .views .imagehelper .ResourceDrawableIdHelper ;
106106import com .facebook .soloader .SoLoader ;
107107import com .facebook .systrace .Systrace ;
@@ -855,7 +855,10 @@ private void clearReactRoot(ReactRoot reactRoot) {
855855 * with the provided reactRoot reactRoot will be started asynchronously, i.e this method won't
856856 * block. This reactRoot will then be tracked by this manager and in case of catalyst instance
857857 * restart it will be re-attached.
858+ *
859+ * @deprecated This method should be internal to ReactRootView and ReactInstanceManager
858860 */
861+ @ Deprecated
859862 @ ThreadConfined (UI )
860863 public void attachRootView (ReactRoot reactRoot ) {
861864 UiThreadUtil .assertOnUiThread ();
@@ -865,35 +868,37 @@ public void attachRootView(ReactRoot reactRoot) {
865868 // Ideally reactRoot should be initialized with id == NO_ID
866869 if (mAttachedReactRoots .add (reactRoot )) {
867870 clearReactRoot (reactRoot );
871+ } else {
872+ FLog .e (ReactConstants .TAG , "ReactRoot was attached multiple times" );
868873 }
869874
870875 // If react context is being created in the background, JS application will be started
871876 // automatically when creation completes, as reactRoot is part of the attached
872877 // reactRoot list.
873878 ReactContext currentContext = getCurrentReactContext ();
874879 if (mCreateReactContextThread == null && currentContext != null ) {
875- if (reactRoot .getState ().compareAndSet (ReactRoot .STATE_STOPPED , ReactRoot .STATE_STARTED )) {
876- attachRootViewToInstance (reactRoot );
877- }
880+ attachRootViewToInstance (reactRoot );
878881 }
879882 }
880883
881884 /**
882885 * Detach given {@param reactRoot} from current catalyst instance. It's safe to call this method
883886 * multiple times on the same {@param reactRoot} - in that case view will be detached with the
884887 * first call.
888+ *
889+ * @deprecated This method should be internal to ReactRootView and ReactInstanceManager
885890 */
891+ @ Deprecated
886892 @ ThreadConfined (UI )
887893 public void detachRootView (ReactRoot reactRoot ) {
888894 UiThreadUtil .assertOnUiThread ();
889- synchronized (mAttachedReactRoots ) {
890- if (mAttachedReactRoots .contains (reactRoot )) {
891- ReactContext currentContext = getCurrentReactContext ();
892- mAttachedReactRoots .remove (reactRoot );
893- if (currentContext != null && currentContext .hasActiveReactInstance ()) {
894- detachViewFromInstance (reactRoot , currentContext .getCatalystInstance ());
895- }
896- }
895+ if (!mAttachedReactRoots .remove (reactRoot )) {
896+ return ;
897+ }
898+
899+ ReactContext reactContext = mCurrentReactContext ;
900+ if (reactContext != null && reactContext .hasActiveReactInstance ()) {
901+ detachRootViewFromInstance (reactRoot , reactContext );
897902 }
898903 }
899904
@@ -1150,9 +1155,7 @@ private void setupReactContext(final ReactApplicationContext reactContext) {
11501155
11511156 ReactMarker .logMarker (ATTACH_MEASURED_ROOT_VIEWS_START );
11521157 for (ReactRoot reactRoot : mAttachedReactRoots ) {
1153- if (reactRoot .getState ().compareAndSet (ReactRoot .STATE_STOPPED , ReactRoot .STATE_STARTED )) {
1154- attachRootViewToInstance (reactRoot );
1155- }
1158+ attachRootViewToInstance (reactRoot );
11561159 }
11571160 ReactMarker .logMarker (ATTACH_MEASURED_ROOT_VIEWS_END );
11581161 }
@@ -1194,6 +1197,11 @@ private void setupReactContext(final ReactApplicationContext reactContext) {
11941197
11951198 private void attachRootViewToInstance (final ReactRoot reactRoot ) {
11961199 FLog .d (ReactConstants .TAG , "ReactInstanceManager.attachRootViewToInstance()" );
1200+ if (!reactRoot .getState ().compareAndSet (ReactRoot .STATE_STOPPED , ReactRoot .STATE_STARTED )) {
1201+ // Already started
1202+ return ;
1203+ }
1204+
11971205 Systrace .beginSection (TRACE_TAG_REACT_JAVA_BRIDGE , "attachRootViewToInstance" );
11981206
11991207 @ Nullable
@@ -1210,7 +1218,6 @@ private void attachRootViewToInstance(final ReactRoot reactRoot) {
12101218 @ Nullable Bundle initialProperties = reactRoot .getAppProperties ();
12111219
12121220 final int rootTag ;
1213-
12141221 if (reactRoot .getUIManagerType () == FABRIC ) {
12151222 rootTag =
12161223 uiManager .startSurface (
@@ -1245,18 +1252,48 @@ private void attachRootViewToInstance(final ReactRoot reactRoot) {
12451252 Systrace .endSection (TRACE_TAG_REACT_JAVA_BRIDGE );
12461253 }
12471254
1248- private void detachViewFromInstance (ReactRoot reactRoot , CatalystInstance catalystInstance ) {
1249- FLog .d (ReactConstants .TAG , "ReactInstanceManager.detachViewFromInstance ()" );
1255+ private void detachRootViewFromInstance (ReactRoot reactRoot , ReactContext reactContext ) {
1256+ FLog .d (ReactConstants .TAG , "ReactInstanceManager.detachRootViewFromInstance ()" );
12501257 UiThreadUtil .assertOnUiThread ();
1251- if (reactRoot .getUIManagerType () == FABRIC ) {
1252- catalystInstance
1253- .getJSModule (ReactFabric .class )
1254- .unmountComponentAtNode (reactRoot .getRootViewTag ());
1258+
1259+ if (!reactRoot .getState ().compareAndSet (ReactRoot .STATE_STARTED , ReactRoot .STATE_STOPPED )) {
1260+ // ReactRoot was already stopped
1261+ return ;
1262+ }
1263+
1264+ @ UIManagerType int uiManagerType = reactRoot .getUIManagerType ();
1265+ if (uiManagerType == UIManagerType .FABRIC ) {
1266+ // Stop surface in Fabric.
1267+ // Calling FabricUIManager.stopSurface causes the C++ Binding.stopSurface
1268+ // to be called synchronously over the JNI, which causes an empty tree
1269+ // to be committed via the Scheduler, which will cause mounting instructions
1270+ // to be queued up and synchronously executed to delete and remove
1271+ // all the views in the hierarchy.
1272+ final int surfaceId = reactRoot .getRootViewTag ();
1273+ if (surfaceId != View .NO_ID ) {
1274+ UIManager uiManager = UIManagerHelper .getUIManager (reactContext , uiManagerType );
1275+ if (uiManager != null ) {
1276+ uiManager .stopSurface (surfaceId );
1277+ } else {
1278+ FLog .w (ReactConstants .TAG , "Failed to stop surface, UIManager has already gone away" );
1279+ reactRoot .getRootViewGroup ().removeAllViews ();
1280+ }
1281+ } else {
1282+ ReactSoftExceptionLogger .logSoftException (
1283+ TAG ,
1284+ new RuntimeException (
1285+ "detachRootViewFromInstance called with ReactRootView with invalid id" ));
1286+ reactRoot .getRootViewGroup ().removeAllViews ();
1287+ }
12551288 } else {
1256- catalystInstance
1289+ reactContext
1290+ .getCatalystInstance ()
12571291 .getJSModule (AppRegistry .class )
12581292 .unmountApplicationComponentAtRootTag (reactRoot .getRootViewTag ());
12591293 }
1294+
1295+ // The view is no longer attached, so mark it as such by resetting its ID.
1296+ reactRoot .getRootViewGroup ().setId (View .NO_ID );
12601297 }
12611298
12621299 @ ThreadConfined (UI )
@@ -1269,7 +1306,11 @@ private void tearDownReactContext(ReactContext reactContext) {
12691306
12701307 synchronized (mAttachedReactRoots ) {
12711308 for (ReactRoot reactRoot : mAttachedReactRoots ) {
1272- clearReactRoot (reactRoot );
1309+ if (ReactFeatureFlags .unmountApplicationOnInstanceDetach ) {
1310+ detachRootViewFromInstance (reactRoot , reactContext );
1311+ } else {
1312+ clearReactRoot (reactRoot );
1313+ }
12731314 }
12741315 }
12751316
0 commit comments