Skip to content

Commit c048837

Browse files
LearningDavefacebook-github-bot
authored andcommitted
run-android-on-specific-device
Summary: At the moment the run-android command from the react-native cli does run all available devices at once. However it would be extreme useful to have the option to choose only one specific device/simulator. Therefore i've created a new flag for the command 'run-android': --deviceIdFromList 'deviceIdFromList' in order to install and launch apps on a specific device/simulator from the command line. 'deviceIdFromList' is the id listed on the output of the command 'adb devices'. I've tested my code with the following commands: react-native run-android --deviceIdFromList "Not existing id" react-native run-android --deviceIdFromList react-native run-android --deviceIdFromList "id of a simulator" react-native run-android --deviceIdFromList "id of a device" Output: ![not-existing-device](https://cloud.githubusercontent.com/assets/9102810/17931086/d843abc8-6a09-11e6-995d-8c737dd5ed5c.png) ![empty-flag](https://cloud.githubusercontent.com/assets/9102810/17931087/d8443930-6a09-11e6-94f3-d Closes facebook/react-native#9568 Differential Revision: D4335133 Pulled By: mkonicek fbshipit-source-id: a827628316be1b5751225851323b1131f451574c
1 parent b285bc0 commit c048837

File tree

1 file changed

+126
-94
lines changed

1 file changed

+126
-94
lines changed

runAndroid/runAndroid.js

Lines changed: 126 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88
*/
99
'use strict';
1010

11-
const adb = require('./adb');
1211
const chalk = require('chalk');
1312
const child_process = require('child_process');
1413
const fs = require('fs');
15-
const isPackagerRunning = require('../util/isPackagerRunning');
1614
const path = require('path');
15+
const isPackagerRunning = require('../util/isPackagerRunning');
16+
const Promise = require('promise');
17+
const adb = require('./adb');
1718

1819
// Verifies this is an Android project
1920
function checkAndroid(root) {
@@ -76,9 +77,98 @@ function tryRunAdbReverse(device) {
7677

7778
// Builds the app and runs it on a connected emulator / device.
7879
function buildAndRun(args) {
80+
process.chdir(path.join(args.root, 'android'));
81+
const cmd = process.platform.startsWith('win')
82+
? 'gradlew.bat'
83+
: './gradlew';
84+
85+
const packageName = fs.readFileSync(
86+
'app/src/main/AndroidManifest.xml',
87+
'utf8'
88+
).match(/package="(.+?)"/)[1];
89+
90+
const adbPath = getAdbPath();
91+
if (args.deviceId) {
92+
runOnSpecificDevice(args, cmd, packageName, adbPath);
93+
} else {
94+
runOnAllDevices(args, cmd, packageName, adbPath);
95+
}
96+
}
97+
98+
function runOnSpecificDevice(args, gradlew, packageName, adbPath) {
99+
let devices = adb.getDevices();
100+
if (devices && devices.length > 0) {
101+
if (devices.indexOf(args.deviceId) !== -1) {
102+
buildApk(gradlew);
103+
installAndLaunchOnDevice(args, args.deviceId, packageName, adbPath);
104+
} else {
105+
console.log('Could not find device with the id: "' + args.deviceId + '".');
106+
console.log('Choose one of the following:');
107+
console.log(devices);
108+
}
109+
} else {
110+
console.log('No Android devices connected.');
111+
}
112+
}
113+
114+
function buildApk(gradlew) {
79115
try {
80-
adb.getDevices().map((device) => tryRunAdbReverse(device));
116+
console.log(chalk.bold(
117+
'Building the app...'
118+
));
81119

120+
// using '-x lint' in order to ignore linting errors while building the apk
121+
child_process.execFileSync(gradlew, ['build', '-x', 'lint'], {
122+
stdio: [process.stdin, process.stdout, process.stderr],
123+
});
124+
} catch (e) {
125+
console.log(chalk.red(
126+
'Could not build the app on the device, read the error above for details.\n'
127+
));
128+
}
129+
}
130+
131+
function tryInstallAppOnDevice(args, device) {
132+
try {
133+
const pathToApk = 'app/build/outputs/apk/app-debug.apk';
134+
const adbPath = getAdbPath();
135+
const adbArgs = ['-s', device, 'install', pathToApk];
136+
console.log(chalk.bold(
137+
`Installing the app on the device (cd android && adb -s ${device} install ${pathToApk}`
138+
));
139+
child_process.execFileSync(adbPath, adbArgs, {
140+
stdio: [process.stdin, process.stdout, process.stderr],
141+
});
142+
} catch (e) {
143+
console.log(e.message);
144+
console.log(chalk.red(
145+
'Could not install the app on the device, read the error above for details.\n'
146+
));
147+
}
148+
}
149+
150+
function tryLaunchAppOnDevice(device, packageName, adbPath) {
151+
try {
152+
const adbArgs = ['-s', device, 'shell', 'am', 'start', '-n', packageName + '/.MainActivity'];
153+
console.log(chalk.bold(
154+
`Starting the app on ${device} (${adbPath} ${adbArgs.join(' ')})...`
155+
));
156+
child_process.spawnSync(adbPath, adbArgs, {stdio: 'inherit'});
157+
} catch (e) {
158+
console.log(chalk.red(
159+
'adb invocation failed. Do you have adb in your PATH?'
160+
));
161+
}
162+
}
163+
164+
function installAndLaunchOnDevice(args, selectedDevice, packageName, adbPath) {
165+
tryRunAdbReverse(selectedDevice);
166+
tryInstallAppOnDevice(args, selectedDevice);
167+
tryLaunchAppOnDevice(selectedDevice, packageName, adbPath);
168+
}
169+
170+
function runOnAllDevices(args, cmd, packageName, adbPath){
171+
try {
82172
const gradleArgs = [];
83173
if (args.variant) {
84174
gradleArgs.push('install' +
@@ -92,46 +182,15 @@ function buildAndRun(args) {
92182
args.flavor[0].toUpperCase() + args.flavor.slice(1)
93183
);
94184
} else {
95-
gradleArgs.push('install');
185+
gradleArgs.push('installDebug');
96186
}
97187

98-
// Append the build type to the current gradle install configuration.
99-
// By default it will generate `installDebug`.
100-
gradleArgs[0] =
101-
gradleArgs[0] + args.configuration[0].toUpperCase() + args.configuration.slice(1);
102-
103-
// Get the Android project directory.
104-
const androidProjectDir = path.join(args.root, 'android');
105-
106-
if (args.configuration.toUpperCase() === 'RELEASE') {
107-
console.log(chalk.bold(
108-
'Generating the bundle for the release build...'
109-
));
110-
111-
child_process.execSync(
112-
'react-native bundle ' +
113-
'--platform android ' +
114-
'--dev false ' +
115-
'--entry-file index.android.js ' +
116-
`--bundle-output ${androidProjectDir}/app/src/main/assets/index.android.bundle ` +
117-
`--assets-dest ${androidProjectDir}/app/src/main/res/`,
118-
{
119-
stdio: [process.stdin, process.stdout, process.stderr],
120-
}
121-
);
188+
if (args.installDebug) {
189+
gradleArgs.push(args.installDebug);
122190
}
123191

124-
// Change to the Android directory.
125-
process.chdir(androidProjectDir);
126-
127-
// Get the gradle binary for the current platform.
128-
const cmd = process.platform.startsWith('win')
129-
? 'gradlew.bat'
130-
: './gradlew';
131-
132192
console.log(chalk.bold(
133-
'Building and installing the app on the device ' +
134-
`(cd android && ${cmd} ${gradleArgs.join(' ')})...`
193+
`Building and installing the app on the device (cd android && ${cmd} ${gradleArgs.join(' ')}...`
135194
));
136195

137196
child_process.execFileSync(cmd, gradleArgs, {
@@ -149,50 +208,33 @@ function buildAndRun(args) {
149208
// `console.log(e.stderr)`
150209
return Promise.reject();
151210
}
152-
153-
try {
154-
const packageName = fs.readFileSync(
155-
'app/src/main/AndroidManifest.xml',
156-
'utf8'
157-
).match(/package="(.+?)"/)[1];
158-
159-
const adbPath = getAdbPath();
160-
161211
const devices = adb.getDevices();
162-
163212
if (devices && devices.length > 0) {
164213
devices.forEach((device) => {
165-
166-
const adbArgs =
167-
['-s', device, 'shell', 'am', 'start', '-n', packageName + '/.' + args.mainActivity];
168-
169-
console.log(chalk.bold(
170-
`Starting the app on ${device} (${adbPath} ${adbArgs.join(' ')})...`
171-
));
172-
173-
child_process.spawnSync(adbPath, adbArgs, {stdio: 'inherit'});
214+
tryRunAdbReverse(device);
215+
tryLaunchAppOnDevice(device, packageName, adbPath);
174216
});
175217
} else {
176-
// If we cannot execute based on adb devices output, fall back to
177-
// shell am start
178-
const fallbackAdbArgs = [
179-
'shell', 'am', 'start', '-n', packageName + '/.MainActivity'
180-
];
181-
console.log(chalk.bold(
182-
`Starting the app (${adbPath} ${fallbackAdbArgs.join(' ')}...`
183-
));
184-
child_process.spawnSync(adbPath, fallbackAdbArgs, {stdio: 'inherit'});
218+
try {
219+
// If we cannot execute based on adb devices output, fall back to
220+
// shell am start
221+
const fallbackAdbArgs = [
222+
'shell', 'am', 'start', '-n', packageName + '/.MainActivity'
223+
];
224+
console.log(chalk.bold(
225+
`Starting the app (${adbPath} ${fallbackAdbArgs.join(' ')}...`
226+
));
227+
child_process.spawnSync(adbPath, fallbackAdbArgs, {stdio: 'inherit'});
228+
} catch (e) {
229+
console.log(chalk.red(
230+
'adb invocation failed. Do you have adb in your PATH?'
231+
));
232+
// stderr is automatically piped from the gradle process, so the user
233+
// should see the error already, there is no need to do
234+
// `console.log(e.stderr)`
235+
return Promise.reject();
236+
}
185237
}
186-
187-
} catch (e) {
188-
console.log(chalk.red(
189-
'adb invocation failed. Do you have adb in your PATH?'
190-
));
191-
// stderr is automatically piped from the gradle process, so the user
192-
// should see the error already, there is no need to do
193-
// `console.log(e.stderr)`
194-
return Promise.reject();
195-
}
196238
}
197239

198240
function startServerInNewWindow() {
@@ -206,9 +248,7 @@ function startServerInNewWindow() {
206248

207249
if (process.platform === 'darwin') {
208250
if (yargV.open) {
209-
return (
210-
child_process.spawnSync('open', ['-a', yargV.open, launchPackagerScript], procConfig)
211-
);
251+
return child_process.spawnSync('open', ['-a', yargV.open, launchPackagerScript], procConfig);
212252
}
213253
return child_process.spawnSync('open', [launchPackagerScript], procConfig);
214254

@@ -233,27 +273,19 @@ module.exports = {
233273
description: 'builds your app and starts it on a connected Android emulator or device',
234274
func: runAndroid,
235275
options: [{
276+
command: '--install-debug',
277+
}, {
236278
command: '--root [string]',
237-
description:
238-
'Override the root directory for the android build ' +
239-
'(which contains the android directory)',
279+
description: 'Override the root directory for the android build (which contains the android directory)',
240280
default: '',
241281
}, {
242282
command: '--flavor [string]',
243283
description: '--flavor has been deprecated. Use --variant instead',
244-
}, {
245-
command: '--configuration [string]',
246-
description:
247-
'You can use `Release` or `Debug`. ' +
248-
'This creates a build based on the selected configuration. ' +
249-
'If you want to use the `Release` configuration make sure you have the ' +
250-
'`signingConfig` configured at `app/build.gradle`.',
251-
default: 'Debug'
252284
}, {
253285
command: '--variant [string]',
254286
}, {
255-
command: '--main-activity [string]',
256-
description: 'Name of the activity to start',
257-
default: 'MainActivity'
287+
command: '--deviceId [string]',
288+
description: 'builds your app and starts it on a specific device/simulator with the ' +
289+
'given device id (listed by running "adb devices" on the command line).',
258290
}],
259-
};
291+
};

0 commit comments

Comments
 (0)