Fix T54997838

Summary:
Fixes T54997838 by preventing any view mutations during `onMeasure` calls.

There might still be places where this is possible, but this is where I'm seeing all the crashes currently.

See comments in ReactRootView for why views were mutated during onMeasure.

Changelog: [Internal]

Reviewed By: mdvacca

Differential Revision: D18518591

fbshipit-source-id: 1406af8a6b0bfcc86f4cc5b451b3967f312dfd85
This commit is contained in:
Joshua Gross
2019-11-14 21:21:31 -08:00
committed by Facebook Github Bot
parent c54f5cf72a
commit ce226c1f28
4 changed files with 67 additions and 1 deletions
@@ -127,8 +127,11 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setAllowImmediateUIOperationExecution(false);
if (mUseSurface) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setAllowImmediateUIOperationExecution(true);
return;
}
@@ -182,6 +185,7 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
mLastHeight = height;
} finally {
setAllowImmediateUIOperationExecution(true);
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
@@ -437,6 +441,38 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
}
}
/**
* In Fabric, it is possible for MountItems to be scheduled during onMeasure calls, specifically:
*
* <p>ReactRootView.onMeasure -> ReactRootView.updateRootLayoutSpecs ->
* FabricUIManager.updateRootLayoutSpecs -> Binding.setConstraints -> (C++) commit new tree ->
* (C++ Android binding) diff tree, schedule mount items -> FabricUIManager.scheduleMountItem
*
* <p>If called on the main thread, `scheduleMountItem` will execute MountItems synchronously,
* causing all ShadowNode updates to be flushed to the view hierarchy, on the main thread, during
* an onMeasure call.
*
* <p>Use this method to disable immediate execution of mount items.
*
* <p>This is a noop outside in pre-Fabric React Native.
*/
private void setAllowImmediateUIOperationExecution(boolean flag) {
final ReactInstanceManager reactInstanceManager = mReactInstanceManager;
if (reactInstanceManager == null) {
return;
}
final ReactContext reactApplicationContext = reactInstanceManager.getCurrentReactContext();
if (reactApplicationContext == null) {
return;
}
UIManagerHelper.getUIManager(reactApplicationContext, getUIManagerType())
.setAllowImmediateUIOperationExecution(flag);
}
/**
* Unmount the react application at this root view, reclaiming any JS memory associated with that
* application. If {@link #startReactApplication} is called, this method must be called before the