-
Notifications
You must be signed in to change notification settings - Fork 24.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Build ViewGroup mechanism for repeatedly retrying UpdateState until i…
…t 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
- Loading branch information
1 parent
187fc09
commit c8f571f
Showing
2 changed files
with
76 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
73 changes: 73 additions & 0 deletions
73
ReactAndroid/src/main/java/com/facebook/react/uimanager/FabricStateBaseViewGroup.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |