Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import com.facebook.react.modules.core.PermissionListener;
import com.facebook.react.util.AndroidVersion;
import org.jetbrains.annotations.NotNull;
import com.facebook.react.uimanager.UIManagerConstantsCache;

/** Base Activity for React Native applications. */
public abstract class ReactActivity extends AppCompatActivity
Expand Down Expand Up @@ -59,7 +58,6 @@ protected ReactActivityDelegate createReactActivityDelegate() {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
UIManagerConstantsCache.getInstance().init(this);
mDelegate.onCreate(savedInstanceState);
if (AndroidVersion.isAtLeastTargetSdk36(this)) {
getOnBackPressedDispatcher().addCallback(this, mBackPressedCallback);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ internal class ReactInstance(
// initialized.
// This happens inside getTurboModuleManagerDelegate getter.
if (ReactNativeFeatureFlags.useNativeViewConfigsInBridgelessMode()) {
val customBubblingEvents: MutableMap<String, Any> = HashMap()
val customDirectEvents: MutableMap<String, Any> = HashMap()

UIConstantsProviderBinding.install(
Expand All @@ -239,19 +240,30 @@ internal class ReactInstance(
getConstantsForViewManager(viewManager, customDirectEvents)
},
{
UIManagerConstantsCache.getInstance().getUIManagerConstantsAsWritableMap()
?: run {
val viewManagers: List<ViewManager<*, *>> =
ArrayList(viewManagerResolver.eagerViewManagerMap.values)
val constants = createConstants(viewManagers, customDirectEvents)

val lazyViewManagers = viewManagerResolver.lazyViewManagerNames
if (!lazyViewManagers.isEmpty()) {
constants["ViewManagerNames"] = ArrayList(lazyViewManagers)
constants["LazyViewManagersEnabled"] = true
}
Arguments.makeNativeMap(constants)
}
val constantsCache = UIManagerConstantsCache.getInstance()
val cachedConstants = constantsCache.getUIManagerConstantsAsWritableMap()
if (cachedConstants != null) {
cachedConstants
} else {
val viewManagers: List<ViewManager<*, *>> =
ArrayList(viewManagerResolver.eagerViewManagerMap.values)
val constants =
createConstants(viewManagers, customBubblingEvents, customDirectEvents)

val lazyViewManagers = viewManagerResolver.lazyViewManagerNames
if (!lazyViewManagers.isEmpty()) {
constants["ViewManagerNames"] = ArrayList(lazyViewManagers)
constants["LazyViewManagersEnabled"] = true
}

// Persist only after the constants are complete so a cache hit returns the exact
// same shape as the miss path.
constantsCache.saveConstantsAndBubblingEventsTypes(
constants,
customBubblingEvents,
)
Arguments.makeNativeMap(constants)
}
},
)
}
Expand Down Expand Up @@ -598,6 +610,7 @@ internal class ReactInstance(

private fun createConstants(
viewManagers: List<ViewManager<in Nothing, in Nothing>>,
customBubblingEvents: MutableMap<String, Any>?,
customDirectEvents: MutableMap<String, Any>?,
): MutableMap<String, Any> {
ReactMarker.logMarker(ReactMarkerConstants.CREATE_UI_MANAGER_MODULE_CONSTANTS_START)
Expand All @@ -607,7 +620,7 @@ internal class ReactInstance(
try {
return UIManagerModuleConstantsHelper.createConstants(
viewManagers,
null,
customBubblingEvents,
customDirectEvents,
)
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ public class UIManagerConstantsCache {
/** Ensures init(...) is only done once. */
private volatile boolean initCalled = false;

/** Allows the host application to keep the cache fully disabled for an experiment control. */
private volatile boolean cacheEnabled = false;

private String cacheVersionName = null;

private UIManagerConstantsCache() {
Expand All @@ -75,18 +78,33 @@ public static UIManagerConstantsCache getInstance() {
}

/**
* Must be called (once) from Application or MainActivity before any UIManager
* constants are accessed. This kicks off:
* Must be called once from Application before any UIManager constants are accessed. This kicks
* off:
* 1) MMKV.initialize(...)
* 2) A background thread that reads two MMKV keys, parses them → Maps,
* and countDown()s loadLatch.
*/
public synchronized void init(Context appContext) {
public void init(Context appContext) {
init(appContext, true);
}

/**
* Initializes the cache when enabled. A disabled initialization permanently leaves this
* singleton disabled for the process so later initialization cannot change the experiment
* assignment.
*/
public synchronized void init(Context appContext, boolean enabled) {
if (initCalled) {
return;
}
cacheEnabled = enabled;
initCalled = true;

if (!cacheEnabled) {
loadLatch.countDown();
return;
}

// Its possible that the constants change between versions, so we have to keep track of the version
// and reset the cache if it changes.
// TODO: This can be improved by us compiling the UIManager constants at compile time
Expand All @@ -96,6 +114,8 @@ public synchronized void init(Context appContext) {
.versionName;
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Failed to get app version name; continuing without caching!", e);
cacheEnabled = false;
loadLatch.countDown();
return;
}

Expand Down Expand Up @@ -183,6 +203,9 @@ public synchronized void init(Context appContext) {
* @return parsed Map<String,Object> or null if none stored / parse failed.
*/
public Map<String,Object> getCachedConstants() {
if (!isCacheEnabled()) {
return null;
}
try {
loadLatch.await();
} catch (InterruptedException e) {
Expand All @@ -200,6 +223,9 @@ public Map<String,Object> getCachedConstants() {
* @return parsed Map<String,Object> or null if none stored / parse failed.
*/
public Map<String,Object> getCachedBubblingEventsTypes() {
if (!isCacheEnabled()) {
return null;
}
try {
loadLatch.await();
} catch (InterruptedException e) {
Expand All @@ -223,6 +249,9 @@ public Map<String,Object> getCachedBubblingEventsTypes() {
* @return WritableNativeMap or null if no full-constants available.
*/
public WritableNativeMap getUIManagerConstantsAsWritableMap() {
if (!isCacheEnabled()) {
return null;
}
try {
loadLatch.await();
} catch (InterruptedException e) {
Expand All @@ -246,6 +275,9 @@ public WritableNativeMap getUIManagerConstantsAsWritableMap() {
public void saveConstantsAndBubblingEventsTypes(
final Map<String,Object> constants,
final Map<String,Object> bubblingEventsTypes) {
if (!isCacheEnabled()) {
return;
}
if (constants == null) {
return;
}
Expand Down Expand Up @@ -282,6 +314,15 @@ public void saveConstantsAndBubblingEventsTypes(
MMKV.defaultMMKV().encode(MMKV_KEY_VERSION, cacheVersionName);
}

private boolean isCacheEnabled() {
if (!initCalled) {
throw new IllegalStateException(
"UIManagerConstantsCache not initialized! Call init(...) first."
);
}
return cacheEnabled;
}

// ────────────────────────────────────────────────────────────────────────────────
// JSON ↔ Map<String,Object> Helpers (identical to before)
// ────────────────────────────────────────────────────────────────────────────────
Expand Down