mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-10 22:47:58 +08:00
Initial implementation of requestIdleCallback on Android
Summary: This is a follow up of the work by brentvatne in #5052. This addresses the feedback by astreet. - Uses ReactChoreographer with a new callback type - Callback dispatch logic moved to JS - Only calls into JS when needed, when there are pending callbacks, it even removes the Choreographer listener when no JS context listen for idle events. ** Test plan ** Tested by running a background task that burns all remaining idle time (see new UIExplorer example) and made sure that UI and JS fps stayed near 60 on a real device (Nexus 6) with dev mode disabled. Also tried adding a JS driven animation and it stayed smooth. Tested that native only calls into JS when there are pending idle callbacks. Also tested that timers are executed before idle callback. ``` requestIdleCallback(() => console.log(1)); setTimeout(() => console.log(2), 100); burnCPU(1000); // 2 // 1 ``` I did *not* test with webworkers but it should work as I'm using executor tokens. Closes https://github.com/facebook/react-native/pull/8569 Differential Revision: D3558869 Pulled By: astreet fbshipit-source-id: 61fa82eb26001d2b8c2ea69c35bf3eb5ce5454ba
This commit is contained in:
committed by
Facebook Github Bot 5
parent
22eabe59a2
commit
18394fb179
@@ -52,8 +52,9 @@ public class TimingModuleTest {
|
||||
private static final long FRAME_TIME_NS = 17 * 1000 * 1000; // 17 ms
|
||||
|
||||
private Timing mTiming;
|
||||
private ReactChoreographer mChoreographerMock;
|
||||
private ReactChoreographer mReactChoreographerMock;
|
||||
private PostFrameCallbackHandler mPostFrameCallbackHandler;
|
||||
private PostFrameIdleCallbackHandler mIdlePostFrameCallbackHandler;
|
||||
private long mCurrentTimeNs;
|
||||
private JSTimersExecution mJSTimersMock;
|
||||
private ExecutorToken mExecutorTokenMock;
|
||||
@@ -73,12 +74,13 @@ public class TimingModuleTest {
|
||||
});
|
||||
|
||||
PowerMockito.mockStatic(SystemClock.class);
|
||||
when(SystemClock.uptimeMillis()).thenReturn(mCurrentTimeNs / 1000000);
|
||||
when(SystemClock.currentTimeMillis()).thenReturn(mCurrentTimeNs / 1000000);
|
||||
when(SystemClock.nanoTime()).thenReturn(mCurrentTimeNs);
|
||||
|
||||
mChoreographerMock = mock(ReactChoreographer.class);
|
||||
mReactChoreographerMock = mock(ReactChoreographer.class);
|
||||
PowerMockito.mockStatic(ReactChoreographer.class);
|
||||
when(ReactChoreographer.getInstance()).thenReturn(mChoreographerMock);
|
||||
when(ReactChoreographer.getInstance()).thenReturn(mReactChoreographerMock);
|
||||
|
||||
CatalystInstance reactInstance = mock(CatalystInstance.class);
|
||||
ReactApplicationContext reactContext = mock(ReactApplicationContext.class);
|
||||
@@ -86,26 +88,49 @@ public class TimingModuleTest {
|
||||
|
||||
mCurrentTimeNs = 0;
|
||||
mPostFrameCallbackHandler = new PostFrameCallbackHandler();
|
||||
mIdlePostFrameCallbackHandler = new PostFrameIdleCallbackHandler();
|
||||
|
||||
doAnswer(mPostFrameCallbackHandler)
|
||||
.when(mChoreographerMock)
|
||||
.when(mReactChoreographerMock)
|
||||
.postFrameCallback(
|
||||
eq(ReactChoreographer.CallbackType.TIMERS_EVENTS),
|
||||
any(Choreographer.FrameCallback.class));
|
||||
|
||||
doAnswer(mIdlePostFrameCallbackHandler)
|
||||
.when(mReactChoreographerMock)
|
||||
.postFrameCallback(
|
||||
eq(ReactChoreographer.CallbackType.IDLE_EVENT),
|
||||
any(Choreographer.FrameCallback.class));
|
||||
|
||||
mTiming = new Timing(reactContext, mock(DevSupportManager.class));
|
||||
mJSTimersMock = mock(JSTimersExecution.class);
|
||||
mExecutorTokenMock = mock(ExecutorToken.class);
|
||||
when(reactContext.getJSModule(mExecutorTokenMock, JSTimersExecution.class)).thenReturn(mJSTimersMock);
|
||||
|
||||
doAnswer(new Answer() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
((Runnable)invocation.getArguments()[0]).run();
|
||||
return null;
|
||||
}
|
||||
}).when(reactContext).runOnJSQueueThread(any(Runnable.class));
|
||||
|
||||
mTiming.initialize();
|
||||
}
|
||||
|
||||
private void stepChoreographerFrame() {
|
||||
Choreographer.FrameCallback callback = mPostFrameCallbackHandler.getAndResetFrameCallback();
|
||||
Choreographer.FrameCallback idleCallback = mIdlePostFrameCallbackHandler.getAndResetFrameCallback();
|
||||
|
||||
mCurrentTimeNs += FRAME_TIME_NS;
|
||||
when(SystemClock.uptimeMillis()).thenReturn(mCurrentTimeNs / 1000000);
|
||||
if (callback != null) {
|
||||
callback.doFrame(mCurrentTimeNs);
|
||||
}
|
||||
|
||||
if (idleCallback != null) {
|
||||
idleCallback.doFrame(mCurrentTimeNs);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -170,6 +195,33 @@ public class TimingModuleTest {
|
||||
verify(mJSTimersMock).callTimers(JavaOnlyArray.of(100));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIdleCallback() {
|
||||
mTiming.onHostResume();
|
||||
mTiming.setSendIdleEvents(mExecutorTokenMock, true);
|
||||
|
||||
stepChoreographerFrame();
|
||||
verify(mJSTimersMock).callIdleCallbacks(SystemClock.currentTimeMillis());
|
||||
}
|
||||
|
||||
private static class PostFrameIdleCallbackHandler implements Answer<Void> {
|
||||
|
||||
private Choreographer.FrameCallback mFrameCallback;
|
||||
|
||||
@Override
|
||||
public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||
Object[] args = invocation.getArguments();
|
||||
mFrameCallback = (Choreographer.FrameCallback) args[1];
|
||||
return null;
|
||||
}
|
||||
|
||||
public Choreographer.FrameCallback getAndResetFrameCallback() {
|
||||
Choreographer.FrameCallback callback = mFrameCallback;
|
||||
mFrameCallback = null;
|
||||
return callback;
|
||||
}
|
||||
}
|
||||
|
||||
private static class PostFrameCallbackHandler implements Answer<Void> {
|
||||
|
||||
private Choreographer.FrameCallback mFrameCallback;
|
||||
|
||||
Reference in New Issue
Block a user