Flutter ADB调试插件flutter_adb的使用

发布于 1周前 作者 phonegap100 来自 Flutter

Flutter ADB调试插件flutter_adb的使用

Native dart 实现的 ADB 网络协议

flutter_adb 是一个基于 Cameron Gutman 的 Java 版本实现的 Dart 包,用于连接到网络上的 Android 设备。它支持自-ADB(即连接到同一设备上运行 Flutter 应用的 ADB 实例)。

使用说明

  1. 创建 ADB 加密对象
    • 根据连接设备的安全设置,您可能需要通过 RSA 密钥对进行身份验证。
    • 该包包含了一个帮助类来生成密钥对并进行设备身份验证,但您也可以提供自己的密钥对。
    • 包使用 pointycastle 进行 RSA 加密,因此您可以使用自己生成的密钥对。
final AsymmetricKeyPair<RSAPublicKey, RSAPrivateKey> keyPair = AdbCrypto.generateRSAKeyPair();
final crypto = AdbCrypto(keyPair: keyPair);
// 或者不传递密钥对以自动生成
final crypto = AdbCrypto();
- 第一次连接设备时,您需要从设备本身授权连接。之后,密钥对将存储在设备上,并且可以在保存和重用密钥对的情况下无需进一步交互连接。
  1. 创建 ADB 连接

    • 创建 ADB 连接对象。
    final adb = AdbConnection(
      '172.0.0.1',
      5555,
      crypto,
    );
    bool connected = await connection.connect();
    // 您还可以监听连接状态更改
    connection.onConnectionChanged.listen((connected) {
      print('Connected: $connected');
    });
    
  2. 打开 shell 等操作

    • 从这里开始,您可以打开 shell 等操作。
    final AdbStream shell = await connection.open('shell:');
    // 或者使用方便的方法
    final AdbStream shell = await connection.openShell();
    
  3. 读取和写入流

    • 读取和写入流。
    // 监听传入数据
    shell.onPayload.listen((payload) {
      print('Received: $payload');
    });
    // 向流写入数据
    bool success = await shell.write('ls\n');
    
    // 关闭流
    shell.sendClose();
    
  4. 使用方便的方法打开 shell 发送单个命令并获取输出

    final String result = Adb.sendSingleCommand(
      'monkey -p com.google.android.googlequicksearchbox 1;sleep 3;input keyevent KEYCODE_HOME',
      ip: '127.0.0.1',
      port: 5555,
      crypto: crypto,
    );
    print(result);
    
    • 这种方法会自动打开连接、打开 shell、发送命令、关闭 shell 和连接,然后给出输出。可以使用 ; 运算符连接多个命令。

示例代码

查看示例应用以了解简单的 ADB 终端实现(使用 riverpod)。

import 'package:flutter/material.dart';
import 'package:flutter_adb/adb_connection.dart';
import 'package:flutter_adb/adb_crypto.dart';
import 'package:flutter_adb/adb_stream.dart';
import 'package:flutter_adb/flutter_adb.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

final StateProvider<AdbConnection> adbConnectionProvider = StateProvider((ref) {
  final crypto = AdbCrypto();
  final connection = AdbConnection('127.0.0.1', 5555, crypto, verbose: true);
  return connection;
});

final FutureProvider<AdbStream> adbStreamProvider = FutureProvider((ref) async {
  final connection = ref.watch(adbConnectionProvider);
  await connection.connect();
  return await connection.openShell();
});

