You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

已做空值检查仍出现Flutter蓝牙空值检查异常求助

问题排查与解决

问题场景

运行蓝牙连接代码时,成功打印Connected to the device后触发空值检查异常:

代码片段

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

// ...

class BTConnection {
  BluetoothConnection? c;
  Future<void> connect(address) async {
    print('Connecting to the device ...');

    BluetoothConnection connection =
        await BluetoothConnection.toAddress(address);
    c = connection;
    print('Connected to the device');
    FlutterIsolate.spawn(listenToData, connection);
  }

  Future<void> disconnect() async {
    if (c != null) {
      await c?.finish();
      print("disconnected from device");
    } else {
      print("There is no bluetooth connection to disconnect");
    }
  }

  @pragma('vm:entry-point')
  Future<void> listenToData(connection) async {
    print("listening ...");
    await connection.input?.listen((Uint8List data) {
      print('Data incoming: ${ascii.decode(data)}');
      connection.output.add(data); // Sending data
    }).onDone(() {
      print('Disconnected by remote request');
    });
  }
}

错误日志

E/flutter (26066): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Null check operator used on a null value
E/flutter (26066): #0      FlutterIsolate.spawn
flutter_isolate.dart:28
E/flutter (26066): #1      BTConnection.connect
main.dart:110
E/flutter (26066): <asynchronous suspension>
E/flutter (26066): #2      _HomePageState.build.<anonymous closure>
main.dart:70
E/flutter (26066): <asynchronous suspension>
E/flutter (26066):

原因分析

FlutterIsolate的spawn方法要求传递的参数必须是可序列化类型(如基本类型、列表、Map或实现序列化协议的对象),但BluetoothConnection是包含原生资源引用的复杂对象,无法跨Isolate序列化传递。直接传递该对象会导致内部序列化失败,参数变为null,进而触发空值检查异常。

解决方法

方案1:通过通信端口传递数据

不直接传递BluetoothConnection,改用ReceivePort和SendPort在主Isolate与子Isolate间传递蓝牙数据:

  1. 修改connect方法,创建通信端口并启动Isolate:
Future<void> connect(address) async {
  print('Connecting to the device ...');
  BluetoothConnection connection = await BluetoothConnection.toAddress(address);
  c = connection;
  print('Connected to the device');

  final receivePort = ReceivePort();
  // 启动Isolate并传递SendPort
  await FlutterIsolate.spawn(listenToData, receivePort.sendPort);
  
  // 主Isolate监听蓝牙数据,转发到子Isolate
  connection.input?.listen((Uint8List data) {
    receivePort.sendPort.send(data);
    connection.output.add(data); // 处理回发数据
  }).onDone(() {
    print('Disconnected by remote request');
    receivePort.close();
  });
}
  1. 修改子Isolate的监听逻辑:
@pragma('vm:entry-point')
Future<void> listenToData(SendPort sendPort) async {
  print("listening ...");
  final receivePort = ReceivePort();
  sendPort.send(receivePort.sendPort);

  await for (final data in receivePort) {
    if (data is Uint8List) {
      print('Data incoming: ${ascii.decode(data)}');
    }
  }
}

方案2:在子Isolate内重建连接

将蓝牙地址(可序列化)传给子Isolate,在子Isolate内部直接建立连接并监听:

Future<void> connect(address) async {
  print('Starting isolate to handle bluetooth connection');
  await FlutterIsolate.spawn(listenToData, address);
}

@pragma('vm:entry-point')
Future<void> listenToData(String address) async {
  print("Connecting in isolate ...");
  BluetoothConnection connection = await BluetoothConnection.toAddress(address);
  print("listening ...");
  
  await connection.input?.listen((Uint8List data) {
    print('Data incoming: ${ascii.decode(data)}');
    connection.output.add(data);
  }).onDone(() {
    print('Disconnected by remote request');
    connection.finish();
  });
}

注:此方案下主Isolate无法直接控制连接断开,需额外通过通信端口发送断开指令。

额外注意事项

  • 确保listenToData方法保留@pragma('vm:entry-point')注解,避免release模式下被混淆移除。
  • 处理Isolate关闭时的资源释放,防止内存泄漏或连接残留。

内容的提问来源于stack exchange,提问作者Lauritz Tieste

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 13:05:17