Skip to content

Commit fb50a3b

Browse files
author
刘威
committed
fix: reuse existing adb server and forward reliably
1 parent 62e9f5b commit fb50a3b

4 files changed

Lines changed: 272 additions & 134 deletions

File tree

src/main/adb/device.js

Lines changed: 92 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ const INITIAL_DEVICE_RETRY_COUNT = 6;
1212
const INITIAL_DEVICE_RETRY_DELAY_MS = 500;
1313
const BACKGROUND_DEVICE_REFRESH_COUNT = 15;
1414
const BACKGROUND_DEVICE_REFRESH_DELAY_MS = 2000;
15+
const SERVER_CHECK_RETRY_COUNT = 3;
16+
const SERVER_CHECK_RETRY_DELAY_MS = 250;
1517

1618
// Platform-specific adb binary paths
1719
const ADB_PATHS = {
@@ -81,7 +83,7 @@ class DeviceManager extends EventEmitter {
8183
this._adbPath = findAdbPath();
8284

8385
// Check if ADB server is already running
84-
const isRunning = await this._checkServerRunning();
86+
const isRunning = await this._checkServerRunningWithRetry();
8587

8688
if (!isRunning) {
8789
console.log('[ADB] ADB server not running, starting...');
@@ -97,6 +99,16 @@ class DeviceManager extends EventEmitter {
9799
console.log('[ADB] ADB server already running');
98100
}
99101

102+
this._serverRunning = true;
103+
this._startHealthCheck();
104+
105+
// Prime the device list via `adb devices` before loading adbkit.
106+
// adbkit pulls in the usb native addon on Windows, which can block the
107+
// main process for a long time in packaged builds. The binary query is
108+
// enough to surface an already-connected device to the UI immediately.
109+
await this._updateDevicesWithRetry(INITIAL_DEVICE_RETRY_COUNT, INITIAL_DEVICE_RETRY_DELAY_MS);
110+
this._scheduleBackgroundRefreshes();
111+
100112
// Initialize adbkit client
101113
try {
102114
// Prefer adbkit from package.json; keep compatibility with devicefarmer fork if present.
@@ -115,15 +127,17 @@ class DeviceManager extends EventEmitter {
115127
);
116128
}
117129
}
118-
this.client = adbkit.createClient();
119-
120-
// Start device tracking
121-
await this._startTracking();
122-
123-
// Start health check
124-
this._startHealthCheck();
130+
this.client = adbkit.createClient({
131+
bin: this._adbPath,
132+
host: '127.0.0.1',
133+
port: ADB_SERVER_PORT
134+
});
125135

126-
this._serverRunning = true;
136+
// Start adbkit tracking in the background so a slow tracker does not
137+
// block packaged-app startup or initial device visibility.
138+
this._startTracking().catch((trackingErr) => {
139+
console.error('[ADB] Background tracking failed:', trackingErr.message);
140+
});
127141
console.log('[ADB] Device manager initialized');
128142
} catch (err) {
129143
console.error('[ADB] Failed to initialize adbkit:', err.message);
@@ -155,6 +169,21 @@ class DeviceManager extends EventEmitter {
155169
});
156170
}
157171

