diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/core/JSTimersExecution.java b/ReactAndroid/src/main/java/com/facebook/react/modules/core/JSTimersExecution.java index e8ff7c906..ee1ca14b5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/core/JSTimersExecution.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/core/JSTimersExecution.java @@ -10,10 +10,8 @@ package com.facebook.react.modules.core; import com.facebook.react.bridge.JavaScriptModule; -import com.facebook.react.bridge.SupportsWebWorkers; import com.facebook.react.bridge.WritableArray; -@SupportsWebWorkers public interface JSTimersExecution extends JavaScriptModule { void callTimers(WritableArray timerIDs); void callIdleCallbacks(double frameTime); diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java b/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java index 275f3798c..128a96aa4 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java @@ -11,22 +11,14 @@ package com.facebook.react.modules.core; import javax.annotation.Nullable; -import java.util.ArrayList; import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; import java.util.PriorityQueue; -import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import android.util.SparseArray; import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.ExecutorToken; import com.facebook.react.bridge.LifecycleEventListener; -import com.facebook.react.bridge.OnExecutorUnregisteredListener; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; @@ -41,9 +33,9 @@ import com.facebook.react.module.annotations.ReactModule; /** * Native module for JS timer execution. Timers fire on frame boundaries. */ -@ReactModule(name = Timing.NAME, supportsWebWorkers = true) +@ReactModule(name = Timing.NAME) public final class Timing extends ReactContextBaseJavaModule implements LifecycleEventListener, - OnExecutorUnregisteredListener, HeadlessJsTaskEventListener { + HeadlessJsTaskEventListener { protected static final String NAME = "Timing"; @@ -58,20 +50,16 @@ public final class Timing extends ReactContextBaseJavaModule implements Lifecycl private final DevSupportManager mDevSupportManager; private static class Timer { - - private final ExecutorToken mExecutorToken; private final int mCallbackID; private final boolean mRepeat; private final int mInterval; private long mTargetTime; private Timer( - ExecutorToken executorToken, int callbackID, long initialTargetTime, int duration, boolean repeat) { - mExecutorToken = executorToken; mCallbackID = callbackID; mTargetTime = initialTargetTime; mInterval = duration; @@ -81,8 +69,8 @@ public final class Timing extends ReactContextBaseJavaModule implements Lifecycl private class TimerFrameCallback extends ChoreographerCompat.FrameCallback { - // Temporary map for constructing the individual arrays of timers per ExecutorToken - private final HashMap mTimersToCall = new HashMap<>(); + // Temporary map for constructing the individual arrays of timers to call + private @Nullable WritableArray mTimersToCall = null; /** * Calls all timers that have expired since the last time this frame callback was called. @@ -97,32 +85,23 @@ public final class Timing extends ReactContextBaseJavaModule implements Lifecycl synchronized (mTimerGuard) { while (!mTimers.isEmpty() && mTimers.peek().mTargetTime < frameTimeMillis) { Timer timer = mTimers.poll(); - WritableArray timersForContext = mTimersToCall.get(timer.mExecutorToken); - if (timersForContext == null) { - timersForContext = Arguments.createArray(); - mTimersToCall.put(timer.mExecutorToken, timersForContext); + if (mTimersToCall == null) { + mTimersToCall = Arguments.createArray(); } - timersForContext.pushInt(timer.mCallbackID); + mTimersToCall.pushInt(timer.mCallbackID); if (timer.mRepeat) { timer.mTargetTime = frameTimeMillis + timer.mInterval; mTimers.add(timer); } else { - SparseArray timers = mTimerIdsToTimers.get(timer.mExecutorToken); - if (timers != null) { - timers.remove(timer.mCallbackID); - if (timers.size() == 0) { - mTimerIdsToTimers.remove(timer.mExecutorToken); - } - } + mTimerIdsToTimers.remove(timer.mCallbackID); } } } - for (Map.Entry entry : mTimersToCall.entrySet()) { - getReactApplicationContext().getJSModule(entry.getKey(), JSTimersExecution.class) - .callTimers(entry.getValue()); + if (mTimersToCall != null) { + getReactApplicationContext().getJSModule(JSTimersExecution.class).callTimers(mTimersToCall); + mTimersToCall = null; } - mTimersToCall.clear(); mReactChoreographer.postFrameCallback(ReactChoreographer.CallbackType.TIMERS_EVENTS, this); } @@ -172,13 +151,13 @@ public final class Timing extends ReactContextBaseJavaModule implements Lifecycl return; } - mIdleCallbackContextsToCall.clear(); + boolean sendIdleEvents; synchronized (mIdleCallbackGuard) { - mIdleCallbackContextsToCall.addAll(mSendIdleEventsExecutorTokens); + sendIdleEvents = mSendIdleEvents; } - for (ExecutorToken context : mIdleCallbackContextsToCall) { - getReactApplicationContext().getJSModule(context, JSTimersExecution.class) + if (sendIdleEvents) { + getReactApplicationContext().getJSModule(JSTimersExecution.class) .callIdleCallbacks(absoluteFrameStartTime); } @@ -193,7 +172,7 @@ public final class Timing extends ReactContextBaseJavaModule implements Lifecycl private final Object mTimerGuard = new Object(); private final Object mIdleCallbackGuard = new Object(); private final PriorityQueue mTimers; - private final Map> mTimerIdsToTimers; + private final SparseArray mTimerIdsToTimers; private final AtomicBoolean isPaused = new AtomicBoolean(true); private final AtomicBoolean isRunningTasks = new AtomicBoolean(false); private final TimerFrameCallback mTimerFrameCallback = new TimerFrameCallback(); @@ -202,9 +181,7 @@ public final class Timing extends ReactContextBaseJavaModule implements Lifecycl private @Nullable IdleCallbackRunnable mCurrentIdleCallbackRunnable; private boolean mFrameCallbackPosted = false; private boolean mFrameIdleCallbackPosted = false; - private final Set mSendIdleEventsExecutorTokens; - // Temporary array used to dipatch idle callbacks on the JS thread. - private final List mIdleCallbackContextsToCall; + private boolean mSendIdleEvents = false; public Timing(ReactApplicationContext reactContext, DevSupportManager devSupportManager) { super(reactContext); @@ -225,9 +202,7 @@ public final class Timing extends ReactContextBaseJavaModule implements Lifecycl } } }); - mTimerIdsToTimers = new HashMap<>(); - mSendIdleEventsExecutorTokens = new HashSet<>(); - mIdleCallbackContextsToCall = new ArrayList<>(); + mTimerIdsToTimers = new SparseArray<>(); mReactChoreographer = ReactChoreographer.getInstance(); } @@ -291,7 +266,7 @@ public final class Timing extends ReactContextBaseJavaModule implements Lifecycl private void maybeSetChoreographerIdleCallback() { synchronized (mIdleCallbackGuard) { - if (mSendIdleEventsExecutorTokens.size() > 0) { + if (mSendIdleEvents) { setChoreographerIdleCallback(); } } @@ -347,32 +322,8 @@ public final class Timing extends ReactContextBaseJavaModule implements Lifecycl return NAME; } - @Override - public boolean supportsWebWorkers() { - return true; - } - - @Override - public void onExecutorDestroyed(ExecutorToken executorToken) { - synchronized (mTimerGuard) { - SparseArray timersForContext = mTimerIdsToTimers.remove(executorToken); - if (timersForContext == null) { - return; - } - for (int i = 0; i < timersForContext.size(); i++) { - Timer timer = timersForContext.get(timersForContext.keyAt(i)); - mTimers.remove(timer); - } - } - - synchronized (mIdleCallbackGuard) { - mSendIdleEventsExecutorTokens.remove(executorToken); - } - } - @ReactMethod public void createTimer( - ExecutorToken executorToken, final int callbackID, final int duration, final double jsSchedulingTime, @@ -386,7 +337,7 @@ public final class Timing extends ReactContextBaseJavaModule implements Lifecycl if (mDevSupportManager.getDevSupportEnabled()) { long driftTime = Math.abs(remoteTime - deviceTime); if (driftTime > 60000) { - getReactApplicationContext().getJSModule(executorToken, JSTimersExecution.class) + getReactApplicationContext().getJSModule(JSTimersExecution.class) .emitTimeDriftWarning( "Debugger and device times have drifted by more than 60s. Please correct this by " + "running adb shell \"date `date +%m%d%H%M%Y.%S`\" on your debugger machine."); @@ -398,59 +349,42 @@ public final class Timing extends ReactContextBaseJavaModule implements Lifecycl if (duration == 0 && !repeat) { WritableArray timerToCall = Arguments.createArray(); timerToCall.pushInt(callbackID); - getReactApplicationContext().getJSModule(executorToken, JSTimersExecution.class) + getReactApplicationContext().getJSModule(JSTimersExecution.class) .callTimers(timerToCall); return; } long initialTargetTime = SystemClock.nanoTime() / 1000000 + adjustedDuration; - Timer timer = new Timer(executorToken, callbackID, initialTargetTime, duration, repeat); + Timer timer = new Timer(callbackID, initialTargetTime, duration, repeat); synchronized (mTimerGuard) { mTimers.add(timer); - SparseArray timersForContext = mTimerIdsToTimers.get(executorToken); - if (timersForContext == null) { - timersForContext = new SparseArray<>(); - mTimerIdsToTimers.put(executorToken, timersForContext); - } - timersForContext.put(callbackID, timer); + mTimerIdsToTimers.put(callbackID, timer); } } @ReactMethod - public void deleteTimer(ExecutorToken executorToken, int timerId) { + public void deleteTimer(int timerId) { synchronized (mTimerGuard) { - SparseArray timersForContext = mTimerIdsToTimers.get(executorToken); - if (timersForContext == null) { - return; - } - Timer timer = timersForContext.get(timerId); + Timer timer = mTimerIdsToTimers.get(timerId); if (timer == null) { return; } - // We may have already called/removed it - timersForContext.remove(timerId); - if (timersForContext.size() == 0) { - mTimerIdsToTimers.remove(executorToken); - } + mTimerIdsToTimers.remove(timerId); mTimers.remove(timer); } } @ReactMethod - public void setSendIdleEvents(ExecutorToken executorToken, boolean sendIdleEvents) { + public void setSendIdleEvents(final boolean sendIdleEvents) { synchronized (mIdleCallbackGuard) { - if (sendIdleEvents) { - mSendIdleEventsExecutorTokens.add(executorToken); - } else { - mSendIdleEventsExecutorTokens.remove(executorToken); - } + mSendIdleEvents = sendIdleEvents; } UiThreadUtil.runOnUiThread(new Runnable() { @Override public void run() { synchronized (mIdleCallbackGuard) { - if (mSendIdleEventsExecutorTokens.size() > 0) { + if (sendIdleEvents) { setChoreographerIdleCallback(); } else { clearChoreographerIdleCallback(); diff --git a/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.java b/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.java index 649f906b7..0bb82bfe5 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.java @@ -10,7 +10,6 @@ package com.facebook.react.modules.timing; import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.ExecutorToken; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.CatalystInstance; import com.facebook.react.bridge.JavaOnlyArray; @@ -53,7 +52,6 @@ public class TimingModuleTest { private PostFrameIdleCallbackHandler mIdlePostFrameCallbackHandler; private long mCurrentTimeNs; private JSTimersExecution mJSTimersMock; - private ExecutorToken mExecutorTokenMock; @Rule public PowerMockRule rule = new PowerMockRule(); @@ -100,8 +98,7 @@ public class TimingModuleTest { mTiming = new Timing(reactContext, mock(DevSupportManager.class)); mJSTimersMock = mock(JSTimersExecution.class); - mExecutorTokenMock = mock(ExecutorToken.class); - when(reactContext.getJSModule(mExecutorTokenMock, JSTimersExecution.class)).thenReturn(mJSTimersMock); + when(reactContext.getJSModule(JSTimersExecution.class)).thenReturn(mJSTimersMock); doAnswer(new Answer() { @Override @@ -132,7 +129,7 @@ public class TimingModuleTest { @Test public void testSimpleTimer() { mTiming.onHostResume(); - mTiming.createTimer(mExecutorTokenMock, 1, 1, 0, false); + mTiming.createTimer(1, 1, 0, false); stepChoreographerFrame(); verify(mJSTimersMock).callTimers(JavaOnlyArray.of(1)); reset(mJSTimersMock); @@ -142,7 +139,7 @@ public class TimingModuleTest { @Test public void testSimpleRecurringTimer() { - mTiming.createTimer(mExecutorTokenMock, 100, 1, 0, true); + mTiming.createTimer(100, 1, 0, true); mTiming.onHostResume(); stepChoreographerFrame(); verify(mJSTimersMock).callTimers(JavaOnlyArray.of(100)); @@ -155,13 +152,13 @@ public class TimingModuleTest { @Test public void testCancelRecurringTimer() { mTiming.onHostResume(); - mTiming.createTimer(mExecutorTokenMock, 105, 1, 0, true); + mTiming.createTimer(105, 1, 0, true); stepChoreographerFrame(); verify(mJSTimersMock).callTimers(JavaOnlyArray.of(105)); reset(mJSTimersMock); - mTiming.deleteTimer(mExecutorTokenMock, 105); + mTiming.deleteTimer(105); stepChoreographerFrame(); verifyNoMoreInteractions(mJSTimersMock); } @@ -169,7 +166,7 @@ public class TimingModuleTest { @Test public void testPausingAndResuming() { mTiming.onHostResume(); - mTiming.createTimer(mExecutorTokenMock, 41, 1, 0, true); + mTiming.createTimer(41, 1, 0, true); stepChoreographerFrame(); verify(mJSTimersMock).callTimers(JavaOnlyArray.of(41)); @@ -189,7 +186,7 @@ public class TimingModuleTest { public void testHeadlessJsTaskInBackground() { mTiming.onHostPause(); mTiming.onHeadlessJsTaskStart(42); - mTiming.createTimer(mExecutorTokenMock, 41, 1, 0, true); + mTiming.createTimer(41, 1, 0, true); stepChoreographerFrame(); verify(mJSTimersMock).callTimers(JavaOnlyArray.of(41)); @@ -204,7 +201,7 @@ public class TimingModuleTest { public void testHeadlessJsTaskInForeground() { mTiming.onHostResume(); mTiming.onHeadlessJsTaskStart(42); - mTiming.createTimer(mExecutorTokenMock, 41, 1, 0, true); + mTiming.createTimer(41, 1, 0, true); stepChoreographerFrame(); verify(mJSTimersMock).callTimers(JavaOnlyArray.of(41)); @@ -223,7 +220,7 @@ public class TimingModuleTest { public void testHeadlessJsTaskIntertwine() { mTiming.onHostResume(); mTiming.onHeadlessJsTaskStart(42); - mTiming.createTimer(mExecutorTokenMock, 41, 1, 0, true); + mTiming.createTimer(41, 1, 0, true); mTiming.onHostPause(); stepChoreographerFrame(); @@ -243,14 +240,14 @@ public class TimingModuleTest { @Test public void testSetTimeoutZero() { - mTiming.createTimer(mExecutorTokenMock, 100, 0, 0, false); + mTiming.createTimer(100, 0, 0, false); verify(mJSTimersMock).callTimers(JavaOnlyArray.of(100)); } @Test public void testIdleCallback() { mTiming.onHostResume(); - mTiming.setSendIdleEvents(mExecutorTokenMock, true); + mTiming.setSendIdleEvents(true); stepChoreographerFrame(); verify(mJSTimersMock).callIdleCallbacks(SystemClock.currentTimeMillis());