diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/core/ChoreographerCompat.java b/ReactAndroid/src/main/java/com/facebook/react/modules/core/ChoreographerCompat.java index a8c159c7b49..98a9eeebd62 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/core/ChoreographerCompat.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/core/ChoreographerCompat.java @@ -13,6 +13,7 @@ import android.os.Build; import android.os.Handler; import android.os.Looper; import android.view.Choreographer; +import com.facebook.react.bridge.UiThreadUtil; /** * 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 boolean IS_JELLYBEAN_OR_HIGHER = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; - private static final ChoreographerCompat INSTANCE = new ChoreographerCompat(); + private static ChoreographerCompat sInstance; private Handler mHandler; private Choreographer mChoreographer; public static ChoreographerCompat getInstance() { - return INSTANCE; + UiThreadUtil.assertOnUiThread(); + if (sInstance == null){ + sInstance = new ChoreographerCompat(); + } + return sInstance; } private ChoreographerCompat() { diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/core/ReactChoreographer.java b/ReactAndroid/src/main/java/com/facebook/react/modules/core/ReactChoreographer.java index 05aa36aa9b1..d937d9eeb0e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/core/ReactChoreographer.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/core/ReactChoreographer.java @@ -7,11 +7,12 @@ package com.facebook.react.modules.core; +import com.facebook.react.bridge.UiThreadUtil; import java.util.ArrayDeque; +import javax.annotation.Nullable; import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; -import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.common.ReactConstants; /** @@ -65,7 +66,6 @@ public class ReactChoreographer { public static void initialize() { if (sInstance == null) { - UiThreadUtil.assertOnUiThread(); sInstance = new ReactChoreographer(); } } @@ -75,7 +75,8 @@ public class ReactChoreographer { 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 ArrayDeque[] mCallbackQueues; @@ -83,12 +84,12 @@ public class ReactChoreographer { private boolean mHasPostedCallback = false; private ReactChoreographer() { - mChoreographer = ChoreographerCompat.getInstance(); mReactChoreographerDispatcher = new ReactChoreographerDispatcher(); mCallbackQueues = new ArrayDeque[CallbackType.values().length]; for (int i = 0; i < mCallbackQueues.length; i++) { mCallbackQueues[i] = new ArrayDeque<>(); } + initializeChoreographer(null); } public synchronized void postFrameCallback( @@ -98,11 +99,40 @@ public class ReactChoreographer { mTotalCallbacks++; Assertions.assertCondition(mTotalCallbacks > 0); if (!mHasPostedCallback) { - mChoreographer.postFrameCallback(mReactChoreographerDispatcher); - mHasPostedCallback = true; + if (mChoreographer == null) { + 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( CallbackType type, ChoreographerCompat.FrameCallback frameCallback) { @@ -117,7 +147,9 @@ public class ReactChoreographer { private void maybeRemoveFrameCallback() { Assertions.assertCondition(mTotalCallbacks >= 0); if (mTotalCallbacks == 0 && mHasPostedCallback) { - mChoreographer.removeFrameCallback(mReactChoreographerDispatcher); + if (mChoreographer != null) { + mChoreographer.removeFrameCallback(mReactChoreographerDispatcher); + } mHasPostedCallback = false; } }