Skip to content

Commit 751f7e9

Browse files
nlutsenkofacebook-github-bot
authored andcommitted
rn-android | Add abstraction layer between ReactChoreographer and android.view.Choreographer that allows substituting current implementation.
Summary: We want to have an extension point for choreographer, so we can override default behavior and have either rate-limiting, or testing or other form of manual control. For all those cases allow substitution of choreographer that ReactChoreographer would use by default with a custom one. Changelog: [Android][Added] ReactChoreographer can now use an implementation substitution instead of relying on android.view.Choreographer directly. Reviewed By: javache Differential Revision: D50827975 fbshipit-source-id: 0fd78e1f4f96ffd832e5d8cdc6c805f9a9e272cf
1 parent ae85be3 commit 751f7e9

File tree

7 files changed

+83
-10
lines changed

7 files changed

+83
-10
lines changed

packages/react-native/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIntegrationTestCase.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.facebook.react.bridge.UiThreadUtil;
2323
import com.facebook.react.common.futures.SimpleSettableFuture;
2424
import com.facebook.react.devsupport.interfaces.DevSupportManager;
25+
import com.facebook.react.internal.AndroidChoreographerProvider;
2526
import com.facebook.react.modules.core.ReactChoreographer;
2627
import com.facebook.react.modules.core.TimingModule;
2728
import com.facebook.react.testing.idledetection.ReactBridgeIdleSignaler;
@@ -134,7 +135,7 @@ protected TimingModule createTimingModule() {
134135
new SimpleSettableFuture<TimingModule>();
135136
UiThreadUtil.runOnUiThread(
136137
() -> {
137-
ReactChoreographer.initialize();
138+
ReactChoreographer.initialize(AndroidChoreographerProvider.getInstance());
138139
TimingModule timingModule =
139140
new TimingModule(getContext(), Mockito.mock(DevSupportManager.class));
140141
simpleSettableFuture.set(timingModule);

packages/react-native/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactTestHelper.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.facebook.react.bridge.NativeModule;
3030
import com.facebook.react.bridge.ReactApplicationContext;
3131
import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec;
32+
import com.facebook.react.internal.AndroidChoreographerProvider;
3233
import com.facebook.react.modules.core.ReactChoreographer;
3334
import com.facebook.react.uimanager.ViewManager;
3435
import java.util.Arrays;
@@ -149,7 +150,7 @@ public CatalystInstance build() {
149150
new Runnable() {
150151
@Override
151152
public void run() {
152-
ReactChoreographer.initialize();
153+
ReactChoreographer.initialize(AndroidChoreographerProvider.getInstance());
153154
instance.initialize();
154155
}
155156
});

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
import com.facebook.react.devsupport.interfaces.DevSupportManager;
8888
import com.facebook.react.devsupport.interfaces.PackagerStatusCallback;
8989
import com.facebook.react.devsupport.interfaces.RedBoxHandler;
90+
import com.facebook.react.internal.AndroidChoreographerProvider;
9091
import com.facebook.react.internal.turbomodule.core.TurboModuleManager;
9192
import com.facebook.react.internal.turbomodule.core.TurboModuleManagerDelegate;
9293
import com.facebook.react.modules.appearance.AppearanceModule;
@@ -293,7 +294,7 @@ public void invokeDefaultOnBackPressed() {
293294
mJSIModulePackage = jsiModulePackage;
294295

295296
// Instantiate ReactChoreographer in UI thread.
296-
ReactChoreographer.initialize();
297+
ReactChoreographer.initialize(AndroidChoreographerProvider.getInstance());
297298
if (mUseDeveloperSupport) {
298299
mDevSupportManager.startInspector();
299300
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.react.internal;
9+
10+
import com.facebook.react.bridge.UiThreadUtil;
11+
12+
/** An implementation of ChoreographerProvider that directly uses android.view.Choreographer. */
13+
public final class AndroidChoreographerProvider implements ChoreographerProvider {
14+
15+
public static final class AndroidChoreographer implements ChoreographerProvider.Choreographer {
16+
private final android.view.Choreographer sInstance = android.view.Choreographer.getInstance();
17+
18+
public void postFrameCallback(android.view.Choreographer.FrameCallback callback) {
19+
sInstance.postFrameCallback(callback);
20+
}
21+
22+
public void removeFrameCallback(android.view.Choreographer.FrameCallback callback) {
23+
sInstance.removeFrameCallback(callback);
24+
}
25+
}
26+
27+
private static class Holder {
28+
private static final AndroidChoreographerProvider INSTANCE = new AndroidChoreographerProvider();
29+
}
30+
31+
public static AndroidChoreographerProvider getInstance() {
32+
return Holder.INSTANCE;
33+
}
34+
35+
public Choreographer getChoreographer() {
36+
UiThreadUtil.assertOnUiThread();
37+
return new AndroidChoreographer();
38+
}
39+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.react.internal;
9+
10+
public interface ChoreographerProvider {
11+
/**
12+
* The interface to a android.view.Choreographer-like object, that can either use the
13+
* android.view.Choreographer or a mock one for testing purposes, or override built-in
14+
* android.view.Choreographer's behaviors.
15+
*/
16+
interface Choreographer {
17+
18+
/** Posts a frame callback to run on the next frame. */
19+
void postFrameCallback(android.view.Choreographer.FrameCallback callback);
20+
/** Removes a previously posted frame callback. */
21+
void removeFrameCallback(android.view.Choreographer.FrameCallback callback);
22+
}
23+
24+
/**
25+
* Get an instance of Choreographer.
26+
*
27+
* @return An instance of Choreographer.
28+
*/
29+
Choreographer getChoreographer();
30+
}

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/ReactChoreographer.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import com.facebook.infer.annotation.Assertions;
1414
import com.facebook.react.bridge.UiThreadUtil;
1515
import com.facebook.react.common.ReactConstants;
16+
import com.facebook.react.internal.ChoreographerProvider;
1617
import java.util.ArrayDeque;
1718

1819
/**
@@ -55,9 +56,9 @@ public enum CallbackType {
5556

5657
private static ReactChoreographer sInstance;
5758

58-
public static void initialize() {
59+
public static void initialize(ChoreographerProvider choreographerProvider) {
5960
if (sInstance == null) {
60-
sInstance = new ReactChoreographer();
61+
sInstance = new ReactChoreographer(choreographerProvider);
6162
}
6263
}
6364

@@ -66,8 +67,7 @@ public static ReactChoreographer getInstance() {
6667
return sInstance;
6768
}
6869

69-
// This needs to be volatile due to double checked locking issue - https://fburl.com/z409owpf
70-
private @Nullable volatile Choreographer mChoreographer;
70+
private @Nullable ChoreographerProvider.Choreographer mChoreographer;
7171

7272
private final ArrayDeque<Choreographer.FrameCallback>[] mCallbackQueues;
7373

@@ -101,15 +101,15 @@ public void doFrame(long frameTimeNanos) {
101101
private int mTotalCallbacks = 0;
102102
private boolean mHasPostedCallback = false;
103103

104-
private ReactChoreographer() {
104+
private ReactChoreographer(ChoreographerProvider choreographerProvider) {
105105
mCallbackQueues = new ArrayDeque[CallbackType.values().length];
106106
for (int i = 0; i < mCallbackQueues.length; i++) {
107107
mCallbackQueues[i] = new ArrayDeque<>();
108108
}
109109

110110
UiThreadUtil.runOnUiThread(
111111
() -> {
112-
mChoreographer = Choreographer.getInstance();
112+
mChoreographer = choreographerProvider.getChoreographer();
113113
});
114114
}
115115

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import com.facebook.react.fabric.ReactNativeConfig;
4545
import com.facebook.react.fabric.events.EventBeatManager;
4646
import com.facebook.react.interfaces.exceptionmanager.ReactJsExceptionHandler;
47+
import com.facebook.react.internal.AndroidChoreographerProvider;
4748
import com.facebook.react.internal.turbomodule.core.CallInvokerHolderImpl;
4849
import com.facebook.react.internal.turbomodule.core.NativeMethodCallInvokerHolderImpl;
4950
import com.facebook.react.internal.turbomodule.core.TurboModuleManager;
@@ -137,7 +138,7 @@ final class ReactInstance {
137138
MessageQueueThread nativeModulesMessageQueueThread =
138139
mQueueConfiguration.getNativeModulesQueueThread();
139140

140-
ReactChoreographer.initialize();
141+
ReactChoreographer.initialize(AndroidChoreographerProvider.getInstance());
141142
if (useDevSupport) {
142143
devSupportManager.startInspector();
143144
}

0 commit comments

Comments
 (0)