Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose an API to enable Concurrent Root on Android #33645

Closed
wants to merge 1 commit into from
Closed
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 @@ -15,6 +15,7 @@
import android.os.Build;
import android.os.Bundle;
import android.view.KeyEvent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.Callback;
Expand All @@ -33,6 +34,7 @@ public class ReactActivityDelegate {
private @Nullable PermissionListener mPermissionListener;
private @Nullable Callback mPermissionsCallback;
private ReactDelegate mReactDelegate;
private boolean mConcurrentRootEnabled;

@Deprecated
public ReactActivityDelegate(Activity activity, @Nullable String mainComponentName) {
Expand All @@ -45,10 +47,27 @@ public ReactActivityDelegate(ReactActivity activity, @Nullable String mainCompon
mMainComponentName = mainComponentName;
}

/**
* Public API to populate the launch options that will be passed to React. Here you can customize
* the values that will be passed as `initialProperties` to the Renderer.
*
* @return Either null or a key-value map as a Bundle
*/
protected @Nullable Bundle getLaunchOptions() {
return null;
}

private @NonNull Bundle composeLaunchOptions() {
Bundle composedLaunchOptions = getLaunchOptions();
if (isConcurrentRootEnabled()) {
if (composedLaunchOptions == null) {
composedLaunchOptions = new Bundle();
}
composedLaunchOptions.putBoolean("concurrentRoot", true);
}
return composedLaunchOptions;
}

protected ReactRootView createRootView() {
return new ReactRootView(getContext());
}
Expand All @@ -74,9 +93,10 @@ public String getMainComponentName() {

protected void onCreate(Bundle savedInstanceState) {
String mainComponentName = getMainComponentName();
Bundle launchOptions = composeLaunchOptions();
mReactDelegate =
new ReactDelegate(
getPlainActivity(), getReactNativeHost(), mainComponentName, getLaunchOptions()) {
getPlainActivity(), getReactNativeHost(), mainComponentName, launchOptions) {
@Override
protected ReactRootView createRootView() {
return ReactActivityDelegate.this.createRootView();
Expand Down Expand Up @@ -190,4 +210,16 @@ protected Context getContext() {
protected Activity getPlainActivity() {
return ((Activity) getContext());
}

/**
* Override this method to enable Concurrent Root on the surface for this Activity. See:
* https://reactjs.org/blog/2022/03/29/react-v18.html
*
* <p>This requires to be rendering on Fabric (i.e. on the New Architecture).
*
* @return Wether you want to enable Concurrent Root for this surface or not.
*/
protected boolean isConcurrentRootEnabled() {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright (c) Meta Platforms, Inc. and 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

import android.os.Bundle
import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class ReactActivityDelegateTest {

@Test
fun delegateWithConcurrentRoot_populatesInitialPropsCorrectly() {
val delegate =
object : ReactActivityDelegate(null, "test-delegate") {
override fun isConcurrentRootEnabled() = true
public val inspectLaunchOptions: Bundle?
get() = getLaunchOptions()
}

assertNotNull(delegate.inspectLaunchOptions)
assertTrue(delegate.inspectLaunchOptions!!.containsKey("concurrentRoot"))
assertTrue(delegate.inspectLaunchOptions!!.getBoolean("concurrentRoot"))
}

@Test
fun delegateWithoutConcurrentRoot_hasNullInitialProperties() {
val delegate =
object : ReactActivityDelegate(null, "test-delegate") {
override fun isConcurrentRootEnabled() = false
public val inspectLaunchOptions: Bundle?
get() = getLaunchOptions()
}

assertNull(delegate.inspectLaunchOptions)
}

@Test
fun delegateWithConcurrentRoot_composesInitialPropertiesCorrectly() {
val delegate =
object : ReactActivityDelegate(null, "test-delegate") {
override fun isConcurrentRootEnabled() = true
override fun getLaunchOptions(): Bundle =
Bundle().apply { putString("test-property", "test-value") }
public val inspectLaunchOptions: Bundle?
get() = getLaunchOptions()
}

assertNotNull(delegate.inspectLaunchOptions)
assertTrue(delegate.inspectLaunchOptions!!.containsKey("concurrentRoot"))
assertTrue(delegate.inspectLaunchOptions!!.getBoolean("concurrentRoot"))
assertTrue(delegate.inspectLaunchOptions!!.containsKey("test-property"))
assertEquals("test-value", delegate.inspectLaunchOptions!!.getString("test-property"))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ protected void onCreate(Bundle savedInstanceState) {
protected Bundle getLaunchOptions() {
return mInitialProps;
}

@Override
protected boolean isConcurrentRootEnabled() {
return true;
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,12 @@ protected ReactRootView createRootView() {
reactRootView.setIsFabric(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED);
return reactRootView;
}

@Override
protected boolean isConcurrentRootEnabled() {
// If you opted-in for the New Architecture, we enable Concurrent Root (i.e. React 18).
// More on this on https://reactjs.org/blog/2022/03/29/react-v18.html
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
}
}
}