support api 15 (use Handler-backed ui driven).

Summary:
Android API 15 still have 1.5~2.0% distribution (refer: [Dashboard - Android Developer](https://developer.android.com/ndk/guides/standalone_toolchain.html#creating_the_toolchain)).

React Native is a good tec but many companies cannot endure loose their consumer. [Choreographer](https://developer.android.com/reference/android/view/Choreographer.html) triggered UI operation is the only reason that React Native Android sdk use minSdkVersion 16, so we can use a backward solution **only in API 15**: [Handler](https://developer.android.com/reference/android/os/Handler.html).

In this PR, the biggest change is :

- Make core operation of ReactChoreographer to an interface: ReactUIDriver;
- Impl ReactUIDriver by Handler => UIDriverHandlerImpl, refactor ReactChoreographer to UIDriverChoreographerImpl;
- Let UIDriverFactory to choose which one impl would be in use. (Only use handler in api 15).
Closes https://github.com/facebook/react-native/pull/12396

Reviewed By: AaaChiuuu

Differential Revision: D4588399

Pulled By: astreet

fbshipit-source-id: 76408e53664314dd926e6a553cde6bafbd37779e
This commit is contained in:
desmondyao 2017-02-27 04:20:51 -08:00 committed by Facebook Github Bot
parent 8b01508410
commit 20ad2b3fbb
18 changed files with 260 additions and 149 deletions

View File

@ -14,10 +14,10 @@ import java.util.concurrent.TimeUnit;
import android.app.Instrumentation;
import android.os.SystemClock;
import android.support.test.InstrumentationRegistry;
import android.view.Choreographer;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.modules.core.ChoreographerCompat;
public class ReactIdleDetectionUtil {
@ -56,8 +56,8 @@ public class ReactIdleDetectionUtil {
new Runnable() {
@Override
public void run() {
Choreographer.getInstance().postFrameCallback(
new Choreographer.FrameCallback() {
ChoreographerCompat.getInstance().postFrameCallback(
new ChoreographerCompat.FrameCallback() {
private int frameCount = 0;
@ -67,7 +67,7 @@ public class ReactIdleDetectionUtil {
if (frameCount == waitFrameCount) {
latch.countDown();
} else {
Choreographer.getInstance().postFrameCallback(this);
ChoreographerCompat.getInstance().postFrameCallback(this);
}
}
});

View File

@ -23,8 +23,8 @@ import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.uimanager.GuardedChoreographerFrameCallback;
import com.facebook.react.modules.core.ReactChoreographer;
import com.facebook.react.uimanager.GuardedFrameCallback;
import com.facebook.react.uimanager.UIManagerModule;
import java.util.ArrayList;
@ -79,7 +79,7 @@ public class NativeAnimatedModule extends ReactContextBaseJavaModule implements
}
private final Object mOperationsCopyLock = new Object();
private @Nullable GuardedChoreographerFrameCallback mAnimatedFrameCallback;
private @Nullable GuardedFrameCallback mAnimatedFrameCallback;
private @Nullable ReactChoreographer mReactChoreographer;
private ArrayList<UIThreadOperation> mOperations = new ArrayList<>();
private volatile @Nullable ArrayList<UIThreadOperation> mReadyOperations = null;
@ -97,7 +97,7 @@ public class NativeAnimatedModule extends ReactContextBaseJavaModule implements
UIManagerModule uiManager = reactCtx.getNativeModule(UIManagerModule.class);
final NativeAnimatedNodesManager nodesManager = new NativeAnimatedNodesManager(uiManager);
mAnimatedFrameCallback = new GuardedChoreographerFrameCallback(reactCtx) {
mAnimatedFrameCallback = new GuardedFrameCallback(reactCtx) {
@Override
protected void doFrameGuarded(final long frameTimeNanos) {

View File

@ -19,6 +19,7 @@ android_library(
react_native_target("java/com/facebook/react/common/network:network"),
react_native_target("java/com/facebook/react/devsupport:interfaces"),
react_native_target("java/com/facebook/react/module/annotations:annotations"),
react_native_target('java/com/facebook/react/modules/core:core'),
react_native_target("java/com/facebook/react/modules/debug:debug"),
react_native_target("java/com/facebook/react/modules/debug:interfaces"),
react_native_target("java/com/facebook/react/modules/systeminfo:systeminfo"),

View File

@ -12,7 +12,6 @@ package com.facebook.react.devsupport;
import java.util.Locale;
import android.annotation.TargetApi;
import android.view.Choreographer;
import android.widget.FrameLayout;
import android.widget.TextView;
@ -20,6 +19,7 @@ import com.facebook.common.logging.FLog;
import com.facebook.react.R;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.modules.core.ChoreographerCompat;
import com.facebook.react.modules.debug.FpsDebugFrameCallback;
/**
@ -41,7 +41,7 @@ public class FpsView extends FrameLayout {
super(reactContext);
inflate(reactContext, R.layout.fps_view, this);
mTextView = (TextView) findViewById(R.id.fps_text);
mFrameCallback = new FpsDebugFrameCallback(Choreographer.getInstance(), reactContext);
mFrameCallback = new FpsDebugFrameCallback(ChoreographerCompat.getInstance(), reactContext);
mFPSMonitorRunnable = new FPSMonitorRunnable();
setCurrentFPS(0, 0, 0, 0);
}

View File

@ -0,0 +1,131 @@
/*
* Copyright (c) 2013, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* This file was pulled from the facebook/rebound repository.
*/
package com.facebook.react.modules.core;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.view.Choreographer;
/**
* Wrapper class for abstracting away availability of the JellyBean Choreographer. If Choreographer
* is unavailable we fallback to using a normal Handler.
*/
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 Handler mHandler;
private Choreographer mChoreographer;
public static ChoreographerCompat getInstance() {
return INSTANCE;
}
private ChoreographerCompat() {
if (IS_JELLYBEAN_OR_HIGHER) {
mChoreographer = getChoreographer();
} else {
mHandler = new Handler(Looper.getMainLooper());
}
}
public void postFrameCallback(FrameCallback callbackWrapper) {
if (IS_JELLYBEAN_OR_HIGHER) {
choreographerPostFrameCallback(callbackWrapper.getFrameCallback());
} else {
mHandler.postDelayed(callbackWrapper.getRunnable(), 0);
}
}
public void postFrameCallbackDelayed(FrameCallback callbackWrapper, long delayMillis) {
if (IS_JELLYBEAN_OR_HIGHER) {
choreographerPostFrameCallbackDelayed(callbackWrapper.getFrameCallback(), delayMillis);
} else {
mHandler.postDelayed(callbackWrapper.getRunnable(), delayMillis + ONE_FRAME_MILLIS);
}
}
public void removeFrameCallback(FrameCallback callbackWrapper) {
if (IS_JELLYBEAN_OR_HIGHER) {
choreographerRemoveFrameCallback(callbackWrapper.getFrameCallback());
} else {
mHandler.removeCallbacks(callbackWrapper.getRunnable());
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private Choreographer getChoreographer() {
return Choreographer.getInstance();
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void choreographerPostFrameCallback(Choreographer.FrameCallback frameCallback) {
mChoreographer.postFrameCallback(frameCallback);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void choreographerPostFrameCallbackDelayed(
Choreographer.FrameCallback frameCallback,
long delayMillis) {
mChoreographer.postFrameCallbackDelayed(frameCallback, delayMillis);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void choreographerRemoveFrameCallback(Choreographer.FrameCallback frameCallback) {
mChoreographer.removeFrameCallback(frameCallback);
}
/**
* This class provides a compatibility wrapper around the JellyBean FrameCallback with methods
* to access cached wrappers for submitting a real FrameCallback to a Choreographer or a Runnable
* to a Handler.
*/
public static abstract class FrameCallback {
private Runnable mRunnable;
private Choreographer.FrameCallback mFrameCallback;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Choreographer.FrameCallback getFrameCallback() {
if (mFrameCallback == null) {
mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
FrameCallback.this.doFrame(frameTimeNanos);
}
};
}
return mFrameCallback;
}
Runnable getRunnable() {
if (mRunnable == null) {
mRunnable = new Runnable() {
@Override
public void run() {
doFrame(System.nanoTime());
}
};
}
return mRunnable;
}
/**
* Just a wrapper for frame callback, see {@link android.view.Choreographer.FrameCallback#doFrame(long)}.
*/
public abstract void doFrame(long frameTimeNanos);
}
}

View File

@ -11,8 +11,6 @@ package com.facebook.react.modules.core;
import java.util.ArrayDeque;
import android.view.Choreographer;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.infer.annotation.Assertions;
@ -25,7 +23,7 @@ import com.facebook.react.common.ReactConstants;
*/
public class ReactChoreographer {
public static enum CallbackType {
public enum CallbackType {
/**
* For use by perf markers that need to happen immediately after draw
@ -75,15 +73,15 @@ public class ReactChoreographer {
return sInstance;
}
private final Choreographer mChoreographer;
private final ChoreographerCompat mChoreographer;
private final ReactChoreographerDispatcher mReactChoreographerDispatcher;
private final ArrayDeque<Choreographer.FrameCallback>[] mCallbackQueues;
private final ArrayDeque<ChoreographerCompat.FrameCallback>[] mCallbackQueues;
private int mTotalCallbacks = 0;
private boolean mHasPostedCallback = false;
private ReactChoreographer() {
mChoreographer = Choreographer.getInstance();
mChoreographer = ChoreographerCompat.getInstance();
mReactChoreographerDispatcher = new ReactChoreographerDispatcher();
mCallbackQueues = new ArrayDeque[CallbackType.values().length];
for (int i = 0; i < mCallbackQueues.length; i++) {
@ -91,7 +89,7 @@ public class ReactChoreographer {
}
}
public void postFrameCallback(CallbackType type, Choreographer.FrameCallback frameCallback) {
public void postFrameCallback(CallbackType type, ChoreographerCompat.FrameCallback frameCallback) {
UiThreadUtil.assertOnUiThread();
mCallbackQueues[type.getOrder()].addLast(frameCallback);
mTotalCallbacks++;
@ -102,7 +100,7 @@ public class ReactChoreographer {
}
}
public void removeFrameCallback(CallbackType type, Choreographer.FrameCallback frameCallback) {
public void removeFrameCallback(CallbackType type, ChoreographerCompat.FrameCallback frameCallback) {
UiThreadUtil.assertOnUiThread();
if (mCallbackQueues[type.getOrder()].removeFirstOccurrence(frameCallback)) {
mTotalCallbacks--;
@ -120,7 +118,7 @@ public class ReactChoreographer {
}
}
private class ReactChoreographerDispatcher implements Choreographer.FrameCallback {
private class ReactChoreographerDispatcher extends ChoreographerCompat.FrameCallback {
@Override
public void doFrame(long frameTimeNanos) {

View File

@ -22,7 +22,6 @@ import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import android.util.SparseArray;
import android.view.Choreographer;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.Arguments;
@ -81,7 +80,7 @@ public final class Timing extends ReactContextBaseJavaModule implements Lifecycl
}
}
private class TimerFrameCallback implements Choreographer.FrameCallback {
private class TimerFrameCallback extends ChoreographerCompat.FrameCallback {
// Temporary map for constructing the individual arrays of timers per ExecutorToken
private final HashMap<ExecutorToken, WritableArray> mTimersToCall = new HashMap<>();
@ -131,7 +130,7 @@ public final class Timing extends ReactContextBaseJavaModule implements Lifecycl
}
}
private class IdleFrameCallback implements Choreographer.FrameCallback {
private class IdleFrameCallback extends ChoreographerCompat.FrameCallback {
@Override
public void doFrame(long frameTimeNanos) {
@ -248,14 +247,14 @@ public final class Timing extends ReactContextBaseJavaModule implements Lifecycl
@Override
public void onHostPause() {
isPaused.set(true);
clearChoreographerCallback();
maybeClearChoreographerIdleCallback();
clearFrameCallback();
maybeIdleCallback();
}
@Override
public void onHostDestroy() {
clearChoreographerCallback();
maybeClearChoreographerIdleCallback();
clearFrameCallback();
maybeIdleCallback();
}
@Override
@ -281,14 +280,14 @@ public final class Timing extends ReactContextBaseJavaModule implements Lifecycl
HeadlessJsTaskContext.getInstance(getReactApplicationContext());
if (!headlessJsTaskContext.hasActiveTasks()) {
isRunningTasks.set(false);
clearChoreographerCallback();
maybeClearChoreographerIdleCallback();
clearFrameCallback();
maybeIdleCallback();
}
}
@Override
public void onCatalystInstanceDestroy() {
clearChoreographerCallback();
clearFrameCallback();
clearChoreographerIdleCallback();
HeadlessJsTaskContext headlessJsTaskContext =
HeadlessJsTaskContext.getInstance(getReactApplicationContext());
@ -303,9 +302,9 @@ public final class Timing extends ReactContextBaseJavaModule implements Lifecycl
}
}
private void maybeClearChoreographerIdleCallback() {
private void maybeIdleCallback() {
if (isPaused.get() && !isRunningTasks.get()) {
clearChoreographerCallback();
clearFrameCallback();
}
}
@ -318,7 +317,7 @@ public final class Timing extends ReactContextBaseJavaModule implements Lifecycl
}
}
private void clearChoreographerCallback() {
private void clearFrameCallback() {
HeadlessJsTaskContext headlessJsTaskContext =
HeadlessJsTaskContext.getInstance(getReactApplicationContext());
if (mFrameCallbackPosted && isPaused.get() &&

View File

@ -13,8 +13,6 @@ import javax.annotation.Nullable;
import java.util.Locale;
import android.os.Build;
import android.view.Choreographer;
import android.widget.Toast;
import com.facebook.common.logging.FLog;
@ -24,6 +22,7 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.core.ChoreographerCompat;
import com.facebook.react.modules.debug.interfaces.DeveloperSettings;
/**
@ -60,10 +59,9 @@ public class AnimationsDebugModule extends ReactContextBaseJavaModule {
if (mFrameCallback != null) {
throw new JSApplicationCausedNativeException("Already recording FPS!");
}
checkAPILevel();
mFrameCallback = new FpsDebugFrameCallback(
Choreographer.getInstance(),
ChoreographerCompat.getInstance(),
getReactApplicationContext());
mFrameCallback.startAndRecordFpsAtEachFrame();
}
@ -78,7 +76,6 @@ public class AnimationsDebugModule extends ReactContextBaseJavaModule {
if (mFrameCallback == null) {
return;
}
checkAPILevel();
mFrameCallback.stop();
@ -116,11 +113,4 @@ public class AnimationsDebugModule extends ReactContextBaseJavaModule {
mFrameCallback = null;
}
}
private static void checkAPILevel() {
if (Build.VERSION.SDK_INT < 16) {
throw new JSApplicationCausedNativeException(
"Animation debugging is not supported in API <16");
}
}
}

View File

@ -13,6 +13,7 @@ android_library(
react_native_target("java/com/facebook/react/bridge:bridge"),
react_native_target("java/com/facebook/react/common:common"),
react_native_target("java/com/facebook/react/module/annotations:annotations"),
react_native_target('java/com/facebook/react/modules/core:core'),
react_native_target("java/com/facebook/react/modules/debug:interfaces"),
react_native_target("java/com/facebook/react/uimanager:uimanager"),
],

View File

@ -9,11 +9,10 @@
package com.facebook.react.modules.debug;
import android.view.Choreographer;
import com.facebook.react.bridge.ReactBridge;
import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener;
import com.facebook.react.common.LongArray;
import com.facebook.react.modules.core.ChoreographerCompat;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.debug.NotThreadSafeViewHierarchyUpdateDebugListener;
@ -22,7 +21,7 @@ import com.facebook.react.uimanager.debug.NotThreadSafeViewHierarchyUpdateDebugL
* to calculate whether JS was able to update the UI during a given frame. After being installed
* on a {@link ReactBridge} and a {@link UIManagerModule},
* {@link #getDidJSHitFrameAndCleanup} should be called once per frame via a
* {@link Choreographer.FrameCallback}.
* {@link ChoreographerCompat.FrameCallback}.
*/
public class DidJSUpdateUiDuringFrameDetector implements NotThreadSafeBridgeIdleDebugListener,
NotThreadSafeViewHierarchyUpdateDebugListener {
@ -56,7 +55,7 @@ public class DidJSUpdateUiDuringFrameDetector implements NotThreadSafeBridgeIdle
}
/**
* Designed to be called from a {@link Choreographer.FrameCallback#doFrame} call.
* Designed to be called from a {@link ChoreographerCompat.FrameCallback#doFrame} call.
*
* There are two 'success' cases that will cause {@link #getDidJSHitFrameAndCleanup} to
* return true for a given frame:

View File

@ -14,10 +14,8 @@ import javax.annotation.Nullable;
import java.util.Map;
import java.util.TreeMap;
import android.annotation.TargetApi;
import android.view.Choreographer;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.core.ChoreographerCompat;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.infer.annotation.Assertions;
@ -30,11 +28,8 @@ import com.facebook.infer.annotation.Assertions;
* Also records the JS FPS, i.e. the frames per second with which either JS updated the UI or was
* idle and not trying to update the UI. This is different from the FPS above since JS rendering is
* async.
*
* TargetApi 16 for use of Choreographer.
*/
@TargetApi(16)
public class FpsDebugFrameCallback implements Choreographer.FrameCallback {
public class FpsDebugFrameCallback extends ChoreographerCompat.FrameCallback {
public static class FpsInfo {
@ -66,7 +61,7 @@ public class FpsDebugFrameCallback implements Choreographer.FrameCallback {
private static final double EXPECTED_FRAME_TIME = 16.9;
private final Choreographer mChoreographer;
private final ChoreographerCompat mChoreographer;
private final ReactContext mReactContext;
private final UIManagerModule mUIManagerModule;
private final DidJSUpdateUiDuringFrameDetector mDidJSUpdateUiDuringFrameDetector;
@ -81,7 +76,7 @@ public class FpsDebugFrameCallback implements Choreographer.FrameCallback {
private boolean mIsRecordingFpsInfoAtEachFrame = false;
private @Nullable TreeMap<Long, FpsInfo> mTimeToFps;
public FpsDebugFrameCallback(Choreographer choreographer, ReactContext reactContext) {
public FpsDebugFrameCallback(ChoreographerCompat choreographer, ReactContext reactContext) {
mChoreographer = choreographer;
mReactContext = reactContext;
mUIManagerModule = reactContext.getNativeModule(UIManagerModule.class);

View File

@ -9,20 +9,19 @@
package com.facebook.react.uimanager;
import android.view.Choreographer;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.core.ChoreographerCompat;
/**
* Abstract base for a Choreographer FrameCallback that should have any RuntimeExceptions it throws
* handled by the {@link com.facebook.react.bridge.NativeModuleCallExceptionHandler} registered if
* the app is in dev mode.
*/
public abstract class GuardedChoreographerFrameCallback implements Choreographer.FrameCallback {
public abstract class GuardedFrameCallback extends ChoreographerCompat.FrameCallback {
private final ReactContext mReactContext;
protected GuardedChoreographerFrameCallback(ReactContext reactContext) {
protected GuardedFrameCallback(ReactContext reactContext) {
mReactContext = reactContext;
}

View File

@ -722,8 +722,7 @@ public class UIViewOperationQueue {
final int reactTag,
final Callback callback) {
mOperations.add(
new MeasureInWindowOperation(reactTag, callback)
);
new MeasureInWindowOperation(reactTag, callback));
}
public void enqueueFindTargetForTouch(
@ -862,7 +861,7 @@ public class UIViewOperationQueue {
* Using a Choreographer callback (which runs immediately before traversals), we guarantee we run
* before the next traversal.
*/
private class DispatchUIFrameCallback extends GuardedChoreographerFrameCallback {
private class DispatchUIFrameCallback extends GuardedFrameCallback {
private static final int MIN_TIME_LEFT_IN_FRAME_TO_SCHEDULE_MORE_WORK_MS = 8;
private static final int FRAME_TIME_MS = 16;

View File

@ -17,13 +17,13 @@ import java.util.Comparator;
import java.util.Map;
import android.util.LongSparseArray;
import android.view.Choreographer;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.modules.core.ChoreographerCompat;
import com.facebook.react.modules.core.ReactChoreographer;
import com.facebook.systrace.Systrace;
@ -251,7 +251,7 @@ public class EventDispatcher implements LifecycleEventListener {
(((long) coalescingKey) & 0xffff) << 48;
}
private class ScheduleDispatchFrameCallback implements Choreographer.FrameCallback {
private class ScheduleDispatchFrameCallback extends ChoreographerCompat.FrameCallback {
private volatile boolean mIsPosted = false;
private boolean mShouldStop = false;

View File

@ -9,8 +9,6 @@
package com.facebook.react.modules.timing;
import android.view.Choreographer;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ExecutorToken;
import com.facebook.react.bridge.ReactApplicationContext;
@ -18,6 +16,7 @@ import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.JavaOnlyArray;
import com.facebook.react.devsupport.interfaces.DevSupportManager;
import com.facebook.react.common.SystemClock;
import com.facebook.react.modules.core.ChoreographerCompat;
import com.facebook.react.modules.core.JSTimersExecution;
import com.facebook.react.modules.core.ReactChoreographer;
import com.facebook.react.modules.core.Timing;
@ -39,8 +38,8 @@ import static org.mockito.Mockito.*;
/**
* Tests for {@link Timing}.
*/
// DISABLED, BROKEN https://circleci.com/gh/facebook/react-native/12068
// t=13905097
// DISABLED, BROKEN https://circleci.com/gh/facebook/react-native/12068
// t=13905097
@PrepareForTest({Arguments.class, SystemClock.class, ReactChoreographer.class})
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"})
@RunWith(RobolectricTestRunner.class)
@ -63,12 +62,12 @@ public class TimingModuleTest {
public void prepareModules() {
PowerMockito.mockStatic(Arguments.class);
when(Arguments.createArray()).thenAnswer(
new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return new JavaOnlyArray();
}
});
new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return new JavaOnlyArray();
}
});
PowerMockito.mockStatic(SystemClock.class);
when(SystemClock.uptimeMillis()).thenReturn(mCurrentTimeNs / 1000000);
@ -88,16 +87,16 @@ public class TimingModuleTest {
mIdlePostFrameCallbackHandler = new PostFrameIdleCallbackHandler();
doAnswer(mPostFrameCallbackHandler)
.when(mReactChoreographerMock)
.postFrameCallback(
eq(ReactChoreographer.CallbackType.TIMERS_EVENTS),
any(Choreographer.FrameCallback.class));
.when(mReactChoreographerMock)
.postFrameCallback(
eq(ReactChoreographer.CallbackType.TIMERS_EVENTS),
any(ChoreographerCompat.FrameCallback.class));
doAnswer(mIdlePostFrameCallbackHandler)
.when(mReactChoreographerMock)
.postFrameCallback(
eq(ReactChoreographer.CallbackType.IDLE_EVENT),
any(Choreographer.FrameCallback.class));
.when(mReactChoreographerMock)
.postFrameCallback(
eq(ReactChoreographer.CallbackType.IDLE_EVENT),
any(ChoreographerCompat.FrameCallback.class));
mTiming = new Timing(reactContext, mock(DevSupportManager.class));
mJSTimersMock = mock(JSTimersExecution.class);
@ -107,7 +106,7 @@ public class TimingModuleTest {
doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
((Runnable)invocation.getArguments()[0]).run();
((Runnable) invocation.getArguments()[0]).run();
return null;
}
}).when(reactContext).runOnJSQueueThread(any(Runnable.class));
@ -116,8 +115,8 @@ public class TimingModuleTest {
}
private void stepChoreographerFrame() {
Choreographer.FrameCallback callback = mPostFrameCallbackHandler.getAndResetFrameCallback();
Choreographer.FrameCallback idleCallback = mIdlePostFrameCallbackHandler.getAndResetFrameCallback();
ChoreographerCompat.FrameCallback callback = mPostFrameCallbackHandler.getAndResetFrameCallback();
ChoreographerCompat.FrameCallback idleCallback = mIdlePostFrameCallbackHandler.getAndResetFrameCallback();
mCurrentTimeNs += FRAME_TIME_NS;
when(SystemClock.uptimeMillis()).thenReturn(mCurrentTimeNs / 1000000);
@ -259,17 +258,17 @@ public class TimingModuleTest {
private static class PostFrameIdleCallbackHandler implements Answer<Void> {
private Choreographer.FrameCallback mFrameCallback;
private ChoreographerCompat.FrameCallback mFrameCallback;
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
mFrameCallback = (Choreographer.FrameCallback) args[1];
mFrameCallback = (ChoreographerCompat.FrameCallback) args[1];
return null;
}
public Choreographer.FrameCallback getAndResetFrameCallback() {
Choreographer.FrameCallback callback = mFrameCallback;
public ChoreographerCompat.FrameCallback getAndResetFrameCallback() {
ChoreographerCompat.FrameCallback callback = mFrameCallback;
mFrameCallback = null;
return callback;
}
@ -277,17 +276,17 @@ public class TimingModuleTest {
private static class PostFrameCallbackHandler implements Answer<Void> {
private Choreographer.FrameCallback mFrameCallback;
private ChoreographerCompat.FrameCallback mFrameCallback;
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
mFrameCallback = (Choreographer.FrameCallback) args[1];
mFrameCallback = (ChoreographerCompat.FrameCallback) args[1];
return null;
}
public Choreographer.FrameCallback getAndResetFrameCallback() {
Choreographer.FrameCallback callback = mFrameCallback;
public ChoreographerCompat.FrameCallback getAndResetFrameCallback() {
ChoreographerCompat.FrameCallback callback = mFrameCallback;
mFrameCallback = null;
return callback;
}

View File

@ -14,7 +14,6 @@ import java.util.Arrays;
import java.util.List;
import android.graphics.Color;
import android.view.Choreographer;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
@ -29,6 +28,8 @@ import com.facebook.react.bridge.JavaOnlyArray;
import com.facebook.react.bridge.JavaOnlyMap;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactTestHelper;
import com.facebook.react.modules.core.ChoreographerCompat;
import com.facebook.react.modules.core.ReactChoreographer;
import com.facebook.react.views.text.ReactRawTextManager;
import com.facebook.react.views.text.ReactTextShadowNode;
import com.facebook.react.views.text.ReactTextViewManager;
@ -70,7 +71,7 @@ public class UIManagerModuleTest {
private ReactApplicationContext mReactContext;
private CatalystInstance mCatalystInstanceMock;
private ArrayList<Choreographer.FrameCallback> mPendingChoreographerCallbacks;
private ArrayList<ChoreographerCompat.FrameCallback> mPendingFrameCallbacks;
@Before
public void setUp() {
@ -91,17 +92,17 @@ public class UIManagerModuleTest {
});
PowerMockito.when(ReactChoreographer.getInstance()).thenReturn(choreographerMock);
mPendingChoreographerCallbacks = new ArrayList<>();
mPendingFrameCallbacks = new ArrayList<>();
doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
mPendingChoreographerCallbacks
.add((Choreographer.FrameCallback) invocation.getArguments()[1]);
mPendingFrameCallbacks
.add((ChoreographerCompat.FrameCallback) invocation.getArguments()[1]);
return null;
}
}).when(choreographerMock).postFrameCallback(
any(ReactChoreographer.CallbackType.class),
any(Choreographer.FrameCallback.class));
any(ChoreographerCompat.FrameCallback.class));
mCatalystInstanceMock = ReactTestHelper.createMockCatalystInstance();
mReactContext = new ReactApplicationContext(RuntimeEnvironment.application);
@ -139,7 +140,7 @@ public class UIManagerModuleTest {
JavaOnlyMap.of(ReactTextShadowNode.PROP_TEXT, "New text"));
uiManager.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
assertThat(textView.getText().toString()).isEqualTo("New text");
}
@ -182,7 +183,7 @@ public class UIManagerModuleTest {
null);
uiManager.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
assertThat(rootView.getChildCount()).isEqualTo(1);
@ -213,7 +214,7 @@ public class UIManagerModuleTest {
null);
uiManager.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
assertChildrenAreExactly(
hierarchy.nativeRootView,
@ -240,7 +241,7 @@ public class UIManagerModuleTest {
JavaOnlyArray.of(0, 3));
uiManager.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
assertChildrenAreExactly(
hierarchy.nativeRootView,
@ -266,7 +267,7 @@ public class UIManagerModuleTest {
JavaOnlyArray.of(1));
uiManager.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
assertChildrenAreExactly(
hierarchy.nativeRootView,
@ -289,7 +290,7 @@ public class UIManagerModuleTest {
JavaOnlyArray.of(3));
uiManager.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
}
@Test(expected = IllegalViewOperationException.class)
@ -306,7 +307,7 @@ public class UIManagerModuleTest {
JavaOnlyArray.of(3, 3));
uiManager.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
}
@Test
@ -335,7 +336,7 @@ public class UIManagerModuleTest {
null);
uiManager.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
assertThat(hierarchy.nativeRootView.getChildCount()).isEqualTo(5);
assertThat(hierarchy.nativeRootView.getChildAt(0)).isEqualTo(expectedViewAt0);
@ -363,7 +364,7 @@ public class UIManagerModuleTest {
null);
uiManager.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
assertChildrenAreExactly(
hierarchy.nativeRootView,
@ -392,7 +393,7 @@ public class UIManagerModuleTest {
JavaOnlyArray.of(1));
uiManager.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
assertChildrenAreExactly(
hierarchy.nativeRootView,
@ -424,7 +425,7 @@ public class UIManagerModuleTest {
null);
uiManager.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
View newView = hierarchy.nativeRootView.getChildAt(4);
assertThat(newView.getLeft()).isEqualTo(10);
@ -472,7 +473,7 @@ public class UIManagerModuleTest {
JavaOnlyArray.of(4));
uiManager.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
assertThat(hierarchy.nativeRootView.getChildCount()).isEqualTo(4);
}
@ -518,7 +519,7 @@ public class UIManagerModuleTest {
ReactViewManager.REACT_CLASS,
JavaOnlyMap.of("left", 10.0, "top", 20.0, "width", 30.0, "height", 40.0));
uiManager.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
assertThat(view0.getLeft()).isGreaterThan(2);
// verify that the layout doesn't get updated when we update style property not affecting the
@ -529,7 +530,7 @@ public class UIManagerModuleTest {
ReactViewManager.REACT_CLASS,
JavaOnlyMap.of("backgroundColor", Color.RED));
uiManager.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
assertThat(view0.getLeft()).isEqualTo(1);
}
@ -565,7 +566,7 @@ public class UIManagerModuleTest {
uiManagerModule.removeAnimation(hierarchy.rootView, 1000);
uiManagerModule.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
verify(callbackMock, times(1)).invoke(false);
verify(mockAnimation).run();
@ -591,7 +592,7 @@ public class UIManagerModuleTest {
uiManager.replaceExistingNonRootView(hierarchy.view2, newViewTag);
uiManager.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
assertThat(hierarchy.nativeRootView.getChildCount()).isEqualTo(4);
assertThat(hierarchy.nativeRootView.getChildAt(2)).isInstanceOf(ReactViewGroup.class);
@ -640,7 +641,7 @@ public class UIManagerModuleTest {
addChild(uiManager, containerTag, containerTag + 3, 1);
uiManager.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
assertThat(rootView.getChildCount()).isEqualTo(2);
assertThat(((ViewGroup) rootView.getChildAt(0)).getChildCount()).isEqualTo(2);
@ -648,7 +649,7 @@ public class UIManagerModuleTest {
uiManager.removeSubviewsFromContainerWithID(containerTag);
uiManager.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
assertThat(rootView.getChildCount()).isEqualTo(2);
assertThat(((ViewGroup) rootView.getChildAt(0)).getChildCount()).isEqualTo(0);
@ -693,7 +694,7 @@ public class UIManagerModuleTest {
null);
uiManager.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
return rootView;
}
@ -744,7 +745,7 @@ public class UIManagerModuleTest {
addChild(uiManager, hierarchy.viewWithChildren1, hierarchy.childView1, 1);
uiManager.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
return hierarchy;
}
@ -803,11 +804,11 @@ public class UIManagerModuleTest {
}
}
private void executePendingChoreographerCallbacks() {
ArrayList<Choreographer.FrameCallback> callbacks =
new ArrayList<>(mPendingChoreographerCallbacks);
mPendingChoreographerCallbacks.clear();
for (Choreographer.FrameCallback frameCallback : callbacks) {
private void executePendingFrameCallbacks() {
ArrayList<ChoreographerCompat.FrameCallback> callbacks =
new ArrayList<>(mPendingFrameCallbacks);
mPendingFrameCallbacks.clear();
for (ChoreographerCompat.FrameCallback frameCallback : callbacks) {
frameCallback.doFrame(0);
}
}

View File

@ -23,7 +23,6 @@ import android.text.TextUtils;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.UnderlineSpan;
import android.view.Choreographer;
import android.widget.TextView;
import com.facebook.react.ReactRootView;
@ -32,7 +31,8 @@ import com.facebook.react.bridge.JavaOnlyArray;
import com.facebook.react.bridge.JavaOnlyMap;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactTestHelper;
import com.facebook.react.uimanager.ReactChoreographer;
import com.facebook.react.modules.core.ChoreographerCompat;
import com.facebook.react.modules.core.ReactChoreographer;
import com.facebook.react.uimanager.UIImplementationProvider;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.ViewManager;
@ -68,32 +68,32 @@ public class ReactTextTest {
@Rule
public PowerMockRule rule = new PowerMockRule();
private ArrayList<Choreographer.FrameCallback> mPendingChoreographerCallbacks;
private ArrayList<ChoreographerCompat.FrameCallback> mPendingFrameCallbacks;
@Before
public void setUp() {
PowerMockito.mockStatic(Arguments.class, ReactChoreographer.class);
ReactChoreographer choreographerMock = mock(ReactChoreographer.class);
ReactChoreographer uiDriverMock = mock(ReactChoreographer.class);
PowerMockito.when(Arguments.createMap()).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return new JavaOnlyMap();
}
});
PowerMockito.when(ReactChoreographer.getInstance()).thenReturn(choreographerMock);
PowerMockito.when(ReactChoreographer.getInstance()).thenReturn(uiDriverMock);
mPendingChoreographerCallbacks = new ArrayList<>();
mPendingFrameCallbacks = new ArrayList<>();
doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
mPendingChoreographerCallbacks
.add((Choreographer.FrameCallback) invocation.getArguments()[1]);
mPendingFrameCallbacks
.add((ChoreographerCompat.FrameCallback) invocation.getArguments()[1]);
return null;
}
}).when(choreographerMock).postFrameCallback(
}).when(uiDriverMock).postFrameCallback(
any(ReactChoreographer.CallbackType.class),
any(Choreographer.FrameCallback.class));
any(ChoreographerCompat.FrameCallback.class));
}
@Test
@ -411,15 +411,15 @@ public class ReactTextTest {
null);
uiManager.onBatchComplete();
executePendingChoreographerCallbacks();
executePendingFrameCallbacks();
return rootView;
}
private void executePendingChoreographerCallbacks() {
ArrayList<Choreographer.FrameCallback> callbacks =
new ArrayList<>(mPendingChoreographerCallbacks);
mPendingChoreographerCallbacks.clear();
for (Choreographer.FrameCallback frameCallback : callbacks) {
private void executePendingFrameCallbacks() {
ArrayList<ChoreographerCompat.FrameCallback> callbacks =
new ArrayList<>(mPendingFrameCallbacks);
mPendingFrameCallbacks.clear();
for (ChoreographerCompat.FrameCallback frameCallback : callbacks) {
frameCallback.doFrame(0);
}
}

View File

@ -13,7 +13,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import android.view.Choreographer;
import android.widget.EditText;
import com.facebook.react.ReactRootView;
@ -22,8 +21,8 @@ import com.facebook.react.bridge.JavaOnlyArray;
import com.facebook.react.bridge.JavaOnlyMap;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactTestHelper;
import com.facebook.react.uimanager.ReactChoreographer;
import com.facebook.react.uimanager.UIImplementation;
import com.facebook.react.modules.core.ChoreographerCompat;
import com.facebook.react.modules.core.ReactChoreographer;
import com.facebook.react.uimanager.UIImplementationProvider;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.ViewManager;
@ -58,7 +57,7 @@ public class TextInputTest {
@Rule
public PowerMockRule rule = new PowerMockRule();
private ArrayList<Choreographer.FrameCallback> mPendingChoreographerCallbacks;
private ArrayList<ChoreographerCompat.FrameCallback> mPendingChoreographerCallbacks;
@Before
public void setUp() {
@ -78,12 +77,12 @@ public class TextInputTest {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
mPendingChoreographerCallbacks
.add((Choreographer.FrameCallback) invocation.getArguments()[1]);
.add((ChoreographerCompat.FrameCallback) invocation.getArguments()[1]);
return null;
}
}).when(choreographerMock).postFrameCallback(
any(ReactChoreographer.CallbackType.class),
any(Choreographer.FrameCallback.class));
any(ChoreographerCompat.FrameCallback.class));
}
@Test
@ -169,10 +168,10 @@ public class TextInputTest {
}
private void executePendingChoreographerCallbacks() {
ArrayList<Choreographer.FrameCallback> callbacks =
ArrayList<ChoreographerCompat.FrameCallback> callbacks =
new ArrayList<>(mPendingChoreographerCallbacks);
mPendingChoreographerCallbacks.clear();
for (Choreographer.FrameCallback frameCallback : callbacks) {
for (ChoreographerCompat.FrameCallback frameCallback : callbacks) {
frameCallback.doFrame(0);
}
}