diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java b/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java
new file mode 100644
index 00000000000..10fa6a6054d
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java
@@ -0,0 +1,349 @@
+package com.facebook.react.modules.core;
+
+import android.util.SparseArray;
+import androidx.annotation.Nullable;
+import com.facebook.react.bridge.Arguments;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.UiThreadUtil;
+import com.facebook.react.bridge.WritableArray;
+import com.facebook.react.common.SystemClock;
+import com.facebook.react.devsupport.interfaces.DevSupportManager;
+import com.facebook.react.jstasks.HeadlessJsTaskContext;
+import java.util.Comparator;
+import java.util.PriorityQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * This class is the native implementation for JS timer execution on Android. It schedules JS timers
+ * to be invoked on frame boundaries using {@link ReactChoreographer}.
+ *
+ *
This is used by the NativeModule {@link TimingModule}.
+ */
+public class JavaTimerManager {
+
+ // These timing constants should be kept in sync with the ones in `JSTimers.js`.
+ // The minimum time in milliseconds left in the frame to call idle callbacks.
+ private static final float IDLE_CALLBACK_FRAME_DEADLINE_MS = 1.f;
+ // The total duration of a frame in milliseconds, this assumes that devices run at 60 fps.
+ // TODO: Lower frame duration on devices that are too slow to run consistently
+ // at 60 fps.
+ private static final float FRAME_DURATION_MS = 1000.f / 60.f;
+
+ private static class Timer {
+ private final int mCallbackID;
+ private final boolean mRepeat;
+ private final int mInterval;
+ private long mTargetTime;
+
+ private Timer(int callbackID, long initialTargetTime, int duration, boolean repeat) {
+ mCallbackID = callbackID;
+ mTargetTime = initialTargetTime;
+ mInterval = duration;
+ mRepeat = repeat;
+ }
+ }
+
+ private class TimerFrameCallback extends ChoreographerCompat.FrameCallback {
+
+ // 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. */
+ @Override
+ public void doFrame(long frameTimeNanos) {
+ if (isPaused.get() && !isRunningTasks.get()) {
+ return;
+ }
+
+ long frameTimeMillis = frameTimeNanos / 1000000;
+ synchronized (mTimerGuard) {
+ while (!mTimers.isEmpty() && mTimers.peek().mTargetTime < frameTimeMillis) {
+ Timer timer = mTimers.poll();
+ if (mTimersToCall == null) {
+ mTimersToCall = Arguments.createArray();
+ }
+ mTimersToCall.pushInt(timer.mCallbackID);
+ if (timer.mRepeat) {
+ timer.mTargetTime = frameTimeMillis + timer.mInterval;
+ mTimers.add(timer);
+ } else {
+ mTimerIdsToTimers.remove(timer.mCallbackID);
+ }
+ }
+ }
+
+ if (mTimersToCall != null) {
+ mReactApplicationContext.getJSModule(JSTimers.class).callTimers(mTimersToCall);
+ mTimersToCall = null;
+ }
+
+ mReactChoreographer.postFrameCallback(ReactChoreographer.CallbackType.TIMERS_EVENTS, this);
+ }
+ }
+
+ private class IdleFrameCallback extends ChoreographerCompat.FrameCallback {
+
+ @Override
+ public void doFrame(long frameTimeNanos) {
+ if (isPaused.get() && !isRunningTasks.get()) {
+ return;
+ }
+
+ // If the JS thread is busy for multiple frames we cancel any other pending runnable.
+ if (mCurrentIdleCallbackRunnable != null) {
+ mCurrentIdleCallbackRunnable.cancel();
+ }
+
+ mCurrentIdleCallbackRunnable = new IdleCallbackRunnable(frameTimeNanos);
+ mReactApplicationContext.runOnJSQueueThread(mCurrentIdleCallbackRunnable);
+
+ mReactChoreographer.postFrameCallback(ReactChoreographer.CallbackType.IDLE_EVENT, this);
+ }
+ }
+
+ private class IdleCallbackRunnable implements Runnable {
+ private volatile boolean mCancelled = false;
+ private final long mFrameStartTime;
+
+ public IdleCallbackRunnable(long frameStartTime) {
+ mFrameStartTime = frameStartTime;
+ }
+
+ @Override
+ public void run() {
+ if (mCancelled) {
+ return;
+ }
+
+ long frameTimeMillis = mFrameStartTime / 1000000;
+ long timeSinceBoot = SystemClock.uptimeMillis();
+ long frameTimeElapsed = timeSinceBoot - frameTimeMillis;
+ long time = SystemClock.currentTimeMillis();
+ long absoluteFrameStartTime = time - frameTimeElapsed;
+
+ if (FRAME_DURATION_MS - (float) frameTimeElapsed < IDLE_CALLBACK_FRAME_DEADLINE_MS) {
+ return;
+ }
+
+ boolean sendIdleEvents;
+ synchronized (mIdleCallbackGuard) {
+ sendIdleEvents = mSendIdleEvents;
+ }
+
+ if (sendIdleEvents) {
+ mReactApplicationContext
+ .getJSModule(JSTimers.class)
+ .callIdleCallbacks(absoluteFrameStartTime);
+ }
+
+ mCurrentIdleCallbackRunnable = null;
+ }
+
+ public void cancel() {
+ mCancelled = true;
+ }
+ }
+
+ private final ReactApplicationContext mReactApplicationContext;
+ private final ReactChoreographer mReactChoreographer;
+ private final DevSupportManager mDevSupportManager;
+ private final Object mTimerGuard = new Object();
+ private final Object mIdleCallbackGuard = new Object();
+ private final PriorityQueue mTimers;
+ private final SparseArray mTimerIdsToTimers;
+ private final AtomicBoolean isPaused = new AtomicBoolean(true);
+ private final AtomicBoolean isRunningTasks = new AtomicBoolean(false);
+ private final TimerFrameCallback mTimerFrameCallback = new TimerFrameCallback();
+ private final IdleFrameCallback mIdleFrameCallback = new IdleFrameCallback();
+ private @Nullable IdleCallbackRunnable mCurrentIdleCallbackRunnable;
+ private boolean mFrameCallbackPosted = false;
+ private boolean mFrameIdleCallbackPosted = false;
+ private boolean mSendIdleEvents = false;
+
+ public JavaTimerManager(
+ ReactApplicationContext reactContext,
+ ReactChoreographer reactChoreographer,
+ DevSupportManager devSupportManager) {
+ mReactApplicationContext = reactContext;
+ mReactChoreographer = reactChoreographer;
+ mDevSupportManager = devSupportManager;
+
+ // We store timers sorted by finish time.
+ mTimers =
+ new PriorityQueue(
+ 11, // Default capacity: for some reason they don't expose a (Comparator) constructor
+ new Comparator() {
+ @Override
+ public int compare(Timer lhs, Timer rhs) {
+ long diff = lhs.mTargetTime - rhs.mTargetTime;
+ if (diff == 0) {
+ return 0;
+ } else if (diff < 0) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+ });
+ mTimerIdsToTimers = new SparseArray<>();
+ }
+
+ public void onHostPause() {
+ isPaused.set(true);
+ clearFrameCallback();
+ maybeIdleCallback();
+ }
+
+ public void onHostDestroy() {
+ clearFrameCallback();
+ maybeIdleCallback();
+ }
+
+ public void onHostResume() {
+ isPaused.set(false);
+ // TODO(5195192) Investigate possible problems related to restarting all tasks at the same
+ // moment
+ setChoreographerCallback();
+ maybeSetChoreographerIdleCallback();
+ }
+
+ public void onHeadlessJsTaskStart(int taskId) {
+ if (!isRunningTasks.getAndSet(true)) {
+ setChoreographerCallback();
+ maybeSetChoreographerIdleCallback();
+ }
+ }
+
+ public void onHeadlessJsTaskFinish(int taskId) {
+ HeadlessJsTaskContext headlessJsTaskContext =
+ HeadlessJsTaskContext.getInstance(mReactApplicationContext);
+ if (!headlessJsTaskContext.hasActiveTasks()) {
+ isRunningTasks.set(false);
+ clearFrameCallback();
+ maybeIdleCallback();
+ }
+ }
+
+ public void onInstanceDestroy() {
+ clearFrameCallback();
+ clearChoreographerIdleCallback();
+ }
+
+ private void maybeSetChoreographerIdleCallback() {
+ synchronized (mIdleCallbackGuard) {
+ if (mSendIdleEvents) {
+ setChoreographerIdleCallback();
+ }
+ }
+ }
+
+ private void maybeIdleCallback() {
+ if (isPaused.get() && !isRunningTasks.get()) {
+ clearFrameCallback();
+ }
+ }
+
+ private void setChoreographerCallback() {
+ if (!mFrameCallbackPosted) {
+ mReactChoreographer.postFrameCallback(
+ ReactChoreographer.CallbackType.TIMERS_EVENTS, mTimerFrameCallback);
+ mFrameCallbackPosted = true;
+ }
+ }
+
+ private void clearFrameCallback() {
+ HeadlessJsTaskContext headlessJsTaskContext =
+ HeadlessJsTaskContext.getInstance(mReactApplicationContext);
+ if (mFrameCallbackPosted && isPaused.get() && !headlessJsTaskContext.hasActiveTasks()) {
+ mReactChoreographer.removeFrameCallback(
+ ReactChoreographer.CallbackType.TIMERS_EVENTS, mTimerFrameCallback);
+ mFrameCallbackPosted = false;
+ }
+ }
+
+ private void setChoreographerIdleCallback() {
+ if (!mFrameIdleCallbackPosted) {
+ mReactChoreographer.postFrameCallback(
+ ReactChoreographer.CallbackType.IDLE_EVENT, mIdleFrameCallback);
+ mFrameIdleCallbackPosted = true;
+ }
+ }
+
+ private void clearChoreographerIdleCallback() {
+ if (mFrameIdleCallbackPosted) {
+ mReactChoreographer.removeFrameCallback(
+ ReactChoreographer.CallbackType.IDLE_EVENT, mIdleFrameCallback);
+ mFrameIdleCallbackPosted = false;
+ }
+ }
+
+ public void createTimer(
+ final int callbackID,
+ final int duration,
+ final double jsSchedulingTime,
+ final boolean repeat) {
+ long deviceTime = SystemClock.currentTimeMillis();
+ long remoteTime = (long) jsSchedulingTime;
+
+ // If the times on the server and device have drifted throw an exception to warn the developer
+ // that things might not work or results may not be accurate. This is required only for
+ // developer builds.
+ if (mDevSupportManager.getDevSupportEnabled()) {
+ long driftTime = Math.abs(remoteTime - deviceTime);
+ if (driftTime > 60000) {
+ mReactApplicationContext
+ .getJSModule(JSTimers.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.");
+ }
+ }
+
+ // Adjust for the amount of time it took for native to receive the timer registration call
+ long adjustedDuration = Math.max(0, remoteTime - deviceTime + duration);
+ if (duration == 0 && !repeat) {
+ WritableArray timerToCall = Arguments.createArray();
+ timerToCall.pushInt(callbackID);
+ mReactApplicationContext.getJSModule(JSTimers.class).callTimers(timerToCall);
+ return;
+ }
+
+ long initialTargetTime = SystemClock.nanoTime() / 1000000 + adjustedDuration;
+ Timer timer = new Timer(callbackID, initialTargetTime, duration, repeat);
+ synchronized (mTimerGuard) {
+ mTimers.add(timer);
+ mTimerIdsToTimers.put(callbackID, timer);
+ }
+ }
+
+ public void deleteTimer(int timerId) {
+ synchronized (mTimerGuard) {
+ Timer timer = mTimerIdsToTimers.get(timerId);
+ if (timer == null) {
+ return;
+ }
+ mTimerIdsToTimers.remove(timerId);
+ mTimers.remove(timer);
+ }
+ }
+
+ public void setSendIdleEvents(final boolean sendIdleEvents) {
+ synchronized (mIdleCallbackGuard) {
+ mSendIdleEvents = sendIdleEvents;
+ }
+
+ UiThreadUtil.runOnUiThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mIdleCallbackGuard) {
+ if (sendIdleEvents) {
+ setChoreographerIdleCallback();
+ } else {
+ clearChoreographerIdleCallback();
+ }
+ }
+ }
+ });
+ }
+}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/core/TimingModule.java b/ReactAndroid/src/main/java/com/facebook/react/modules/core/TimingModule.java
index aa30de03315..2712c49562e 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/modules/core/TimingModule.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/modules/core/TimingModule.java
@@ -6,23 +6,14 @@
*/
package com.facebook.react.modules.core;
-import android.util.SparseArray;
-import androidx.annotation.Nullable;
-import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
-import com.facebook.react.bridge.UiThreadUtil;
-import com.facebook.react.bridge.WritableArray;
-import com.facebook.react.common.SystemClock;
import com.facebook.react.devsupport.interfaces.DevSupportManager;
import com.facebook.react.jstasks.HeadlessJsTaskContext;
import com.facebook.react.jstasks.HeadlessJsTaskEventListener;
import com.facebook.react.module.annotations.ReactModule;
-import java.util.Comparator;
-import java.util.PriorityQueue;
-import java.util.concurrent.atomic.AtomicBoolean;
/** Native module for JS timer execution. Timers fire on frame boundaries. */
@ReactModule(name = TimingModule.NAME)
@@ -31,167 +22,12 @@ public final class TimingModule extends ReactContextBaseJavaModule
public static final String NAME = "Timing";
- // These timing constants should be kept in sync with the ones in `JSTimers.js`.
- // The minimum time in milliseconds left in the frame to call idle callbacks.
- private static final float IDLE_CALLBACK_FRAME_DEADLINE_MS = 1.f;
- // The total duration of a frame in milliseconds, this assumes that devices run at 60 fps.
- // TODO: Lower frame duration on devices that are too slow to run consistently
- // at 60 fps.
- private static final float FRAME_DURATION_MS = 1000.f / 60.f;
-
- private final DevSupportManager mDevSupportManager;
-
- private static class Timer {
- private final int mCallbackID;
- private final boolean mRepeat;
- private final int mInterval;
- private long mTargetTime;
-
- private Timer(int callbackID, long initialTargetTime, int duration, boolean repeat) {
- mCallbackID = callbackID;
- mTargetTime = initialTargetTime;
- mInterval = duration;
- mRepeat = repeat;
- }
- }
-
- private class TimerFrameCallback extends ChoreographerCompat.FrameCallback {
-
- // 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. */
- @Override
- public void doFrame(long frameTimeNanos) {
- if (isPaused.get() && !isRunningTasks.get()) {
- return;
- }
-
- long frameTimeMillis = frameTimeNanos / 1000000;
- synchronized (mTimerGuard) {
- while (!mTimers.isEmpty() && mTimers.peek().mTargetTime < frameTimeMillis) {
- Timer timer = mTimers.poll();
- if (mTimersToCall == null) {
- mTimersToCall = Arguments.createArray();
- }
- mTimersToCall.pushInt(timer.mCallbackID);
- if (timer.mRepeat) {
- timer.mTargetTime = frameTimeMillis + timer.mInterval;
- mTimers.add(timer);
- } else {
- mTimerIdsToTimers.remove(timer.mCallbackID);
- }
- }
- }
-
- if (mTimersToCall != null) {
- getReactApplicationContext().getJSModule(JSTimers.class).callTimers(mTimersToCall);
- mTimersToCall = null;
- }
-
- mReactChoreographer.postFrameCallback(ReactChoreographer.CallbackType.TIMERS_EVENTS, this);
- }
- }
-
- private class IdleFrameCallback extends ChoreographerCompat.FrameCallback {
-
- @Override
- public void doFrame(long frameTimeNanos) {
- if (isPaused.get() && !isRunningTasks.get()) {
- return;
- }
-
- // If the JS thread is busy for multiple frames we cancel any other pending runnable.
- if (mCurrentIdleCallbackRunnable != null) {
- mCurrentIdleCallbackRunnable.cancel();
- }
-
- mCurrentIdleCallbackRunnable = new IdleCallbackRunnable(frameTimeNanos);
- getReactApplicationContext().runOnJSQueueThread(mCurrentIdleCallbackRunnable);
-
- mReactChoreographer.postFrameCallback(ReactChoreographer.CallbackType.IDLE_EVENT, this);
- }
- }
-
- private class IdleCallbackRunnable implements Runnable {
- private volatile boolean mCancelled = false;
- private final long mFrameStartTime;
-
- public IdleCallbackRunnable(long frameStartTime) {
- mFrameStartTime = frameStartTime;
- }
-
- @Override
- public void run() {
- if (mCancelled) {
- return;
- }
-
- long frameTimeMillis = mFrameStartTime / 1000000;
- long timeSinceBoot = SystemClock.uptimeMillis();
- long frameTimeElapsed = timeSinceBoot - frameTimeMillis;
- long time = SystemClock.currentTimeMillis();
- long absoluteFrameStartTime = time - frameTimeElapsed;
-
- if (FRAME_DURATION_MS - (float) frameTimeElapsed < IDLE_CALLBACK_FRAME_DEADLINE_MS) {
- return;
- }
-
- boolean sendIdleEvents;
- synchronized (mIdleCallbackGuard) {
- sendIdleEvents = mSendIdleEvents;
- }
-
- if (sendIdleEvents) {
- getReactApplicationContext()
- .getJSModule(JSTimers.class)
- .callIdleCallbacks(absoluteFrameStartTime);
- }
-
- mCurrentIdleCallbackRunnable = null;
- }
-
- public void cancel() {
- mCancelled = true;
- }
- }
-
- private final Object mTimerGuard = new Object();
- private final Object mIdleCallbackGuard = new Object();
- private final PriorityQueue mTimers;
- private final SparseArray mTimerIdsToTimers;
- private final AtomicBoolean isPaused = new AtomicBoolean(true);
- private final AtomicBoolean isRunningTasks = new AtomicBoolean(false);
- private final TimerFrameCallback mTimerFrameCallback = new TimerFrameCallback();
- private final IdleFrameCallback mIdleFrameCallback = new IdleFrameCallback();
- private final ReactChoreographer mReactChoreographer;
- private @Nullable IdleCallbackRunnable mCurrentIdleCallbackRunnable;
- private boolean mFrameCallbackPosted = false;
- private boolean mFrameIdleCallbackPosted = false;
- private boolean mSendIdleEvents = false;
+ private final JavaTimerManager mJavaTimerManager;
public TimingModule(ReactApplicationContext reactContext, DevSupportManager devSupportManager) {
super(reactContext);
- mDevSupportManager = devSupportManager;
- // We store timers sorted by finish time.
- mTimers =
- new PriorityQueue(
- 11, // Default capacity: for some reason they don't expose a (Comparator) constructor
- new Comparator() {
- @Override
- public int compare(Timer lhs, Timer rhs) {
- long diff = lhs.mTargetTime - rhs.mTargetTime;
- if (diff == 0) {
- return 0;
- } else if (diff < 0) {
- return -1;
- } else {
- return 1;
- }
- }
- });
- mTimerIdsToTimers = new SparseArray<>();
- mReactChoreographer = ReactChoreographer.getInstance();
+ mJavaTimerManager =
+ new JavaTimerManager(reactContext, ReactChoreographer.getInstance(), devSupportManager);
}
@Override
@@ -202,104 +38,6 @@ public final class TimingModule extends ReactContextBaseJavaModule
headlessJsTaskContext.addTaskEventListener(this);
}
- @Override
- public void onHostPause() {
- isPaused.set(true);
- clearFrameCallback();
- maybeIdleCallback();
- }
-
- @Override
- public void onHostDestroy() {
- clearFrameCallback();
- maybeIdleCallback();
- }
-
- @Override
- public void onHostResume() {
- isPaused.set(false);
- // TODO(5195192) Investigate possible problems related to restarting all tasks at the same
- // moment
- setChoreographerCallback();
- maybeSetChoreographerIdleCallback();
- }
-
- @Override
- public void onHeadlessJsTaskStart(int taskId) {
- if (!isRunningTasks.getAndSet(true)) {
- setChoreographerCallback();
- maybeSetChoreographerIdleCallback();
- }
- }
-
- @Override
- public void onHeadlessJsTaskFinish(int taskId) {
- HeadlessJsTaskContext headlessJsTaskContext =
- HeadlessJsTaskContext.getInstance(getReactApplicationContext());
- if (!headlessJsTaskContext.hasActiveTasks()) {
- isRunningTasks.set(false);
- clearFrameCallback();
- maybeIdleCallback();
- }
- }
-
- @Override
- public void onCatalystInstanceDestroy() {
- clearFrameCallback();
- clearChoreographerIdleCallback();
- HeadlessJsTaskContext headlessJsTaskContext =
- HeadlessJsTaskContext.getInstance(getReactApplicationContext());
- headlessJsTaskContext.removeTaskEventListener(this);
- }
-
- private void maybeSetChoreographerIdleCallback() {
- synchronized (mIdleCallbackGuard) {
- if (mSendIdleEvents) {
- setChoreographerIdleCallback();
- }
- }
- }
-
- private void maybeIdleCallback() {
- if (isPaused.get() && !isRunningTasks.get()) {
- clearFrameCallback();
- }
- }
-
- private void setChoreographerCallback() {
- if (!mFrameCallbackPosted) {
- mReactChoreographer.postFrameCallback(
- ReactChoreographer.CallbackType.TIMERS_EVENTS, mTimerFrameCallback);
- mFrameCallbackPosted = true;
- }
- }
-
- private void clearFrameCallback() {
- HeadlessJsTaskContext headlessJsTaskContext =
- HeadlessJsTaskContext.getInstance(getReactApplicationContext());
- if (mFrameCallbackPosted && isPaused.get() && !headlessJsTaskContext.hasActiveTasks()) {
- mReactChoreographer.removeFrameCallback(
- ReactChoreographer.CallbackType.TIMERS_EVENTS, mTimerFrameCallback);
- mFrameCallbackPosted = false;
- }
- }
-
- private void setChoreographerIdleCallback() {
- if (!mFrameIdleCallbackPosted) {
- mReactChoreographer.postFrameCallback(
- ReactChoreographer.CallbackType.IDLE_EVENT, mIdleFrameCallback);
- mFrameIdleCallbackPosted = true;
- }
- }
-
- private void clearChoreographerIdleCallback() {
- if (mFrameIdleCallbackPosted) {
- mReactChoreographer.removeFrameCallback(
- ReactChoreographer.CallbackType.IDLE_EVENT, mIdleFrameCallback);
- mFrameIdleCallbackPosted = false;
- }
- }
-
@Override
public String getName() {
return NAME;
@@ -311,70 +49,49 @@ public final class TimingModule extends ReactContextBaseJavaModule
final int duration,
final double jsSchedulingTime,
final boolean repeat) {
- long deviceTime = SystemClock.currentTimeMillis();
- long remoteTime = (long) jsSchedulingTime;
-
- // If the times on the server and device have drifted throw an exception to warn the developer
- // that things might not work or results may not be accurate. This is required only for
- // developer builds.
- if (mDevSupportManager.getDevSupportEnabled()) {
- long driftTime = Math.abs(remoteTime - deviceTime);
- if (driftTime > 60000) {
- getReactApplicationContext()
- .getJSModule(JSTimers.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.");
- }
- }
-
- // Adjust for the amount of time it took for native to receive the timer registration call
- long adjustedDuration = Math.max(0, remoteTime - deviceTime + duration);
- if (duration == 0 && !repeat) {
- WritableArray timerToCall = Arguments.createArray();
- timerToCall.pushInt(callbackID);
- getReactApplicationContext().getJSModule(JSTimers.class).callTimers(timerToCall);
- return;
- }
-
- long initialTargetTime = SystemClock.nanoTime() / 1000000 + adjustedDuration;
- Timer timer = new Timer(callbackID, initialTargetTime, duration, repeat);
- synchronized (mTimerGuard) {
- mTimers.add(timer);
- mTimerIdsToTimers.put(callbackID, timer);
- }
+ mJavaTimerManager.createTimer(callbackID, duration, jsSchedulingTime, repeat);
}
@ReactMethod
public void deleteTimer(int timerId) {
- synchronized (mTimerGuard) {
- Timer timer = mTimerIdsToTimers.get(timerId);
- if (timer == null) {
- return;
- }
- mTimerIdsToTimers.remove(timerId);
- mTimers.remove(timer);
- }
+ mJavaTimerManager.deleteTimer(timerId);
}
@ReactMethod
public void setSendIdleEvents(final boolean sendIdleEvents) {
- synchronized (mIdleCallbackGuard) {
- mSendIdleEvents = sendIdleEvents;
- }
+ mJavaTimerManager.setSendIdleEvents(sendIdleEvents);
+ }
- UiThreadUtil.runOnUiThread(
- new Runnable() {
- @Override
- public void run() {
- synchronized (mIdleCallbackGuard) {
- if (sendIdleEvents) {
- setChoreographerIdleCallback();
- } else {
- clearChoreographerIdleCallback();
- }
- }
- }
- });
+ @Override
+ public void onHostResume() {
+ mJavaTimerManager.onHostResume();
+ }
+
+ @Override
+ public void onHostPause() {
+ mJavaTimerManager.onHostPause();
+ }
+
+ @Override
+ public void onHostDestroy() {
+ mJavaTimerManager.onHostDestroy();
+ }
+
+ @Override
+ public void onHeadlessJsTaskStart(int taskId) {
+ mJavaTimerManager.onHeadlessJsTaskStart(taskId);
+ }
+
+ @Override
+ public void onHeadlessJsTaskFinish(int taskId) {
+ mJavaTimerManager.onHeadlessJsTaskFinish(taskId);
+ }
+
+ @Override
+ public void onCatalystInstanceDestroy() {
+ HeadlessJsTaskContext headlessJsTaskContext =
+ HeadlessJsTaskContext.getInstance(getReactApplicationContext());
+ headlessJsTaskContext.removeTaskEventListener(this);
+ mJavaTimerManager.onInstanceDestroy();
}
}