Skip to content

Commit

Permalink
perf: ⬆️ Improve QR code connection performance
Browse files Browse the repository at this point in the history
  • Loading branch information
viarotel committed Jan 24, 2025
1 parent fd2e639 commit b413889
Show file tree
Hide file tree
Showing 18 changed files with 312 additions and 104 deletions.
2 changes: 1 addition & 1 deletion README-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ Windows 及 Linux 端内部集成了 Gnirehtet, 用于提供 PC 到安卓设
27. 对设备进行分组(通过筛选备注) ✅
28. 改进历史设备连接体验 ✅
29. 文件管理支持上传目录 ✅
30. 更好的文件上传进度展示 🚧
30. 更好的功能进度展示 🚧

## 常见问题

Expand Down
2 changes: 1 addition & 1 deletion README-RU.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ Gnirehtet встроен в приложения для Windows и Linux, что
26. Группировать устройства (по фильтрации примечаний) ✅
27. Улучшен опыт подключения истории устройств ✅
29. Управление файлами поддерживает загрузку каталогов ✅
30. Улучшенное отображение прогресса загрузки файлов 🚧
30. Улучшенное отображение прогресса функций 🚧

## Часто задаваемые вопросы

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ Refer to [scrcpy/doc/shortcuts](https://github.com/Genymobile/scrcpy/blob/master
27. Group devices (by filtering remarks) ✅
28. Improved history device connection experience ✅
29. File management supports uploading directories ✅
30. Improved file upload progress display 🚧
30. Better feature progress display 🚧

## FAQ

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Bonjour } from 'bonjour-service'
import net from 'node:net'
import appStore from '$electron/helpers/store.js'
import { parseDeviceId } from '$/utils/index.js'

export const MDNS_CONFIG = {
PAIRING_TYPE: 'adb-tls-pairing',
Expand Down Expand Up @@ -74,15 +75,15 @@ export class DeviceScanner {
}
}

