After being successful in creating build configurations with and without the custom development client for IOS (see this thread), I wanted to achieve the same thing on the Android side of things.
Current progress
In order to do this, I went through the changes made to the MainActivity
and MainApplication
files and tried to find obvious ways to make the implementation optional. My initial thinking was to add build flavors, where one flavor would contain a USE_EXPO
variable that could be used to create conditions in the code. This is what I came up with:
MainActivity.java
package com.company.appname;
import android.content.Intent;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import expo.modules.devlauncher.DevLauncherController;
import expo.modules.devmenu.react.DevMenuAwareReactActivity;
public class MainActivity extends DevMenuAwareReactActivity {
@Override
public void onNewIntent(Intent intent) {
+ if (DevLauncherController.tryToHandleIntent(this, intent) && BuildConfig.USE_EXPO) {
return;
}
super.onNewIntent(intent);
}
/**
* Returns the name of the main component registered from JavaScript. This is used to schedule
* rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "AppName";
}
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
+ if (BuildConfig.USE_EXPO) {
return DevLauncherController.wrapReactActivityDelegate(this, () -> new ReactActivityDelegate(this, getMainComponentName()) {
@Override
protected ReactRootView createRootView() {
return new ReactRootView(MainActivity.this);
}
});
+ } else {
+ return new ReactActivityDelegate(this, getMainComponentName()) {
+ @Override
+ protected ReactRootView createRootView() {
+ return new ReactRootView(MainActivity.this);
+ }
+ };
}
}
}
MainApplication.java
package com.company.appname;
import android.app.Application;
import android.content.Context;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.soloader.SoLoader;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import expo.modules.devlauncher.DevLauncherController;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost =
new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
return packages;
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
+ if(BuildConfig.USE_EXPO) {
DevLauncherController.initialize(this, getReactNativeHost());
+ }
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
}
/**
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
*
* @param context
* @param reactInstanceManager
*/
private static void initializeFlipper(
Context context, ReactInstanceManager reactInstanceManager) {
if (BuildConfig.DEBUG) {
try {
/*
We use reflection here to pick up the class that initializes Flipper,
since Flipper library is not available in release mode
*/
Class<?> aClass = Class.forName("com.company.appname.ReactNativeFlipper");
aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
I realize that this is most likely a naive way of accomplishing my goal, but it works! Almost. There are (at least) two issues remaining.
Issues
To begin with, the app still includes all modules needed for the custom client, which increases build time and bundle size.
Additionally, running the app with the USE_EXPO
variable set to false
still seems to trigger this bottom sheet:
Question
What would be the right thing to do here? I have tried many search queries and have been unable to find a scenario where this has been implemented before, which actually surprised me given how convenient it would be to have it set up this way (at least IMHO).
Any tips and/or advice would be more than welcome!
Kind Regards,
Malte