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:
Родитель
c54f5cf72a
Коммит
ce226c1f28
|
@ -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
|
||||
|
|
|
@ -62,4 +62,12 @@ public interface UIManager extends JSIModule, PerformanceCounter {
|
|||
* @param eventType
|
||||
*/
|
||||
void sendAccessibilityEvent(int reactTag, int eventType);
|
||||
|
||||
/**
|
||||
* When mounting instructions are scheduled on the UI thread, should they be executed immediately?
|
||||
* For Fabric. Should noop in pre-Fabric.
|
||||
*
|
||||
* @param flag
|
||||
*/
|
||||
void setAllowImmediateUIOperationExecution(boolean flag);
|
||||
}
|
||||
|
|
|
@ -125,6 +125,13 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
@NonNull
|
||||
private final DispatchUIFrameCallback mDispatchUIFrameCallback;
|
||||
|
||||
/**
|
||||
* Whether or not to immediately, synchronously execute mountItems when they are scheduled on the
|
||||
* UI thread.
|
||||
*/
|
||||
@ThreadConfined(UI)
|
||||
private boolean mImmediatelyExecutedMountItemsOnUI = true;
|
||||
|
||||
/**
|
||||
* This is used to keep track of whether or not the FabricUIManager has been destroyed. Once the
|
||||
* Catalyst instance is being destroyed, we should cease all operation here.
|
||||
|
@ -446,6 +453,16 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When mounting instructions are scheduled on the UI thread, should they be executed immediately?
|
||||
* *
|
||||
*/
|
||||
@Override
|
||||
@ThreadConfined(UI)
|
||||
public void setAllowImmediateUIOperationExecution(boolean flag) {
|
||||
mImmediatelyExecutedMountItemsOnUI = flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method enqueues UI operations directly to the UI thread. This might change in the future
|
||||
* to enforce execution order using {@link ReactChoreographer#CallbackType}.
|
||||
|
@ -480,7 +497,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
mMountItems.add(mountItem);
|
||||
}
|
||||
|
||||
if (UiThreadUtil.isOnUiThread()) {
|
||||
if (mImmediatelyExecutedMountItemsOnUI && UiThreadUtil.isOnUiThread()) {
|
||||
dispatchMountItems();
|
||||
}
|
||||
|
||||
|
|
|
@ -828,6 +828,11 @@ public class UIManagerModule extends ReactContextBaseJavaModule
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAllowImmediateUIOperationExecution(boolean flag) {
|
||||
// Noop outside of Fabric, call directly on FabricUIManager
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a block to be executed on the UI thread. Useful if you need to execute view logic
|
||||
* after all currently queued view updates have completed.
|
||||
|
|
Загрузка…
Ссылка в новой задаче