Fix: Release builds on android (#1325)
Summary: After `react-native-flipper` 0.48.0 I cannot build releases on android (I think that passed on CI tests because the example was running an older version of this package, 0.47.0). So I moved all the `android/src/main` content to `android/src/debug` because we will not use Flipper in another Build Variant ## Changelog I moved all content from `react-native-flipper/android/src/main` to `react-native-flipper/android/src/debug`. Probably solves https://github.com/facebook/flipper/issues/1303 Pull Request resolved: https://github.com/facebook/flipper/pull/1325 Test Plan: Maybe create a custom CI script to verify if Flipper deps are present on Release Builds, [something like this](https://github.com/facebook/flipper/issues/1274#issue-641197153) Reviewed By: cekkaewnumchai Differential Revision: D22333432 Pulled By: passy fbshipit-source-id: 4abbab5ecbe08d44752b2138569ff60d25724087
This commit is contained in:
committed by
Facebook GitHub Bot
parent
228d09d572
commit
51e37311d0
@@ -0,0 +1,4 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.flipper.reactnative;
|
||||
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* The FlipperModule is a React Native Native Module. The instance handles incoming calls that
|
||||
* arrive over the React Native bridge from JavaScript. The instance is created per
|
||||
* ReactApplicationContext. Which means this module gets reinstated if RN performs a reload. So it
|
||||
* should not hold any further state on its own. All state is hold by the Plugin and PluginManager
|
||||
* classes.
|
||||
*/
|
||||
@ReactModule(name = FlipperModule.NAME)
|
||||
public class FlipperModule extends ReactContextBaseJavaModule {
|
||||
|
||||
public static final String NAME = "Flipper";
|
||||
|
||||
private final FlipperReactNativeJavaScriptPluginManager mManager;
|
||||
|
||||
public FlipperModule(
|
||||
FlipperReactNativeJavaScriptPluginManager manager, ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
mManager = manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void registerPlugin(
|
||||
final String pluginId, final Boolean inBackground, final Callback statusCallback) {
|
||||
mManager.registerPlugin(this, pluginId, inBackground, statusCallback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void send(String pluginId, String method, String data) {
|
||||
mManager.send(pluginId, method, data);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void reportErrorWithMetadata(String pluginId, String reason, String stackTrace) {
|
||||
mManager.reportErrorWithMetadata(pluginId, reason, stackTrace);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void reportError(String pluginId, String error) {
|
||||
mManager.reportError(pluginId, error);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void subscribe(String pluginId, String method) {
|
||||
mManager.subscribe(this, pluginId, method);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void respondSuccess(String responderId, String data) {
|
||||
mManager.respondSuccess(responderId, data);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void respondError(String responderId, String data) {
|
||||
mManager.respondError(responderId, data);
|
||||
}
|
||||
|
||||
void sendJSEvent(String eventName, WritableMap params) {
|
||||
final ReactApplicationContext context = getReactApplicationContextIfActiveOrWarn();
|
||||
if (context != null) {
|
||||
context
|
||||
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
||||
.emit(eventName, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.flipper.reactnative;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Exposes the react native modules that should be created per ReactApplicationContext. Note that an
|
||||
* application context lives shorter than the application itself, e.g. reload creates a fresh one.
|
||||
*/
|
||||
public class FlipperPackage implements ReactPackage {
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
return Collections.<NativeModule>singletonList(
|
||||
new FlipperModule(FlipperReactNativeJavaScriptPluginManager.getInstance(), reactContext));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.flipper.reactnative;
|
||||
|
||||
import com.facebook.flipper.core.FlipperConnection;
|
||||
import com.facebook.flipper.core.FlipperPlugin;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
|
||||
/**
|
||||
* This class holds the state of a single plugin that is created from the JS world trough
|
||||
* `Flipper.addPlugin`. It's main concern is managing the FlipperConnection to the Desktop client.
|
||||
*
|
||||
* <p>This class is abstract, as Flipper does not support having multiple instances of the same
|
||||
* class as plugins, But every JS plugin will store it state in a FlipperPlugin, so for every plugin
|
||||
* we will create an anonymous subclass.
|
||||
*
|
||||
* <p>Note that this class does not directly interact back over the JS bridge to React Native, as
|
||||
* the JavaPlugin has a longer lifecycle than it's JS counter part, which will be recreated on
|
||||
* reload. However, if the native module reload, we keep these instances to not loose the connextion
|
||||
* to the Flipper Desktop client.
|
||||
*/
|
||||
public abstract class FlipperReactNativeJavaScriptPlugin implements FlipperPlugin {
|
||||
private final String mPluginId;
|
||||
private final boolean mInBackground;
|
||||
private FlipperConnection mConnection;
|
||||
private FlipperModule mModule;
|
||||
|
||||
FlipperReactNativeJavaScriptPlugin(FlipperModule module, String pluginId, boolean inBackground) {
|
||||
mPluginId = pluginId;
|
||||
mModule = module;
|
||||
mInBackground = inBackground;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return mPluginId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnect(FlipperConnection connection) {
|
||||
mConnection = connection;
|
||||
fireOnConnect();
|
||||
}
|
||||
|
||||
void fireOnConnect() {
|
||||
if (!isConnected()) {
|
||||
throw new RuntimeException("Plugin not connected " + mPluginId);
|
||||
}
|
||||
mModule.sendJSEvent("react-native-flipper-plugin-connect", getPluginParams());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnect() {
|
||||
mModule.sendJSEvent("react-native-flipper-plugin-disconnect", getPluginParams());
|
||||
mConnection = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean runInBackground() {
|
||||
return mInBackground;
|
||||
}
|
||||
|
||||
boolean isConnected() {
|
||||
return mConnection != null;
|
||||
}
|
||||
|
||||
FlipperConnection getConnection() {
|
||||
return mConnection;
|
||||
}
|
||||
|
||||
void setModule(FlipperModule module) {
|
||||
mModule = module;
|
||||
}
|
||||
|
||||
private WritableMap getPluginParams() {
|
||||
final WritableMap params = Arguments.createMap();
|
||||
params.putString("plugin", mPluginId);
|
||||
return params;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.flipper.reactnative;
|
||||
|
||||
import com.facebook.flipper.android.AndroidFlipperClient;
|
||||
import com.facebook.flipper.core.FlipperArray;
|
||||
import com.facebook.flipper.core.FlipperClient;
|
||||
import com.facebook.flipper.core.FlipperObject;
|
||||
import com.facebook.flipper.core.FlipperPlugin;
|
||||
import com.facebook.flipper.core.FlipperResponder;
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONTokener;
|
||||
|
||||
/**
|
||||
* This class manages all loaded FlipperPlugins. It is a singleton to make sure plugin states are
|
||||
* preserved even when the native modules get reloaded (e.g. due to a reload in RN). This avoids
|
||||
* loosing our connections with Flipper.
|
||||
*
|
||||
* <p>Note that this manager is not bound to a specific FlipperModule instance, as that might be
|
||||
* swapped in and out over time.
|
||||
*/
|
||||
public final class FlipperReactNativeJavaScriptPluginManager {
|
||||
private static FlipperReactNativeJavaScriptPluginManager sInstance;
|
||||
|
||||
public static synchronized FlipperReactNativeJavaScriptPluginManager getInstance() {
|
||||
if (sInstance == null) {
|
||||
sInstance = new FlipperReactNativeJavaScriptPluginManager();
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
private final FlipperClient mFlipperClient;
|
||||
|
||||
// uniqueResponderId -> ResponderObject
|
||||
private final Map<String, FlipperResponder> mResponders = new ConcurrentHashMap<>();
|
||||
// generated the next responder id
|
||||
private final AtomicLong mResponderId = new AtomicLong();
|
||||
|
||||
private FlipperReactNativeJavaScriptPluginManager() {
|
||||
mFlipperClient = AndroidFlipperClient.getInstanceIfInitialized();
|
||||
}
|
||||
|
||||
public void registerPlugin(
|
||||
FlipperModule module,
|
||||
final String pluginId,
|
||||
final Boolean inBackground,
|
||||
final Callback statusCallback) {
|
||||
if (mFlipperClient == null) {
|
||||
// Flipper is not available in this build
|
||||
statusCallback.invoke("noflipper");
|
||||
return;
|
||||
}
|
||||
final FlipperReactNativeJavaScriptPlugin existing = getPlugin(pluginId);
|
||||
if (existing != null) {
|
||||
// Make sure events are emitted on the right application context
|
||||
existing.setModule(module);
|
||||
// this happens if the plugin hot reloaded on JS side, but we had it here already
|
||||
if (existing.isConnected()) {
|
||||
existing.fireOnConnect();
|
||||
}
|
||||
statusCallback.invoke("ok");
|
||||
return;
|
||||
}
|
||||
// we always create a new plugin class on the fly,
|
||||
// as Flipper only allows one plugin per type to be registered!
|
||||
final FlipperPlugin plugin =
|
||||
new FlipperReactNativeJavaScriptPlugin(module, pluginId, inBackground) {
|
||||
// inner class with no new members
|
||||
};
|
||||
mFlipperClient.addPlugin(plugin);
|
||||
statusCallback.invoke("ok");
|
||||
}
|
||||
|
||||
void send(String pluginId, String method, String data) {
|
||||
final FlipperReactNativeJavaScriptPlugin plugin = getPlugin(pluginId);
|
||||
if (data == null) {
|
||||
plugin.getConnection().send(method, (FlipperObject) null);
|
||||
return;
|
||||
}
|
||||
// Optimization: throwing raw strings around to the desktop would probably avoid some double
|
||||
// parsing...
|
||||
final Object parsedData = parseJSON(data);
|
||||
if (parsedData instanceof FlipperArray) {
|
||||
plugin.getConnection().send(method, (FlipperArray) parsedData);
|
||||
} else {
|
||||
plugin.getConnection().send(method, (FlipperObject) parsedData);
|
||||
}
|
||||
}
|
||||
|
||||
void reportErrorWithMetadata(String pluginId, String reason, String stackTrace) {
|
||||
getPlugin(pluginId).getConnection().reportErrorWithMetadata(reason, stackTrace);
|
||||
}
|
||||
|
||||
void reportError(String pluginId, String error) {
|
||||
getPlugin(pluginId).getConnection().reportError(new Error(error));
|
||||
}
|
||||
|
||||
void subscribe(FlipperModule module, String pluginId, String method) {
|
||||
final FlipperReactNativeJavaScriptReceiver receiver =
|
||||
new FlipperReactNativeJavaScriptReceiver(this, module, pluginId, method);
|
||||
// Fresh connection should be the case for a new subscribe...
|
||||
getPlugin(pluginId).getConnection().receive(method, receiver);
|
||||
}
|
||||
|
||||
void respondSuccess(String responderId, String data) {
|
||||
final FlipperResponder responder = mResponders.remove(responderId);
|
||||
if (data == null) {
|
||||
responder.success();
|
||||
} else {
|
||||
final Object parsedData = parseJSON(data);
|
||||
if (parsedData instanceof FlipperArray) {
|
||||
responder.success((FlipperArray) parsedData);
|
||||
} else {
|
||||
responder.success((FlipperObject) parsedData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void respondError(String responderId, String data) {
|
||||
final FlipperResponder responder = mResponders.remove(responderId);
|
||||
final Object parsedData = parseJSON(data);
|
||||
if (parsedData instanceof FlipperArray) {
|
||||
responder.success((FlipperArray) parsedData);
|
||||
} else {
|
||||
responder.success((FlipperObject) parsedData);
|
||||
}
|
||||
}
|
||||
|
||||
private FlipperReactNativeJavaScriptPlugin getPlugin(String pluginId) {
|
||||
return mFlipperClient.getPlugin(pluginId);
|
||||
}
|
||||
|
||||
String createResponderId(FlipperResponder responder) {
|
||||
final String id = String.valueOf(mResponderId.incrementAndGet());
|
||||
mResponders.put(id, responder);
|
||||
return id;
|
||||
}
|
||||
|
||||
private static Object /* FlipperArray | FlipperObject */ parseJSON(String json) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
// returns either a FlipperObject or Flipper array, pending the data
|
||||
try {
|
||||
final JSONTokener tokener = new JSONTokener(json);
|
||||
final char firstChar = tokener.nextClean();
|
||||
tokener.back();
|
||||
if (firstChar == '[') {
|
||||
return new FlipperArray(new JSONArray(tokener));
|
||||
} else {
|
||||
return new FlipperObject(new JSONObject(tokener));
|
||||
}
|
||||
} catch (final JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.flipper.reactnative;
|
||||
|
||||
import com.facebook.flipper.core.FlipperObject;
|
||||
import com.facebook.flipper.core.FlipperReceiver;
|
||||
import com.facebook.flipper.core.FlipperResponder;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
|
||||
public class FlipperReactNativeJavaScriptReceiver implements FlipperReceiver {
|
||||
String plugin;
|
||||
String method;
|
||||
FlipperReactNativeJavaScriptPluginManager manager;
|
||||
FlipperModule module;
|
||||
|
||||
public FlipperReactNativeJavaScriptReceiver(
|
||||
FlipperReactNativeJavaScriptPluginManager manager,
|
||||
FlipperModule module,
|
||||
String plugin,
|
||||
String method) {
|
||||
this.plugin = plugin;
|
||||
this.method = method;
|
||||
this.manager = manager;
|
||||
this.module = module;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(FlipperObject params, FlipperResponder responder) {
|
||||
final WritableMap eventData = Arguments.createMap();
|
||||
eventData.putString("plugin", plugin);
|
||||
eventData.putString("method", method);
|
||||
eventData.putString("params", params.toJsonString());
|
||||
if (responder != null) {
|
||||
final String responderId = manager.createResponderId(responder);
|
||||
eventData.putString("responderId", responderId);
|
||||
}
|
||||
module.sendJSEvent("react-native-flipper-receive-event", eventData);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user