mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Release React Native for Android
This is an early release and there are several things that are known not to work if you're porting your iOS app to Android. See the Known Issues guide on the website. We will work with the community to reach platform parity with iOS.
This commit is contained in:
+120
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, 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.
|
||||
*/
|
||||
|
||||
package com.facebook.react.modules.debug;
|
||||
|
||||
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;
|
||||
import com.facebook.react.bridge.JSApplicationCausedNativeException;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
|
||||
/**
|
||||
* Module that records debug information during transitions (animated navigation events such as
|
||||
* going from one screen to another).
|
||||
*/
|
||||
public class AnimationsDebugModule extends ReactContextBaseJavaModule {
|
||||
|
||||
private @Nullable FpsDebugFrameCallback mFrameCallback;
|
||||
private final DeveloperSettings mCatalystSettings;
|
||||
|
||||
public AnimationsDebugModule(
|
||||
ReactApplicationContext reactContext,
|
||||
DeveloperSettings catalystSettings) {
|
||||
super(reactContext);
|
||||
mCatalystSettings = catalystSettings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "AnimationsDebugModule";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void startRecordingFps() {
|
||||
if (!mCatalystSettings.isAnimationFpsDebugEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mFrameCallback != null) {
|
||||
throw new JSApplicationCausedNativeException("Already recording FPS!");
|
||||
}
|
||||
checkAPILevel();
|
||||
|
||||
mFrameCallback = new FpsDebugFrameCallback(
|
||||
Choreographer.getInstance(),
|
||||
getReactApplicationContext());
|
||||
mFrameCallback.startAndRecordFpsAtEachFrame();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an animation finishes. The caller should include the animation stop time in ms
|
||||
* (unix time) so that we know when the animation stopped from the JS perspective and we don't
|
||||
* count time after as being part of the animation.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void stopRecordingFps(double animationStopTimeMs) {
|
||||
if (mFrameCallback == null) {
|
||||
return;
|
||||
}
|
||||
checkAPILevel();
|
||||
|
||||
mFrameCallback.stop();
|
||||
|
||||
// Casting to long is safe here since animationStopTimeMs is unix time and thus relatively small
|
||||
FpsDebugFrameCallback.FpsInfo fpsInfo = mFrameCallback.getFpsInfo((long) animationStopTimeMs);
|
||||
|
||||
if (fpsInfo == null) {
|
||||
Toast.makeText(getReactApplicationContext(), "Unable to get FPS info", Toast.LENGTH_LONG);
|
||||
} else {
|
||||
String fpsString = String.format(
|
||||
Locale.US,
|
||||
"FPS: %.2f, %d frames (%d expected)",
|
||||
fpsInfo.fps,
|
||||
fpsInfo.totalFrames,
|
||||
fpsInfo.totalExpectedFrames);
|
||||
String jsFpsString = String.format(
|
||||
Locale.US,
|
||||
"JS FPS: %.2f, %d frames (%d expected)",
|
||||
fpsInfo.jsFps,
|
||||
fpsInfo.totalJsFrames,
|
||||
fpsInfo.totalExpectedFrames);
|
||||
String debugString = fpsString + "\n" + jsFpsString + "\n" +
|
||||
"Total Time MS: " + String.format(Locale.US, "%d", fpsInfo.totalTimeMs);
|
||||
FLog.d(ReactConstants.TAG, debugString);
|
||||
Toast.makeText(getReactApplicationContext(), debugString, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
mFrameCallback = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCatalystInstanceDestroy() {
|
||||
if (mFrameCallback != null) {
|
||||
mFrameCallback.stop();
|
||||
mFrameCallback = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkAPILevel() {
|
||||
if (Build.VERSION.SDK_INT < 16) {
|
||||
throw new JSApplicationCausedNativeException(
|
||||
"Animation debugging is not supported in API <16");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, 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.
|
||||
*/
|
||||
|
||||
package com.facebook.react.modules.debug;
|
||||
|
||||
/**
|
||||
* Provides access to React Native developers settings.
|
||||
*/
|
||||
public interface DeveloperSettings {
|
||||
|
||||
/**
|
||||
* @return whether an overlay showing current FPS should be shown.
|
||||
*/
|
||||
boolean isFpsDebugEnabled();
|
||||
|
||||
/**
|
||||
* @return Whether debug information about transitions should be displayed.
|
||||
*/
|
||||
boolean isAnimationFpsDebugEnabled();
|
||||
}
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, 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.
|
||||
*/
|
||||
|
||||
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.uimanager.UIManagerModule;
|
||||
import com.facebook.react.uimanager.debug.NotThreadSafeUiManagerDebugListener;
|
||||
|
||||
/**
|
||||
* Debug object that listens to bridge busy/idle events and UiManagerModule dispatches and uses it
|
||||
* 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}.
|
||||
*/
|
||||
public class DidJSUpdateUiDuringFrameDetector implements NotThreadSafeBridgeIdleDebugListener,
|
||||
NotThreadSafeUiManagerDebugListener {
|
||||
|
||||
private final LongArray mTransitionToIdleEvents = LongArray.createWithInitialCapacity(20);
|
||||
private final LongArray mTransitionToBusyEvents = LongArray.createWithInitialCapacity(20);
|
||||
private final LongArray mViewHierarchyUpdateEnqueuedEvents =
|
||||
LongArray.createWithInitialCapacity(20);
|
||||
private final LongArray mViewHierarchyUpdateFinishedEvents =
|
||||
LongArray.createWithInitialCapacity(20);
|
||||
private volatile boolean mWasIdleAtEndOfLastFrame = true;
|
||||
|
||||
@Override
|
||||
public synchronized void onTransitionToBridgeIdle() {
|
||||
mTransitionToIdleEvents.add(System.nanoTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onTransitionToBridgeBusy() {
|
||||
mTransitionToBusyEvents.add(System.nanoTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onViewHierarchyUpdateEnqueued() {
|
||||
mViewHierarchyUpdateEnqueuedEvents.add(System.nanoTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onViewHierarchyUpdateFinished() {
|
||||
mViewHierarchyUpdateFinishedEvents.add(System.nanoTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Designed to be called from a {@link Choreographer.FrameCallback#doFrame} call.
|
||||
*
|
||||
* There are two 'success' cases that will cause {@link #getDidJSHitFrameAndCleanup} to
|
||||
* return true for a given frame:
|
||||
*
|
||||
* 1) UIManagerModule finished dispatching a batched UI update on the UI thread during the frame.
|
||||
* This means that during the next hierarchy traversal, new UI will be drawn if needed (good).
|
||||
* 2) The bridge ended the frame idle (meaning there were no JS nor native module calls still in
|
||||
* flight) AND there was no UiManagerModule update enqueued that didn't also finish. NB: if
|
||||
* there was one enqueued that actually finished, we'd have case 1), so effectively we just
|
||||
* look for whether one was enqueued.
|
||||
*
|
||||
* NB: This call can only be called once for a given frame time range because it cleans up
|
||||
* events it recorded for that frame.
|
||||
*
|
||||
* NB2: This makes the assumption that onViewHierarchyUpdateEnqueued is called from the
|
||||
* {@link UIManagerModule#onBatchComplete()}, e.g. while the bridge is still considered busy,
|
||||
* which means there is no race condition where the bridge has gone idle but a hierarchy update is
|
||||
* waiting to be enqueued.
|
||||
*
|
||||
* @param frameStartTimeNanos the time in nanos that the last frame started
|
||||
* @param frameEndTimeNanos the time in nanos that the last frame ended
|
||||
*/
|
||||
public synchronized boolean getDidJSHitFrameAndCleanup(
|
||||
long frameStartTimeNanos,
|
||||
long frameEndTimeNanos) {
|
||||
// Case 1: We dispatched a UI update
|
||||
boolean finishedUiUpdate = hasEventBetweenTimestamps(
|
||||
mViewHierarchyUpdateFinishedEvents,
|
||||
frameStartTimeNanos,
|
||||
frameEndTimeNanos);
|
||||
boolean didEndFrameIdle = didEndFrameIdle(frameStartTimeNanos, frameEndTimeNanos);
|
||||
|
||||
boolean hitFrame;
|
||||
if (finishedUiUpdate) {
|
||||
hitFrame = true;
|
||||
} else {
|
||||
// Case 2: Ended idle but no UI was enqueued during that frame
|
||||
hitFrame = didEndFrameIdle && !hasEventBetweenTimestamps(
|
||||
mViewHierarchyUpdateEnqueuedEvents,
|
||||
frameStartTimeNanos,
|
||||
frameEndTimeNanos);
|
||||
}
|
||||
|
||||
cleanUp(mTransitionToIdleEvents, frameEndTimeNanos);
|
||||
cleanUp(mTransitionToBusyEvents, frameEndTimeNanos);
|
||||
cleanUp(mViewHierarchyUpdateEnqueuedEvents, frameEndTimeNanos);
|
||||
cleanUp(mViewHierarchyUpdateFinishedEvents, frameEndTimeNanos);
|
||||
|
||||
mWasIdleAtEndOfLastFrame = didEndFrameIdle;
|
||||
|
||||
return hitFrame;
|
||||
}
|
||||
|
||||
private static boolean hasEventBetweenTimestamps(
|
||||
LongArray eventArray,
|
||||
long startTime,
|
||||
long endTime) {
|
||||
for (int i = 0; i < eventArray.size(); i++) {
|
||||
long time = eventArray.get(i);
|
||||
if (time >= startTime && time < endTime) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static long getLastEventBetweenTimestamps(
|
||||
LongArray eventArray,
|
||||
long startTime,
|
||||
long endTime) {
|
||||
long lastEvent = -1;
|
||||
for (int i = 0; i < eventArray.size(); i++) {
|
||||
long time = eventArray.get(i);
|
||||
if (time >= startTime && time < endTime) {
|
||||
lastEvent = time;
|
||||
} else if (time >= endTime) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return lastEvent;
|
||||
}
|
||||
|
||||
private boolean didEndFrameIdle(long startTime, long endTime) {
|
||||
long lastIdleTransition = getLastEventBetweenTimestamps(
|
||||
mTransitionToIdleEvents,
|
||||
startTime,
|
||||
endTime);
|
||||
long lastBusyTransition = getLastEventBetweenTimestamps(
|
||||
mTransitionToBusyEvents,
|
||||
startTime,
|
||||
endTime);
|
||||
|
||||
if (lastIdleTransition == -1 && lastBusyTransition == -1) {
|
||||
return mWasIdleAtEndOfLastFrame;
|
||||
}
|
||||
|
||||
return lastIdleTransition > lastBusyTransition;
|
||||
}
|
||||
|
||||
private static void cleanUp(LongArray eventArray, long endTime) {
|
||||
int size = eventArray.size();
|
||||
int indicesToRemove = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (eventArray.get(i) < endTime) {
|
||||
indicesToRemove++;
|
||||
}
|
||||
}
|
||||
|
||||
if (indicesToRemove > 0) {
|
||||
for (int i = 0; i < size - indicesToRemove; i++) {
|
||||
eventArray.set(i, eventArray.get(i + indicesToRemove));
|
||||
}
|
||||
eventArray.dropTail(indicesToRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, 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.
|
||||
*/
|
||||
|
||||
package com.facebook.react.modules.debug;
|
||||
|
||||
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.uimanager.UIManagerModule;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
|
||||
/**
|
||||
* Each time a frame is drawn, records whether it should have expected any more callbacks since
|
||||
* the last time a frame was drawn (i.e. was a frame skipped?). Uses this plus total elapsed time
|
||||
* to determine FPS. Can also record total and expected frame counts, though NB, since the expected
|
||||
* frame rate is estimated, the expected frame count will lose accuracy over time.
|
||||
*
|
||||
* 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 static class FpsInfo {
|
||||
|
||||
public final int totalFrames;
|
||||
public final int totalJsFrames;
|
||||
public final int totalExpectedFrames;
|
||||
public final double fps;
|
||||
public final double jsFps;
|
||||
public final int totalTimeMs;
|
||||
|
||||
public FpsInfo(
|
||||
int totalFrames,
|
||||
int totalJsFrames,
|
||||
int totalExpectedFrames,
|
||||
double fps,
|
||||
double jsFps,
|
||||
int totalTimeMs) {
|
||||
this.totalFrames = totalFrames;
|
||||
this.totalJsFrames = totalJsFrames;
|
||||
this.totalExpectedFrames = totalExpectedFrames;
|
||||
this.fps = fps;
|
||||
this.jsFps = jsFps;
|
||||
this.totalTimeMs = totalTimeMs;
|
||||
}
|
||||
}
|
||||
|
||||
private static final double EXPECTED_FRAME_TIME = 16.9;
|
||||
|
||||
private final Choreographer mChoreographer;
|
||||
private final ReactContext mReactContext;
|
||||
private final UIManagerModule mUIManagerModule;
|
||||
private final DidJSUpdateUiDuringFrameDetector mDidJSUpdateUiDuringFrameDetector;
|
||||
|
||||
private boolean mShouldStop = false;
|
||||
private long mFirstFrameTime = -1;
|
||||
private long mLastFrameTime = -1;
|
||||
private int mNumFrameCallbacks = 0;
|
||||
private int mNumFrameCallbacksWithBatchDispatches = 0;
|
||||
private boolean mIsRecordingFpsInfoAtEachFrame = false;
|
||||
private @Nullable TreeMap<Long, FpsInfo> mTimeToFps;
|
||||
|
||||
public FpsDebugFrameCallback(Choreographer choreographer, ReactContext reactContext) {
|
||||
mChoreographer = choreographer;
|
||||
mReactContext = reactContext;
|
||||
mUIManagerModule = reactContext.getNativeModule(UIManagerModule.class);
|
||||
mDidJSUpdateUiDuringFrameDetector = new DidJSUpdateUiDuringFrameDetector();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFrame(long l) {
|
||||
if (mShouldStop) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mFirstFrameTime == -1) {
|
||||
mFirstFrameTime = l;
|
||||
}
|
||||
|
||||
long lastFrameStartTime = mLastFrameTime;
|
||||
mLastFrameTime = l;
|
||||
|
||||
if (mDidJSUpdateUiDuringFrameDetector.getDidJSHitFrameAndCleanup(
|
||||
lastFrameStartTime,
|
||||
l)) {
|
||||
mNumFrameCallbacksWithBatchDispatches++;
|
||||
}
|
||||
|
||||
mNumFrameCallbacks++;
|
||||
|
||||
if (mIsRecordingFpsInfoAtEachFrame) {
|
||||
Assertions.assertNotNull(mTimeToFps);
|
||||
FpsInfo info = new FpsInfo(
|
||||
getNumFrames(),
|
||||
getNumJSFrames(),
|
||||
getExpectedNumFrames(),
|
||||
getFPS(),
|
||||
getJSFPS(),
|
||||
getTotalTimeMS());
|
||||
mTimeToFps.put(System.currentTimeMillis(), info);
|
||||
}
|
||||
|
||||
mChoreographer.postFrameCallback(this);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
mShouldStop = false;
|
||||
mReactContext.getCatalystInstance().addBridgeIdleDebugListener(
|
||||
mDidJSUpdateUiDuringFrameDetector);
|
||||
mUIManagerModule.setUiManagerDebugListener(mDidJSUpdateUiDuringFrameDetector);
|
||||
mChoreographer.postFrameCallback(this);
|
||||
}
|
||||
|
||||
public void startAndRecordFpsAtEachFrame() {
|
||||
mTimeToFps = new TreeMap<Long, FpsInfo>();
|
||||
mIsRecordingFpsInfoAtEachFrame = true;
|
||||
start();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
mShouldStop = true;
|
||||
mReactContext.getCatalystInstance().removeBridgeIdleDebugListener(
|
||||
mDidJSUpdateUiDuringFrameDetector);
|
||||
mUIManagerModule.setUiManagerDebugListener(null);
|
||||
}
|
||||
|
||||
public double getFPS() {
|
||||
if (mLastFrameTime == mFirstFrameTime) {
|
||||
return 0;
|
||||
}
|
||||
return ((double) (getNumFrames()) * 1e9) / (mLastFrameTime - mFirstFrameTime);
|
||||
}
|
||||
|
||||
public double getJSFPS() {
|
||||
if (mLastFrameTime == mFirstFrameTime) {
|
||||
return 0;
|
||||
}
|
||||
return ((double) (getNumJSFrames()) * 1e9) / (mLastFrameTime - mFirstFrameTime);
|
||||
}
|
||||
|
||||
public int getNumFrames() {
|
||||
return mNumFrameCallbacks - 1;
|
||||
}
|
||||
|
||||
public int getNumJSFrames() {
|
||||
return mNumFrameCallbacksWithBatchDispatches - 1;
|
||||
}
|
||||
|
||||
public int getExpectedNumFrames() {
|
||||
double totalTimeMS = getTotalTimeMS();
|
||||
int expectedFrames = (int) (totalTimeMS / EXPECTED_FRAME_TIME + 1);
|
||||
return expectedFrames;
|
||||
}
|
||||
|
||||
public int getTotalTimeMS() {
|
||||
return (int) ((double) mLastFrameTime - mFirstFrameTime) / 1000000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the FpsInfo as if stop had been called at the given upToTimeMs. Only valid if
|
||||
* monitoring was started with {@link #startAndRecordFpsAtEachFrame()}.
|
||||
*/
|
||||
public @Nullable FpsInfo getFpsInfo(long upToTimeMs) {
|
||||
Assertions.assertNotNull(mTimeToFps, "FPS was not recorded at each frame!");
|
||||
Map.Entry<Long, FpsInfo> bestEntry = mTimeToFps.floorEntry(upToTimeMs);
|
||||
if (bestEntry == null) {
|
||||
return null;
|
||||
}
|
||||
return bestEntry.getValue();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
mFirstFrameTime = -1;
|
||||
mLastFrameTime = -1;
|
||||
mNumFrameCallbacks = 0;
|
||||
mNumFrameCallbacksWithBatchDispatches = 0;
|
||||
mIsRecordingFpsInfoAtEachFrame = false;
|
||||
mTimeToFps = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, 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.
|
||||
*/
|
||||
|
||||
package com.facebook.react.modules.debug;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.BaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.bridge.WritableNativeMap;
|
||||
|
||||
/**
|
||||
* Module that exposes the URL to the source code map (used for exception stack trace parsing) to JS
|
||||
*/
|
||||
public class SourceCodeModule extends BaseJavaModule {
|
||||
|
||||
private final String mSourceMapUrl;
|
||||
private final String mSourceUrl;
|
||||
|
||||
public SourceCodeModule(String sourceUrl, String sourceMapUrl) {
|
||||
mSourceMapUrl = sourceMapUrl;
|
||||
mSourceUrl = sourceUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "RKSourceCode";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getScriptText(final Callback onSuccess, final Callback onError) {
|
||||
WritableMap map = new WritableNativeMap();
|
||||
map.putString("fullSourceMappingURL", mSourceMapUrl);
|
||||
onSuccess.invoke(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Map<String, Object> getConstants() {
|
||||
HashMap<String, Object> constants = new HashMap<String, Object>();
|
||||
constants.put("scriptURL", mSourceUrl);
|
||||
return constants;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user