Skip to content

run-android on real device using custom flavors #1373

@dmitryn

Description

@dmitryn

Ask your Question

Hello,

I didn't open a bug for this as this is more like a question i think.

Running run-android --deviceId=a28385e5 results in ./gradlew build -x lint which is not the command i expect to be running when i have multiple flavors.

For instance, let's assume app has flavors: "dev" and "prod". Then i expect ./gradlew installDevDebug command to be running, but not ./gradlew build -x lint which builds app for every flavor (including "prod" which we don't need).

Here is the custom patch i'm using:

diff --git a/node_modules/@react-native-community/cli-platform-android/build/commands/runAndroid/index.js b/node_modules/@react-native-community/cli-platform-android/build/commands/runAndroid/index.js
index 59b7aef..befc5f9 100644
--- a/node_modules/@react-native-community/cli-platform-android/build/commands/runAndroid/index.js
+++ b/node_modules/@react-native-community/cli-platform-android/build/commands/runAndroid/index.js
@@ -159,7 +159,7 @@ function runOnSpecificDevice(args, gradlew, packageName, adbPath, androidProject
 
   if (devices.length > 0 && deviceId) {
     if (devices.indexOf(deviceId) !== -1) {
-      buildApk(gradlew, androidProject.sourceDir);
+      buildApk(gradlew, androidProject.sourceDir, args, androidProject);
       installAndLaunchOnDevice(args, deviceId, packageName, adbPath, androidProject);
     } else {
       _cliTools().logger.error(`Could not find device with the id: "${deviceId}". Please choose one of the following:`, ...devices);
@@ -169,10 +169,31 @@ function runOnSpecificDevice(args, gradlew, packageName, adbPath, androidProject
   }
 }
 
-function buildApk(gradlew, sourceDir) {
+function getTaskNames(appName, commands) {
+  return appName ? commands.map(command => `${appName}:${command}`) : commands;
+}
+
+function toPascalCase(value) {
+  return value !== '' ? value[0].toUpperCase() + value.slice(1) : value;
+}
+
+function buildApk(gradlew, sourceDir, args, androidProject) {
   try {
-    // using '-x lint' in order to ignore linting errors while building the apk
-    const gradleArgs = ['build', '-x', 'lint'];
+
+    let installTask = "install";
+    const buildType = toPascalCase(args.type) || "Debug";
+
+    if (args.flavor) {
+      installTask += toPascalCase(args.flavor);
+    }
+    installTask += buildType;
+
+    const tasks = args.tasks || [installTask];
+    const gradleArgs = getTaskNames(args.appFolder || androidProject.appName, tasks);
+
+    if (args.port != null) {
+      gradleArgs.push('-PreactNativeDevServerPort=' + args.port);
+    }
 
     _cliTools().logger.info('Building the app...');
 
@@ -194,14 +215,18 @@ function tryInstallAppOnDevice(args, adbPath, device, androidProject) {
       appName,
       sourceDir
     } = androidProject;
-    const {
-      appFolder
-    } = args;
-    const variant = args.variant.toLowerCase();
-    const buildDirectory = `${sourceDir}/${appName}/build/outputs/apk/${variant}`;
-    const apkFile = getInstallApkName(appFolder || appName, // TODO: remove appFolder
-    adbPath, variant, device, buildDirectory);
-    const pathToApk = `${buildDirectory}/${apkFile}`;
+    const buildType = args.type || "debug"
+    let buildDirectory = `${sourceDir}/${appName}/build/outputs/apk`;
+    if (args.flavor) {
+      buildDirectory += `/${args.flavor}`;
+    }
+    buildDirectory += `/${buildType}`;
+    let apkFileName = "app";
+    if (args.flavor) {
+      apkFileName += `-${args.flavor}`;
+    }
+    apkFileName += `-${buildType}.apk`
+    const pathToApk = `${buildDirectory}/${apkFileName}`;
     const adbArgs = ['-s', device, 'install', '-r', '-d', pathToApk];
 
     _cliTools().logger.info(`Installing the app on the device "${device}"...`);
@@ -216,28 +241,6 @@ function tryInstallAppOnDevice(args, adbPath, device, androidProject) {
   }
 }
 
-function getInstallApkName(appName, adbPath, variant, device, buildDirectory) {
-  const availableCPUs = _adb.default.getAvailableCPUs(adbPath, device); // check if there is an apk file like app-armeabi-v7a-debug.apk
-
-
-  for (const availableCPU of availableCPUs.concat('universal')) {
-    const apkName = `${appName}-${availableCPU}-${variant}.apk`;
-
-    if (_fs().default.existsSync(`${buildDirectory}/${apkName}`)) {
-      return apkName;
-    }
-  } // check if there is a default file like app-debug.apk
-
-
-  const apkName = `${appName}-${variant}.apk`;
-
-  if (_fs().default.existsSync(`${buildDirectory}/${apkName}`)) {
-    return apkName;
-  }
-
-  throw new (_cliTools().CLIError)('Could not find the correct install APK file.');
-}
-
 function installAndLaunchOnDevice(args, selectedDevice, packageName, adbPath, androidProject) {
   (0, _tryRunAdbReverse.default)(args.port, selectedDevice);
   tryInstallAppOnDevice(args, adbPath, selectedDevice, androidProject);
@@ -320,8 +323,11 @@ var _default = {
     description: '[DEPRECATED - root is discovered automatically] Override the root directory for the android build (which contains the android directory)',
     default: ''
   }, {
-    name: '--variant [string]',
-    description: "Specify your app's build variant",
+    name: '--flavor [string]',
+    description: "Specify your app's build flavor"
+  }, {
+    name: '--type [string]',
+    description: "Specify your app's build type",
     default: 'debug'
   }, {
     name: '--appFolder [string]',
diff --git a/node_modules/@react-native-community/cli-platform-android/build/commands/runAndroid/runOnAllDevices.js b/node_modules/@react-native-community/cli-platform-android/build/commands/runAndroid/runOnAllDevices.js
index c12c4bb..b91fed3 100644
--- a/node_modules/@react-native-community/cli-platform-android/build/commands/runAndroid/runOnAllDevices.js
+++ b/node_modules/@react-native-community/cli-platform-android/build/commands/runAndroid/runOnAllDevices.js
@@ -80,7 +80,15 @@ async function runOnAllDevices(args, cmd, packageName, adbPath, androidProject)
   }
 
   try {
-    const tasks = args.tasks || ['install' + toPascalCase(args.variant)];
+    let installTask = "install";
+    const buildType = toPascalCase(args.type) || "Debug";
+
+    if (args.flavor) {
+      installTask += toPascalCase(args.flavor);
+    }
+    installTask += buildType;
+
+    const tasks = args.tasks || [installTask];
     const gradleArgs = getTaskNames(args.appFolder || androidProject.appName, tasks);
 
     if (args.port != null) {

The command i'm using with this patch is run-android --deviceId=a28385e5 --flavor=dev which results in this gradle command: ./gradlew app:installDevDebug

I also renamed variant to flavor and added type argument when you want different build type (not "debug" which is default). I think of "variant" more like combination of build flavor and build type so having them separate makes more sense imo https://developer.android.com/studio/build/build-variants?authuser=1

Feel free to use it or modify for your needs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionFurther information is requestedstale

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions