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

@@ -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);