Skip to content

Commit d97350d

Browse files
Andrei Shikovfacebook-github-bot
authored andcommitted
Queue mount items executed before root view is attached
Summary: Changelog: [Internal] After D27016919, some mounting instructions can be executed before the view is attached, which is invalid. This change adds additional queue for such items, which can be later dispatched after view is ready. The new queue is expected to be empty for usual rendering and used in prerendering flows only. In case of prerendering, it should only hold intermediate items between early start of `SurfaceHandler` and view attach. Reviewed By: mdvacca Differential Revision: D27291706 fbshipit-source-id: f383c1d0d7050f271993553b51bf2e387efe1e9e
1 parent 2c0a0a7 commit d97350d

File tree

3 files changed

+74
-28
lines changed

3 files changed

+74
-28
lines changed

ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ private boolean dispatchMountItems() {
183183
printMountItem(command, "dispatchMountItems: Executing viewCommandMountItem");
184184
}
185185
try {
186-
command.execute(mMountingManager);
186+
executeOrEnqueue(command);
187187
} catch (RetryableMountingLayerException e) {
188188
// If the exception is marked as Retryable, we retry the viewcommand exactly once, after
189189
// the current batch of mount items has finished executing.
@@ -225,7 +225,7 @@ private boolean dispatchMountItems() {
225225
+ preMountItemsToDispatch.size());
226226

227227
for (PreAllocateViewMountItem preMountItem : preMountItemsToDispatch) {
228-
preMountItem.execute(mMountingManager);
228+
executeOrEnqueue(preMountItem);
229229
}
230230

231231
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
@@ -244,7 +244,7 @@ private boolean dispatchMountItems() {
244244
}
245245

246246
try {
247-
mountItem.execute(mMountingManager);
247+
executeOrEnqueue(mountItem);
248248
} catch (Throwable e) {
249249
// If there's an exception, we want to log diagnostics in prod and rethrow.
250250
FLog.e(TAG, "dispatchMountItems: caught exception, displaying all MountItems", e);
@@ -288,7 +288,7 @@ public void dispatchPreMountItems(long frameTimeNanos) {
288288
break;
289289
}
290290

291-
preMountItemToDispatch.execute(mMountingManager);
291+
executeOrEnqueue(preMountItemToDispatch);
292292
}
293293
} finally {
294294
mInDispatch = false;
@@ -297,6 +297,16 @@ public void dispatchPreMountItems(long frameTimeNanos) {
297297
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
298298
}
299299

300+
private void executeOrEnqueue(MountItem item) {
301+
if (mMountingManager.isWaitingForViewAttach(item.getSurfaceId())) {
302+
SurfaceMountingManager surfaceMountingManager =
303+
mMountingManager.getSurfaceManager(item.getSurfaceId());
304+
surfaceMountingManager.executeOnViewAttach(item);
305+
} else {
306+
item.execute(mMountingManager);
307+
}
308+
}
309+
300310
@Nullable
301311
private static <E extends MountItem> List<E> drainConcurrentItemQueue(
302312
ConcurrentLinkedQueue<E> queue) {

ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import com.facebook.react.common.mapbuffer.ReadableMapBuffer;
2727
import com.facebook.react.fabric.FabricUIManager;
2828
import com.facebook.react.fabric.events.EventEmitterWrapper;
29-
import com.facebook.react.fabric.mounting.mountitems.MountItem;
3029
import com.facebook.react.touch.JSResponderHandler;
3130
import com.facebook.react.uimanager.RootViewManager;
3231
import com.facebook.react.uimanager.ThemedReactContext;
@@ -40,7 +39,7 @@
4039

4140
/**
4241
* Class responsible for actually dispatching view updates enqueued via {@link
43-
* FabricUIManager#scheduleMountItems(int, MountItem[])} on the UI thread.
42+
* FabricUIManager#scheduleMountItem} on the UI thread.
4443
*/
4544
public class MountingManager {
4645
public static final String TAG = MountingManager.class.getSimpleName();
@@ -53,14 +52,12 @@ public class MountingManager {
5352
private final CopyOnWriteArrayList<Integer> mStoppedSurfaceIds = new CopyOnWriteArrayList<>();
5453

5554
@Nullable private SurfaceMountingManager mMostRecentSurfaceMountingManager;
55+
@Nullable private SurfaceMountingManager mLastQueriedSurfaceMountingManager;
5656

5757
@NonNull private final JSResponderHandler mJSResponderHandler = new JSResponderHandler();
5858
@NonNull private final ViewManagerRegistry mViewManagerRegistry;
5959
@NonNull private final RootViewManager mRootViewManager = new RootViewManager();
6060

61-
private volatile int mStoppedSurfaceCacheLastId = View.NO_ID;
62-
private volatile boolean mStoppedSurfaceCacheLastResult = false;
63-
6461
public MountingManager(@NonNull ViewManagerRegistry viewManagerRegistry) {
6562
mViewManagerRegistry = viewManagerRegistry;
6663
}
@@ -81,7 +78,7 @@ public void startSurface(
8178
public SurfaceMountingManager startSurface(final int surfaceId) {
8279
SurfaceMountingManager surfaceMountingManager =
8380
new SurfaceMountingManager(
84-
surfaceId, mJSResponderHandler, mViewManagerRegistry, mRootViewManager);
81+
surfaceId, mJSResponderHandler, mViewManagerRegistry, mRootViewManager, this);
8582

8683
// There could technically be a race condition here if addRootView is called twice from
8784
// different threads, though this is (probably) extremely unlikely, and likely an error.
@@ -117,8 +114,6 @@ public void attachRootView(
117114

118115
@AnyThread
119116
public void stopSurface(final int surfaceId) {
120-
mStoppedSurfaceCacheLastId = View.NO_ID;
121-
122117
SurfaceMountingManager surfaceMountingManager = mSurfaceIdToManager.get(surfaceId);
123118
if (surfaceMountingManager != null) {
124119
// Maximum number of stopped surfaces to keep track of
@@ -145,6 +140,15 @@ public void stopSurface(final int surfaceId) {
145140

146141
@Nullable
147142
public SurfaceMountingManager getSurfaceManager(int surfaceId) {
143+
if (mLastQueriedSurfaceMountingManager != null
144+
&& mLastQueriedSurfaceMountingManager.getSurfaceId() == surfaceId) {
145+
return mLastQueriedSurfaceMountingManager;
146+
}
147+
148+
if (mMostRecentSurfaceMountingManager != null
149+
&& mMostRecentSurfaceMountingManager.getSurfaceId() == surfaceId) {
150+
return mMostRecentSurfaceMountingManager;
151+
}
148152
return mSurfaceIdToManager.get(surfaceId);
149153
}
150154

@@ -164,32 +168,31 @@ public SurfaceMountingManager getSurfaceManagerEnforced(int surfaceId, String co
164168
}
165169

166170
public boolean surfaceIsStopped(int surfaceId) {
167-
if (surfaceId == View.NO_ID) {
168-
return false;
169-
}
170-
if (surfaceId == mStoppedSurfaceCacheLastId) {
171-
return mStoppedSurfaceCacheLastResult;
172-
}
173-
174-
boolean res = surfaceIsStoppedImpl(surfaceId);
175-
mStoppedSurfaceCacheLastResult = res;
176-
mStoppedSurfaceCacheLastId = surfaceId;
177-
return res;
178-
}
179-
180-
private boolean surfaceIsStoppedImpl(int surfaceId) {
181171
if (mStoppedSurfaceIds.contains(surfaceId)) {
182172
return true;
183173
}
184174

185-
SurfaceMountingManager surfaceMountingManager = mSurfaceIdToManager.get(surfaceId);
175+
SurfaceMountingManager surfaceMountingManager = getSurfaceManager(surfaceId);
186176
if (surfaceMountingManager != null && surfaceMountingManager.isStopped()) {
187177
return true;
188178
}
189179

190180
return false;
191181
}
192182

183+
public boolean isWaitingForViewAttach(int surfaceId) {
184+
SurfaceMountingManager mountingManager = getSurfaceManager(surfaceId);
185+
if (mountingManager == null) {
186+
return false;
187+
}
188+
189+
if (mountingManager.isStopped()) {
190+
return false;
191+
}
192+
193+
return !mountingManager.isRootViewAttached();
194+
}
195+
193196
/**
194197
* Get SurfaceMountingManager associated with a ReactTag. Unfortunately, this requires lookups
195198
* over N maps, where N is the number of active or recently-stopped Surfaces. Each lookup will

ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.facebook.react.bridge.UiThreadUtil;
2727
import com.facebook.react.common.build.ReactBuildConfig;
2828
import com.facebook.react.fabric.events.EventEmitterWrapper;
29+
import com.facebook.react.fabric.mounting.mountitems.MountItem;
2930
import com.facebook.react.touch.JSResponderHandler;
3031
import com.facebook.react.uimanager.IllegalViewOperationException;
3132
import com.facebook.react.uimanager.ReactRoot;
@@ -39,6 +40,7 @@
3940
import com.facebook.react.uimanager.ViewManagerRegistry;
4041
import java.util.Set;
4142
import java.util.concurrent.ConcurrentHashMap;
43+
import java.util.concurrent.ConcurrentLinkedQueue;
4244
import javax.annotation.Nullable;
4345

4446
public class SurfaceMountingManager {
@@ -47,15 +49,18 @@ public class SurfaceMountingManager {
4749
private static final boolean SHOW_CHANGED_VIEW_HIERARCHIES = ReactBuildConfig.DEBUG && false;
4850

4951
private volatile boolean mIsStopped = false;
52+
private volatile boolean mRootViewAttached = false;
5053

5154
@Nullable private ThemedReactContext mThemedReactContext;
5255

5356
// These are all non-null, until StopSurface is called
5457
private ConcurrentHashMap<Integer, ViewState> mTagToViewState =
5558
new ConcurrentHashMap<>(); // any thread
59+
private ConcurrentLinkedQueue<MountItem> mOnViewAttachItems = new ConcurrentLinkedQueue<>();
5660
private JSResponderHandler mJSResponderHandler;
5761
private ViewManagerRegistry mViewManagerRegistry;
5862
private RootViewManager mRootViewManager;
63+
private MountingManager mMountingManager;
5964

6065
// This is null *until* StopSurface is called.
6166
private Set<Integer> mTagSetForStoppedSurface;
@@ -67,12 +72,14 @@ public SurfaceMountingManager(
6772
int surfaceId,
6873
@NonNull JSResponderHandler jsResponderHandler,
6974
@NonNull ViewManagerRegistry viewManagerRegistry,
70-
@NonNull RootViewManager rootViewManager) {
75+
@NonNull RootViewManager rootViewManager,
76+
@NonNull MountingManager mountingManager) {
7177
mSurfaceId = surfaceId;
7278

7379
mJSResponderHandler = jsResponderHandler;
7480
mViewManagerRegistry = viewManagerRegistry;
7581
mRootViewManager = rootViewManager;
82+
mMountingManager = mountingManager;
7683
}
7784

7885
public boolean isStopped() {
@@ -84,6 +91,14 @@ public void attachRootView(View rootView, ThemedReactContext themedReactContext)
8491
addRootView(rootView);
8592
}
8693

94+
public int getSurfaceId() {
95+
return mSurfaceId;
96+
}
97+
98+
public boolean isRootViewAttached() {
99+
return mRootViewAttached;
100+
}
101+
87102
@Nullable
88103
public ThemedReactContext getContext() {
89104
return mThemedReactContext;
@@ -133,6 +148,11 @@ public boolean getViewExists(int tag) {
133148
return mTagToViewState.containsKey(tag);
134149
}
135150

151+
@AnyThread
152+
public void executeOnViewAttach(MountItem item) {
153+
mOnViewAttachItems.add(item);
154+
}
155+
136156
@AnyThread
137157
private void addRootView(@NonNull final View rootView) {
138158
if (isStopped()) {
@@ -174,6 +194,9 @@ public void run() {
174194
if (rootView instanceof ReactRoot) {
175195
((ReactRoot) rootView).setRootViewTag(mSurfaceId);
176196
}
197+
mRootViewAttached = true;
198+
199+
executeViewAttachMountItems();
177200
}
178201
};
179202

@@ -184,6 +207,14 @@ public void run() {
184207
}
185208
}
186209

210+
@UiThread
211+
private void executeViewAttachMountItems() {
212+
while (!mOnViewAttachItems.isEmpty()) {
213+
MountItem item = mOnViewAttachItems.poll();
214+
item.execute(mMountingManager);
215+
}
216+
}
217+
187218
/**
188219
* Stop surface and all operations within it. Garbage-collect Views (caller is responsible for
189220
* removing RootView from View layer).
@@ -232,6 +263,8 @@ public void run() {
232263
mTagToViewState = null;
233264
mJSResponderHandler = null;
234265
mRootViewManager = null;
266+
mMountingManager = null;
267+
mOnViewAttachItems.clear();
235268
}
236269
};
237270

0 commit comments

Comments
 (0)