export class AdbConnectionMonitor {
export class AdbScanner {
constructor() {
this.deviceScanner = new DeviceScanner()
this.isActive = false
this.adb = null
this.onStatus = () => {}
}

async startQrCodeScanning(options) {
async connect(options) {
this.validateOptions(options)

const {
Expand All @@ -101,25 +102,42 @@ export class AdbConnectionMonitor {
await this.pairWithDevice(device, password)

this.onStatus('connecting')
try {
const connectDevice = await this.waitForDeviceConnect(device)
await this.connectToDevice(connectDevice)
}
catch (error) {
if (error.code === ERROR_CODES.TIMEOUT) {
this.onStatus('connecting-fallback')
// 使用回退端口尝试连接
const fallbackPort = this.getBackPort(device)

// 先尝试使用历史端口连接
const backPort = this.getBackPort(device)

if (backPort && ![5555].includes(backPort)) {
try {
await this.connectToDevice({
...device,
port: fallbackPort,
port: backPort,
})
this.onStatus('connected')
return {
success: true,
device,
}
}
else {
throw error
catch (error) {
// The historical port connection failed. Continue to use the standard procedure
console.log('Fallback port connection failed, trying standard flow')
}
}

// Standard connection process
try {
const connectDevice = await this.waitForDeviceConnect(device)
await this.connectToDevice(connectDevice)
}
catch (error) {
this.onStatus('connecting-fallback')
// Last attempt
await this.connectToDevice({
...device,
port: 5555,
})
}

this.onStatus('connected')

return {
Expand All @@ -129,7 +147,6 @@ export class AdbConnectionMonitor {
}
catch (error) {
this.onStatus('error', error.message)

return {
success: false,
error: error.message,
Expand Down Expand Up @@ -229,8 +246,9 @@ export class AdbConnectionMonitor {

const value = Object.entries(devices).reduce((port, [key, value]) => {
if (key.includes(device.address)) {
port = key.split(':')[1]
port = parseDeviceId(key).port
}

return port
}, 5555)

Expand All @@ -243,4 +261,4 @@ export class AdbConnectionMonitor {
}
}

export default new AdbConnectionMonitor()
export default new AdbScanner()
90 changes: 41 additions & 49 deletions electron/exposes/adb/helpers/uploader/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ export class ADBUploader {
...options,
}
this.isCancelled = false

this.stats = {
totalBytes: 0,
uploadedBytes: 0,
startTime: 0,
}

this._lastBytes = 0
}

/**
Expand Down Expand Up @@ -45,9 +53,11 @@ export class ADBUploader {

async uploadTo(remoteDir, localPaths, deviceId = null) {
this.isCancelled = false
const startTime = Date.now()
let totalBytes = 0
let uploadedBytes = 0
this.stats = {
totalBytes: 0,
uploadedBytes: 0,
startTime: Date.now(),
}

try {
const paths = Array.isArray(localPaths) ? localPaths : [localPaths]
Expand All @@ -58,10 +68,10 @@ export class ADBUploader {
const stats = await fs.stat(localPath)
if (stats.isDirectory()) {
const files = await this._getFileList(localPath)
totalBytes += files.reduce((acc, file) => acc + file.size, 0)
this.stats.totalBytes += files.reduce((acc, file) => acc + file.size, 0)
}
else {
totalBytes += stats.size
this.stats.totalBytes += stats.size
}
}
}
Expand Down Expand Up @@ -102,33 +112,19 @@ export class ADBUploader {
// 目录或文件开始上传回调
this.options.onDirectoryStart(localPath, {
isDirectory,
totalBytes,
uploadedBytes,
startTime,
...this.stats,
})

const success = isDirectory
? await this._uploadSingleDirectory(sync, localPath, targetRemotePath, {
totalBytes,
uploadedBytes,
startTime,
onProgress: bytes => uploadedBytes += bytes,
})
: await this._uploadSingleFile(sync, localPath, targetRemotePath, {
totalBytes,
uploadedBytes,
startTime,
onProgress: bytes => uploadedBytes += bytes,
})
? await this._uploadSingleDirectory(sync, localPath, targetRemotePath)
: await this._uploadSingleFile(sync, localPath, targetRemotePath)

results.push({ localPath, success })

this.options.onDirectoryComplete(localPath, success, {
isDirectory,
totalBytes,
uploadedBytes,
startTime,
duration: Date.now() - startTime,
...this.stats,
duration: Date.now() - this.stats.startTime,
})
}
catch (error) {
Expand All @@ -145,9 +141,8 @@ export class ADBUploader {
success: results.every(r => r.success),
results,
stats: {
totalBytes,
uploadedBytes,
duration: Date.now() - startTime,
...this.stats,
duration: Date.now() - this.stats.startTime,
},
}
}
Expand All @@ -163,26 +158,25 @@ export class ADBUploader {
error: error.message,
results: [],
stats: {
totalBytes,
uploadedBytes,
duration: Date.now() - startTime,
...this.stats,
duration: Date.now() - this.stats.startTime,
},
}
}
}

async _uploadSingleFile(sync, localPath, remotePath, stats) {
async _uploadSingleFile(sync, localPath, remotePath) {
if (this.options.validateFile) {
await this.options.validateFile(localPath)
}

this.options.onFileStart(localPath, stats)
await this._uploadFileWithRetry(sync, localPath, remotePath, stats)
this.options.onFileComplete(localPath, stats)
this.options.onFileStart(localPath, this.stats)
await this._uploadFileWithRetry(sync, localPath, remotePath)
this.options.onFileComplete(localPath, this.stats)
return true
}

async _uploadSingleDirectory(sync, localDir, remoteDir, stats) {
async _uploadSingleDirectory(sync, localDir, remoteDir) {
const files = await this._getFileList(localDir)

for (const file of files) {
Expand All @@ -194,10 +188,7 @@ export class ADBUploader {
}

const remotePath = path.posix.join(remoteDir, file.relativePath)
await this._uploadSingleFile(sync, file.localPath, remotePath, {
...stats,
fileSize: file.size,
})
await this._uploadSingleFile(sync, file.localPath, remotePath)
}
return true
}
Expand Down Expand Up @@ -232,7 +223,7 @@ export class ADBUploader {
return fileList
}

async _uploadFileWithRetry(sync, localPath, remotePath, stats) {
async _uploadFileWithRetry(sync, localPath, remotePath) {
let lastError = null

for (let attempt = 0; attempt < this.options.retries; attempt++) {
Expand All @@ -241,29 +232,30 @@ export class ADBUploader {

try {
await new Promise((resolve, reject) => {
const fileSize = fs.statSync(localPath).size
const transfer = sync.pushFile(localPath, remotePath)

transfer.on('end', resolve)
transfer.on('error', reject)

// More granular progress callbacks
transfer.on('progress', (progressStats) => {
const { bytesTransferred, bytesTotal } = progressStats
stats.onProgress(bytesTransferred - (this._lastBytes || 0))
const bytesTransferred = progressStats.bytesTransferred
const deltaBytes = bytesTransferred - (this._lastBytes || 0)
this.stats.uploadedBytes += deltaBytes
this._lastBytes = bytesTransferred

this.options.onProgress({
file: {
path: localPath,
size: bytesTotal,
size: fileSize,
uploaded: bytesTransferred,
percent: Math.round((bytesTransferred / bytesTotal) * 100),
percent: Math.round((bytesTransferred / fileSize) * 100),
},
total: {
size: stats.totalBytes,
uploaded: stats.uploadedBytes,
percent: Math.round((stats.uploadedBytes / stats.totalBytes) * 100),
elapsed: Date.now() - stats.startTime,
size: this.stats.totalBytes,
uploaded: this.stats.uploadedBytes,
percent: Math.round((this.stats.uploadedBytes / this.stats.totalBytes) * 100),
elapsed: Date.now() - this.stats.startTime,
},
})
})
Expand Down
28 changes: 16 additions & 12 deletions electron/exposes/adb/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import appStore from '$electron/helpers/store.js'
import { formatFileSize } from '$renderer/utils/index'
import { Adb } from '@devicefarmer/adbkit'
import dayjs from 'dayjs'
import adbConnectionMonitor from './helpers/adbConnectionMonitor/index.js'
import { streamToBase64 } from '$electron/helpers/index.js'
import { parseBatteryDump } from './helpers/battery/index.js'
import { ipv6Wrapper, isIpv6 } from './helpers/index.js'
import adbScanner from './helpers/scanner/index.js'
import { ADBUploader } from './helpers/uploader/index.js'

const exec = util.promisify(_exec)
Expand Down Expand Up @@ -163,8 +163,7 @@ const version = async () => client.version()
const watch = async (callback) => {
const tracker = await client.trackDevices()
tracker.on('add', async (ret) => {
const host = await getDeviceIP(ret.id)
callback('add', { ...ret, $host: host })
callback('add', ret)
})

tracker.on('remove', (device) => {
Expand Down Expand Up @@ -247,12 +246,13 @@ async function pull(id, filePath, args = {}) {
})
}

async function connectCode(password, options = {}) {
return adbConnectionMonitor.startQrCodeScanning({
async function scannerConnect(password, options = {}) {
return adbScanner.connect({
password,
adb: {
pair,
connect,
shell,
},
...options,
})
Expand Down Expand Up @@ -310,7 +310,7 @@ async function disconnect(host, port = 5555) {
return stdout
}

async function uploader(options = {}) {
function uploader(options = {}) {
const { deviceId, localPaths, remotePath = '/sdcard/Download', ...initialOptions } = options

const uploader = new ADBUploader({
Expand All @@ -321,11 +321,15 @@ async function uploader(options = {}) {
...initialOptions,
})

return uploader.uploadTo(
remotePath,
localPaths,
deviceId,
)
return {
context: uploader,
start: () => uploader.uploadTo(
remotePath,
localPaths,
deviceId,
),
cancel: () => uploader.cancel(),
}
}

function init() {
Expand Down Expand Up @@ -356,7 +360,7 @@ export default {
pull,
watch,
readdir,
connectCode,
scannerConnect,
battery,
uploader,
}
Loading

0 comments on commit b413889

Please sign in to comment.