Skip to content

Commit 5e7c774

Browse files
axe-fbfacebook-github-bot
authored andcommitted
Initialize the Choreographer in ReactChoreographer lazily
Summary: Today, ReactInstanceManager calls `ReactChoreographer.initialize()` in its constructor. Since `ReactChoreographer` also needs to run on the UI thread, this forces the entire `ReactInstanceManager` to run on the UI thread. By moving `ReactChoreographer` to lazily set up its Choreographer, we can make `ReactInstanceManager` run on any thread Reviewed By: mdvacca Differential Revision: D10097432 fbshipit-source-id: eb8c80aafcba745ea15c86296d11c487329b1df0
1 parent c8b6d60 commit 5e7c774

File tree

2 files changed

+46
-9
lines changed

2 files changed

+46
-9
lines changed

ReactAndroid/src/main/java/com/facebook/react/modules/core/ChoreographerCompat.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import android.os.Handler;
1414
import android.os.Looper;
1515
import android.view.Choreographer;
16+
import com.facebook.react.bridge.UiThreadUtil;
1617

1718
/**
1819
* Wrapper class for abstracting away availability of the JellyBean Choreographer. If Choreographer
@@ -23,13 +24,17 @@ public class ChoreographerCompat {
2324
private static final long ONE_FRAME_MILLIS = 17;
2425
private static final boolean IS_JELLYBEAN_OR_HIGHER =
2526
Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
26-
private static final ChoreographerCompat INSTANCE = new ChoreographerCompat();
27+
private static ChoreographerCompat sInstance;
2728

2829
private Handler mHandler;
2930
private Choreographer mChoreographer;
3031

3132
public static ChoreographerCompat getInstance() {
32-
return INSTANCE;
33+
UiThreadUtil.assertOnUiThread();
34+
if (sInstance == null){
35+
sInstance = new ChoreographerCompat();
36+
}
37+
return sInstance;
3338
}
3439

3540
private ChoreographerCompat() {

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

+39-7
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77

88
package com.facebook.react.modules.core;
99

10+
import com.facebook.react.bridge.UiThreadUtil;
1011
import java.util.ArrayDeque;
12+
import javax.annotation.Nullable;
1113

1214
import com.facebook.common.logging.FLog;
1315
import com.facebook.infer.annotation.Assertions;
14-
import com.facebook.react.bridge.UiThreadUtil;
1516
import com.facebook.react.common.ReactConstants;
1617

1718
/**
@@ -65,7 +66,6 @@ private CallbackType(int order) {
6566

6667
public static void initialize() {
6768
if (sInstance == null) {
68-
UiThreadUtil.assertOnUiThread();
6969
sInstance = new ReactChoreographer();
7070
}
7171
}
@@ -75,20 +75,21 @@ public static ReactChoreographer getInstance() {
7575
return sInstance;
7676
}
7777

78-
private final ChoreographerCompat mChoreographer;
78+
// This needs to be volatile due to double checked locking issue - https://fburl.com/z409owpf
79+
private @Nullable volatile ChoreographerCompat mChoreographer;
7980
private final ReactChoreographerDispatcher mReactChoreographerDispatcher;
8081
private final ArrayDeque<ChoreographerCompat.FrameCallback>[] mCallbackQueues;
8182

8283
private int mTotalCallbacks = 0;
8384
private boolean mHasPostedCallback = false;
8485

8586
private ReactChoreographer() {
86-
mChoreographer = ChoreographerCompat.getInstance();
8787
mReactChoreographerDispatcher = new ReactChoreographerDispatcher();
8888
mCallbackQueues = new ArrayDeque[CallbackType.values().length];
8989
for (int i = 0; i < mCallbackQueues.length; i++) {
9090
mCallbackQueues[i] = new ArrayDeque<>();
9191
}
92+
initializeChoreographer(null);
9293
}
9394

9495
public synchronized void postFrameCallback(
@@ -98,11 +99,40 @@ public synchronized void postFrameCallback(
9899
mTotalCallbacks++;
99100
Assertions.assertCondition(mTotalCallbacks > 0);
100101
if (!mHasPostedCallback) {
101-
mChoreographer.postFrameCallback(mReactChoreographerDispatcher);
102-
mHasPostedCallback = true;
102+
if (mChoreographer == null) {
103+
initializeChoreographer(new Runnable(){
104+
@Override
105+
public void run() {
106+
postFrameCallbackOnChoreographer();
107+
}
108+
});
109+
} else {
110+
postFrameCallbackOnChoreographer();
111+
}
103112
}
104113
}
105114

115+
public void postFrameCallbackOnChoreographer() {
116+
mChoreographer.postFrameCallback(mReactChoreographerDispatcher);
117+
mHasPostedCallback = true;
118+
}
119+
120+
public void initializeChoreographer(@Nullable final Runnable runnable) {
121+
UiThreadUtil.runOnUiThread(new Runnable() {
122+
@Override
123+
public void run() {
124+
synchronized (ReactChoreographer.class) {
125+
if (mChoreographer == null) {
126+
mChoreographer = ChoreographerCompat.getInstance();
127+
}
128+
}
129+
if (runnable != null) {
130+
runnable.run();
131+
}
132+
}
133+
});
134+
}
135+
106136
public synchronized void removeFrameCallback(
107137
CallbackType type,
108138
ChoreographerCompat.FrameCallback frameCallback) {
@@ -117,7 +147,9 @@ public synchronized void removeFrameCallback(
117147
private void maybeRemoveFrameCallback() {
118148
Assertions.assertCondition(mTotalCallbacks >= 0);
119149
if (mTotalCallbacks == 0 && mHasPostedCallback) {
120-
mChoreographer.removeFrameCallback(mReactChoreographerDispatcher);
150+
if (mChoreographer != null) {
151+
mChoreographer.removeFrameCallback(mReactChoreographerDispatcher);
152+
}
121153
mHasPostedCallback = false;
122154
}
123155
}

0 commit comments

Comments
 (0)