mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Build ViewGroup mechanism for repeatedly retrying UpdateState until it succeeds
Summary: With BackgroundExecutor+State Reconciliation, it is pretty common to have races where View layer UpdateState calls are thrown away. Theoretically this is /always/ possible since C++ doesn't retry failed UpdateStates, we just try once and then bail. This is part 1 of improving our mechanisms for ensuring that State stays up-to-date from the View layer. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D22933927 fbshipit-source-id: 77b3b87402f772e3f149be0f9200e673bbed7b57
This commit is contained in:
committed by
Facebook GitHub Bot
parent
187fc09b9d
commit
c8f571fdad
@@ -68,4 +68,7 @@ public class ReactFeatureFlags {
|
||||
|
||||
/** Feature flag to use stopSurface when ReactRootView is unmounted. */
|
||||
public static boolean enableStopSurfaceOnRootViewUnmount = false;
|
||||
|
||||
/** Use experimental SetState retry mechanism in view? */
|
||||
public static boolean enableExperimentalStateUpdateRetry = false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.uimanager;
|
||||
|
||||
import android.view.ViewGroup;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.config.ReactFeatureFlags;
|
||||
|
||||
/** This is a helper base class for ViewGroups that use Fabric State. */
|
||||
public abstract class FabricStateBaseViewGroup extends ViewGroup {
|
||||
public interface StateUpdateCallback {
|
||||
WritableMap getStateUpdate();
|
||||
}
|
||||
|
||||
private StateWrapper mStateWrapper = null;
|
||||
|
||||
public FabricStateBaseViewGroup(ThemedReactContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public void setStateWrapper(StateWrapper stateWrapper) {
|
||||
mStateWrapper = stateWrapper;
|
||||
}
|
||||
|
||||
private static void setState(
|
||||
final FabricStateBaseViewGroup view,
|
||||
final StateWrapper stateWrapper,
|
||||
final StateUpdateCallback stateUpdateCallback,
|
||||
final int numTries) {
|
||||
// The StateWrapper will change, breaking this loop, whenever the UpdateState MountItem
|
||||
// is executed.
|
||||
// The caller is responsible for detecting if data is up-to-date, and doing nothing, or
|
||||
// detecting if state is stale and calling setState again.
|
||||
if (stateWrapper != view.mStateWrapper) {
|
||||
return;
|
||||
}
|
||||
// We arbitrarily bail out after a certain number of retries.
|
||||
// This is a pretty large number: in practice I've seen this number go over 50
|
||||
// with minimal/no visual jank.
|
||||
if (numTries > 5 * 60) {
|
||||
return;
|
||||
}
|
||||
|
||||
stateWrapper.updateState(stateUpdateCallback.getStateUpdate());
|
||||
|
||||
// An `updateState` call can fail, and there's no way to verify if it succeeds besides
|
||||
// waiting for a corresponding `StateUpdate` MountItem to be executed on some future UI tick.
|
||||
// So.... to resolve conflicts with updateState, we just keep firing it until it succeeds or
|
||||
// the View goes away.
|
||||
if (ReactFeatureFlags.enableExperimentalStateUpdateRetry) {
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setState(view, stateWrapper, stateUpdateCallback, numTries + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void setState(
|
||||
final FabricStateBaseViewGroup view,
|
||||
final StateWrapper stateWrapper,
|
||||
final StateUpdateCallback stateUpdateCallback) {
|
||||
setState(view, stateWrapper, stateUpdateCallback, 0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user