172+
async _checkServerRunningWithRetry(attempts = SERVER_CHECK_RETRY_COUNT, delayMs = SERVER_CHECK_RETRY_DELAY_MS) {
173+
for (let attempt = 1; attempt <= attempts; attempt++) {
174+
const running = await this._checkServerRunning();
175+
if (running) {
176+
return true;
177+
}
178+
179+
if (attempt < attempts) {
180+
await new Promise((resolve) => setTimeout(resolve, delayMs));
181+
}
182+
}
183+
184+
return false;
185+
}
186+
158187
/**
159188
* Start ADB server using binary adb
160189
*/
@@ -185,7 +214,7 @@ class DeviceManager extends EventEmitter {
185214
finish({ ok: true });
186215
} else {
187216
// Some adb variants return non-zero while server is actually up.
188-
const running = await this._checkServerRunning();
217+
const running = await this._checkServerRunningWithRetry();
189218
if (running) {
190219
console.warn(`[ADB] start-server exited with code ${code}, but server is reachable`);
191220
finish({ ok: true });
@@ -220,11 +249,19 @@ class DeviceManager extends EventEmitter {
220249
});
221250

222251
// Timeout after 10 seconds
223-
const timeout = setTimeout(() => {
252+
const timeout = setTimeout(async () => {
224253
if (!proc.killed) {
225254
proc.kill();
226255
console.log('[ADB] Server start timed out');
227256
}
257+
258+
const running = await this._checkServerRunningWithRetry();
259+
if (running) {
260+
console.warn('[ADB] start-server timed out, but server is reachable');
261+
finish({ ok: true });
262+
return;
263+
}
264+
228265
finish({
229266
ok: false,
230267
message: 'Failed to start ADB server (timeout)',
@@ -243,7 +280,7 @@ class DeviceManager extends EventEmitter {
243280
}
244281

245282
this._healthCheckInterval = setInterval(async () => {
246-
const isRunning = await this._checkServerRunning();
283+
const isRunning = await this._checkServerRunningWithRetry();
247284

248285
if (!isRunning && this._serverRunning) {
249286
console.log('[ADB] Server died, attempting restart...');
@@ -313,46 +350,36 @@ class DeviceManager extends EventEmitter {
313350
async _startTracking() {
314351
if (!this.client) return;
315352

316-
try {
317-
this.tracker = await this.client.trackDevices();
318-
319-
this.tracker.on('add', (device) => {
320-
console.log(`[ADB] Device connected: ${device.id} (${device.type})`);
321-
this._updateDevices();
322-
this.emit('device:connected', device);
323-
});
353+
this.tracker = await this.client.trackDevices();
324354

325-
this.tracker.on('remove', (device) => {
326-
console.log(`[ADB] Device disconnected: ${device.id}`);
327-
this._updateDevices();
328-
this.emit('device:disconnected', device);
329-
});
355+
this.tracker.on('add', (device) => {
356+
console.log(`[ADB] Device connected: ${device.id} (${device.type})`);
357+
this._updateDevices();
358+
this.emit('device:connected', device);
359+
});
330360

331-
this.tracker.on('change', (device) => {
332-
console.log(`[ADB] Device changed: ${device.id} (${device.type})`);
333-
this._updateDevices();
334-
this.emit('device:changed', device);
335-
});
361+
this.tracker.on('remove', (device) => {
362+
console.log(`[ADB] Device disconnected: ${device.id}`);
363+
this._updateDevices();
364+
this.emit('device:disconnected', device);
365+
});
336366

337-
this.tracker.on('end', () => {
338-
console.log('[ADB] Device tracking ended');
339-
this.tracker = null;
340-
});
367+
this.tracker.on('change', (device) => {
368+
console.log(`[ADB] Device changed: ${device.id} (${device.type})`);
369+
this._updateDevices();
370+
this.emit('device:changed', device);
371+
});
341372

342-
this.tracker.on('error', (err) => {
343-
console.error('[ADB] Tracker error:', err.message);
344-
});
373+
this.tracker.on('end', () => {
374+
console.log('[ADB] Device tracking ended');
375+
this.tracker = null;
376+
});
345377

346-
// When adb has just started, listDevices() may briefly return empty even
347-
// though a USB device is already authorized. Retry a few times so the UI
348-
// does not get stuck on "No Devices" after startup.
349-
await this._updateDevicesWithRetry(INITIAL_DEVICE_RETRY_COUNT, INITIAL_DEVICE_RETRY_DELAY_MS);
350-
this._scheduleBackgroundRefreshes();
378+
this.tracker.on('error', (err) => {
379+
console.error('[ADB] Tracker error:', err.message);
380+
});
351381

352-
console.log('[ADB] Device tracking started');
353-
} catch (err) {
354-
console.error('[ADB] Failed to start device tracking:', err.message);
355-
}
382+
console.log('[ADB] Device tracking started');
356383
}
357384

358385
async _updateDevicesWithRetry(maxAttempts, delayMs) {
@@ -374,33 +401,26 @@ class DeviceManager extends EventEmitter {
374401
* Update device list
375402
*/
376403
async _updateDevices() {
377-
if (!this.client) return this.devices;
404+
let devices = await this._listDevicesViaAdbBinary();
405+
if (devices.length > 0) {
406+
this.devices = devices;
407+
this.emit('devices:updated', this.devices);
408+
return this.devices;
409+
}
378410

379-
try {
380-
let devices = await this.client.listDevices();
381-
382-
// adbkit can occasionally return an empty list during server/device warmup
383-
// while `adb devices` already reports the device. Fall back to the binary
384-
// query so the UI does not stay stuck on "No Devices".
385-
if (devices.length === 0) {
386-
const cliDevices = await this._listDevicesViaAdbBinary();
387-
if (cliDevices.length > 0) {
388-
console.warn(`[ADB] adbkit returned 0 devices, using adb binary result (${cliDevices.length})`);
389-
devices = cliDevices;
390-
}
391-
}
411+
if (!this.client) {
412+
this.devices = [];
413+
this.emit('devices:updated', this.devices);
414+
return this.devices;
415+
}
392416

417+
try {
418+
devices = await this.client.listDevices();
393419
this.devices = devices;
394420
this.emit('devices:updated', this.devices);
395421
return this.devices;
396422
} catch (err) {
397-
console.error('[ADB] Failed to list devices:', err.message);
398-
const cliDevices = await this._listDevicesViaAdbBinary();
399-
if (cliDevices.length > 0) {
400-
console.warn(`[ADB] Recovered device list via adb binary after adbkit error (${cliDevices.length})`);
401-
this.devices = cliDevices;
402-
this.emit('devices:updated', this.devices);
403-
}
423+
console.error('[ADB] Failed to list devices via adbkit:', err.message);
404424
return this.devices;
405425
}
406426
}
@@ -476,7 +496,7 @@ class DeviceManager extends EventEmitter {
476496
const tick = async () => {
477497
this._backgroundRefreshTimeout = null;
478498

479-
if (!this.client || this.devices.length > 0 || this._backgroundRefreshRemaining <= 0) {
499+
if (this.devices.length > 0 || this._backgroundRefreshRemaining <= 0) {
480500
return;
481501
}
482502

@@ -505,6 +525,10 @@ class DeviceManager extends EventEmitter {
505525
return this.devices;
506526
}
507527

528+
getAdbPath() {
529+
return this._adbPath;
530+
}
531+
508532
/**
509533
* Get first connected device
510534
*/

0 commit comments

Comments
 (0)