Conditionally include dev client modules on Android

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:

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 {
  public void onNewIntent(Intent intent) {
+    if (DevLauncherController.tryToHandleIntent(this, intent) && BuildConfig.USE_EXPO) {
   * Returns the name of the main component registered from JavaScript. This is used to schedule
   * rendering of the component.
  protected String getMainComponentName() {
    return "AppName";

  protected ReactActivityDelegate createReactActivityDelegate() {
+    if (BuildConfig.USE_EXPO) {
      return DevLauncherController.wrapReactActivityDelegate(this, () -> new ReactActivityDelegate(this, getMainComponentName()) {
        protected ReactRootView createRootView() {
          return new ReactRootView(MainActivity.this);
+    } else {
+      return new ReactActivityDelegate(this, getMainComponentName()) {
+        @Override
+        protected ReactRootView createRootView() {
+          return new ReactRootView(MainActivity.this);
+        }
+      };

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) {
        public boolean getUseDeveloperSupport() {
          return BuildConfig.DEBUG;

        protected List<ReactPackage> getPackages() {
          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;

        protected String getJSMainModuleName() {
          return "index";

  public ReactNativeHost getReactNativeHost() {
    return mReactNativeHost;

  public void 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("");
            .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
            .invoke(null, context, reactInstanceManager);
      } catch (ClassNotFoundException e) {
      } catch (NoSuchMethodException e) {
      } catch (IllegalAccessException e) {
      } catch (InvocationTargetException e) {

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.


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:


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,