void main() {
  final ProviderContainer container = ProviderContainer();
  WidgetsFlutterBinding.ensureInitialized();
  runApp(UncontrolledProviderScope(container: container, child: const MyApp()));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Adb Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends ConsumerStatefulWidget {
  const MyHomePage({super.key});

  [@override](/user/override)
  ConsumerState<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends ConsumerState<MyHomePage> {
  final _formKey = GlobalKey<FormState>();

  final TextEditingController _ipController = TextEditingController();
  final TextEditingController _portController = TextEditingController();

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
          title: StreamBuilder(
            stream: ref.read(adbConnectionProvider).onConnectionChanged,
            initialData: false,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                if (snapshot.data ?? false) {
                  return Text(
                    'ADB Flutter Example, Connected to: ${ref.watch(adbConnectionProvider).ip}:${ref.watch(adbConnectionProvider).port}',
                    style: const TextStyle(fontSize: 32),
                  );
                } else {
                  return const Text(
                    'ADB Flutter Example, Not connected',
                    style: TextStyle(fontSize: 32),
                  );
                }
              } else {
                return const CircularProgressIndicator();
              }
            },
          ),
        ),
        body: Padding(
          padding: const EdgeInsets.all(16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Form(
                key: _formKey,
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    const Spacer(),
                    IntrinsicWidth(
                      child: TextFormField(
                        controller: _ipController,
                        decoration: InputDecoration(
                          border: const OutlineInputBorder(),
                          hintText: ref.watch(adbConnectionProvider).ip,
                        ),
                        // 验证有效的 IPv4 地址
                        validator: (value {
                          if (value == null || value.isEmpty) {
                            return 'Invalid IP';
                          }
                          if (!RegExp(r'^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$').hasMatch(value)) {
                            return 'Invalid IP';
                          }
                          return null;
                        },
                      ),
                    ),
                    const SizedBox(width: 10),
                    IntrinsicWidth(
                      child: TextFormField(
                        controller: _portController,
                        decoration: InputDecoration(
                          border: const OutlineInputBorder(),
                          hintText: ref.watch(adbConnectionProvider).port.toString(),
                        ),
                        // 验证有效的端口
                        validator: (value {
                          if (value == null || value.isEmpty) {
                            return 'Invalid Port';
                          }
                          if (int.tryParse(value) == null || int.tryParse(value)! < 0 || int.tryParse(value)! > 65535 {
                            return 'Invalid Port';
                          }
                          return null;
                        },
                      ),
                    ),
                    TextButton(
                      onPressed: () async {
                        if (_formKey.currentState!.validate()) {
                          await ref.read(adbConnectionProvider).disconnect();
                          ref.read(adbConnectionProvider.notifier).state = AdbConnection(
                            _ipController.text,
                            int.parse(_portController.text),
                            AdbCrypto(),
                          );
                        }
                      },
                      child: const Text('Connect'),
                    ),
                  ],
                ),
              ),
              Expanded(
                child: ConstrainedBox(
                  constraints: const BoxConstraints(maxWidth: 700),
                  child: ref.watch(adbStreamProvider).maybeWhen(
                    data: (adbStream) {
                      return AdbTerminal(stream: adbStream);
                    },
                    loading: () =&gt; const CircularProgressIndicator(),
                    error: (error, stack) {
                      return Text('Error: $error');
                    },
                    orElse: () =&gt; const SizedBox(),
                  ),
                ),
              ),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () async {
            final result = await Adb.sendSingleCommand(
              'monkey -p com.google.android.googlequicksearchbox 1;sleep 3;input keyevent KEYCODE_HOME',
              ip: '127.0.0.1',
              port: 5555,
              crypto: crypto,
            );
            debugPrint('Result: $result');
          },
          tooltip: 'Send single command',
          child: const Icon(Icons.send),
        ));
  }
}

更多关于Flutter ADB调试插件flutter_adb的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter ADB调试插件flutter_adb的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用flutter_adb插件进行ADB调试的代码案例。flutter_adb插件允许你在Flutter应用中直接与Android设备的ADB(Android Debug Bridge)进行交互。

首先,你需要在你的Flutter项目中添加flutter_adb依赖。在pubspec.yaml文件中添加以下依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_adb: ^x.y.z  # 请替换为最新版本号

然后运行flutter pub get来获取依赖。

接下来,你可以在你的Flutter应用中使用flutter_adb插件。以下是一个简单的示例,展示如何使用该插件获取已连接的设备列表并执行一些基本的ADB命令。

import 'package:flutter/material.dart';
import 'package:flutter_adb/flutter_adb.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<String> devices = [];

  @override
  void initState() {
    super.initState();
    _getDevices();
  }

  Future<void> _getDevices() async {
    try {
      List<AdbDevice> adbDevices = await FlutterAdb.getDevices();
      setState(() {
        devices = adbDevices.map((device) => device.id).toList();
      });
    } catch (e) {
      print("Error getting devices: $e");
    }
  }

  Future<void> _runAdbCommand(String deviceId, String command) async {
    try {
      String result = await FlutterAdb.runCommand(deviceId, command);
      print("Command result: $result");
    } catch (e) {
      print("Error running command: $e");
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter ADB Demo'),
        ),
        body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            children: [
              Text('Connected Devices:'),
              Wrap(
                spacing: 8.0,
                runSpacing: 8.0,
                children: devices.map((deviceId) => Chip(
                  label: Text(deviceId),
                  onDeleted: () {
                    // Here you can add a button to run a command on the selected device
                    // For example:
                    // _runAdbCommand(deviceId, 'shell input keyevent 26'); // Home button
                  },
                )).toList(),
              ),
              SizedBox(height: 20.0),
              ElevatedButton(
                onPressed: () async {
                  if (devices.isNotEmpty) {
                    String deviceId = devices.first; // For demonstration, using the first device
                    await _runAdbCommand(deviceId, 'shell input keyevent 26'); // Simulate pressing the home button
                  } else {
                    print("No devices connected.");
                  }
                },
                child: Text('Press Home on First Device'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

在这个示例中:

  1. 我们在initState方法中调用_getDevices函数来获取已连接的Android设备列表,并将它们显示在屏幕上。
  2. FlutterAdb.getDevices()方法返回一个包含所有已连接设备的列表。
  3. _runAdbCommand函数用于在指定的设备上运行ADB命令。在这个例子中,我们模拟按下Home按钮(shell input keyevent 26)。
  4. 我们使用ElevatedButton来触发在第一个连接的设备上运行ADB命令的操作。

请注意,这只是一个基本的示例。你可以根据需要扩展这个示例,以包括更多的ADB命令和设备交互功能。确保在实际应用中处理错误和异常情况,并提供适当的用户反馈。

回到顶部