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
This commit is contained in:
Ram N 2018-10-12 19:35:50 -07:00 committed by Facebook Github Bot
parent c8b6d606a0
commit 5e7c774d64
2 changed files with 46 additions and 9 deletions

View File

@ -13,6 +13,7 @@ import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.view.Choreographer; import android.view.Choreographer;
import com.facebook.react.bridge.UiThreadUtil;
/** /**
* Wrapper class for abstracting away availability of the JellyBean Choreographer. If Choreographer * Wrapper class for abstracting away availability of the JellyBean Choreographer. If Choreographer
@ -23,13 +24,17 @@ public class ChoreographerCompat {
private static final long ONE_FRAME_MILLIS = 17; private static final long ONE_FRAME_MILLIS = 17;
private static final boolean IS_JELLYBEAN_OR_HIGHER = private static final boolean IS_JELLYBEAN_OR_HIGHER =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
private static final ChoreographerCompat INSTANCE = new ChoreographerCompat(); private static ChoreographerCompat sInstance;
private Handler mHandler; private Handler mHandler;
private Choreographer mChoreographer; private Choreographer mChoreographer;
public static ChoreographerCompat getInstance() { public static ChoreographerCompat getInstance() {
return INSTANCE; UiThreadUtil.assertOnUiThread();
if (sInstance == null){
sInstance = new ChoreographerCompat();
}
return sInstance;
} }
private ChoreographerCompat() { private ChoreographerCompat() {

View File

@ -7,11 +7,12 @@
package com.facebook.react.modules.core; package com.facebook.react.modules.core;
import com.facebook.react.bridge.UiThreadUtil;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import javax.annotation.Nullable;
import com.facebook.common.logging.FLog; import com.facebook.common.logging.FLog;
import com.facebook.infer.annotation.Assertions; import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.common.ReactConstants; import com.facebook.react.common.ReactConstants;
/** /**
@ -65,7 +66,6 @@ public class ReactChoreographer {
public static void initialize() { public static void initialize() {
if (sInstance == null) { if (sInstance == null) {
UiThreadUtil.assertOnUiThread();
sInstance = new ReactChoreographer(); sInstance = new ReactChoreographer();
} }
} }
@ -75,7 +75,8 @@ public class ReactChoreographer {
return sInstance; return sInstance;
} }
private final ChoreographerCompat mChoreographer; // This needs to be volatile due to double checked locking issue - https://fburl.com/z409owpf
private @Nullable volatile ChoreographerCompat mChoreographer;
private final ReactChoreographerDispatcher mReactChoreographerDispatcher; private final ReactChoreographerDispatcher mReactChoreographerDispatcher;
private final ArrayDeque<ChoreographerCompat.FrameCallback>[] mCallbackQueues; private final ArrayDeque<ChoreographerCompat.FrameCallback>[] mCallbackQueues;
@ -83,12 +84,12 @@ public class ReactChoreographer {
private boolean mHasPostedCallback = false; private boolean mHasPostedCallback = false;
private ReactChoreographer() { private ReactChoreographer() {
mChoreographer = ChoreographerCompat.getInstance();
mReactChoreographerDispatcher = new ReactChoreographerDispatcher(); mReactChoreographerDispatcher = new ReactChoreographerDispatcher();
mCallbackQueues = new ArrayDeque[CallbackType.values().length]; mCallbackQueues = new ArrayDeque[CallbackType.values().length];
for (int i = 0; i < mCallbackQueues.length; i++) { for (int i = 0; i < mCallbackQueues.length; i++) {
mCallbackQueues[i] = new ArrayDeque<>(); mCallbackQueues[i] = new ArrayDeque<>();
} }
initializeChoreographer(null);
} }
public synchronized void postFrameCallback( public synchronized void postFrameCallback(
@ -98,11 +99,40 @@ public class ReactChoreographer {
mTotalCallbacks++; mTotalCallbacks++;
Assertions.assertCondition(mTotalCallbacks > 0); Assertions.assertCondition(mTotalCallbacks > 0);
if (!mHasPostedCallback) { if (!mHasPostedCallback) {
mChoreographer.postFrameCallback(mReactChoreographerDispatcher); if (mChoreographer == null) {
mHasPostedCallback = true; initializeChoreographer(new Runnable(){
@Override
public void run() {
postFrameCallbackOnChoreographer();
}
});
} else {
postFrameCallbackOnChoreographer();
}
} }
} }
public void postFrameCallbackOnChoreographer() {
mChoreographer.postFrameCallback(mReactChoreographerDispatcher);
mHasPostedCallback = true;
}
public void initializeChoreographer(@Nullable final Runnable runnable) {
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
synchronized (ReactChoreographer.class) {
if (mChoreographer == null) {
mChoreographer = ChoreographerCompat.getInstance();
}
}
if (runnable != null) {
runnable.run();
}
}
});
}
public synchronized void removeFrameCallback( public synchronized void removeFrameCallback(
CallbackType type, CallbackType type,
ChoreographerCompat.FrameCallback frameCallback) { ChoreographerCompat.FrameCallback frameCallback) {
@ -117,7 +147,9 @@ public class ReactChoreographer {
private void maybeRemoveFrameCallback() { private void maybeRemoveFrameCallback() {
Assertions.assertCondition(mTotalCallbacks >= 0); Assertions.assertCondition(mTotalCallbacks >= 0);
if (mTotalCallbacks == 0 && mHasPostedCallback) { if (mTotalCallbacks == 0 && mHasPostedCallback) {
mChoreographer.removeFrameCallback(mReactChoreographerDispatcher); if (mChoreographer != null) {
mChoreographer.removeFrameCallback(mReactChoreographerDispatcher);
}
mHasPostedCallback = false; mHasPostedCallback = false;
} }
} }