Flutter attach error: connection to device ended too early
问题
flutter attach
OPPO R11s Plus 设备时报错
Error connecting to the service protocol: Exception: connection to device ended too early
google 了一下,没有找到相关的解决方法
过程
使用 VS Code 打开 flutter_tools 调试,配置launch.json
文件如下:
{
"configurations": [
{
"name": "flutter_tools",
"request": "launch",
"type": "dart",
"args": [
"attach",
"--app-id",
"com.flutter.test",
],
"program": "/Users/noif/flutter/packages/flutter_tools/lib/executable.dart",
"cwd": "/Users/noif/Desktop/flutter_test",
}
]
}
其中 program
为需要调试的程序入口文件,cwd
则是需要 attach 的 flutter 项目代码地址。
搜索报错信息 “connection to device ended too early” 发现是在 FlutterDevice
一个 Stream 的 onDone 方法中。
subscription = observatoryUris!.listen((Uri? observatoryUri) async {
// ...
}, onError: (dynamic error) {
globals.printTrace('Fail to handle observatory URI: $error');
}, onDone: () {
_isListeningForObservatoryUri = false;
if (!completer.isCompleted && !isWaitingForVm) {
completer
.completeError(Exception('connection to device ended too early'));
}
});
往上追溯 observatoryUris
是何处创建的,来到 AttachCommand
中
class AttachCommand extends FlutterCommand {
Future<ResidentRunner> createResidentRunner({
required Stream<Uri> observatoryUris,
required Device device,
required FlutterProject flutterProject,
required bool usesIpv6,
}) async {
// ...
final FlutterDevice flutterDevice = await FlutterDevice.create(
device,
target: targetFile,
targetModel: TargetModel(stringArgDeprecated('target-model')!),
buildInfo: buildInfo,
userIdentifier: userIdentifier,
platform: _platform,
);
flutterDevice.observatoryUris = observatoryUris;
// ...
}
继续往上看看 createResidentRunner
方法在何处调用,在 _attachToDevice
方法
Future<void> _attachToDevice(Device device) async {
// ...
Stream<Uri>? observatoryUri;
bool usesIpv6 = ipv6!;
final String ipv6Loopback = InternetAddress.loopbackIPv6.address;
final String ipv4Loopback = InternetAddress.loopbackIPv4.address;
final String hostname = usesIpv6 ? ipv6Loopback : ipv4Loopback;
if (debugPort == null && debugUri == null) {
if (device is FuchsiaDevice) {
// ...
} else if ((device is IOSDevice) || (device is IOSSimulator) || (device is MacOSDesignedForIPadDevice)) {
// ...
}
// If MDNS discovery fails or we're not on iOS, fallback to ProtocolDiscovery.
if (observatoryUri == null) {
final ProtocolDiscovery observatoryDiscovery =
ProtocolDiscovery.observatory(
// If it's an Android device, attaching relies on past log searching
// to find the service protocol.
await device.getLogReader(includePastLogs: device is AndroidDevice),
portForwarder: device.portForwarder,
ipv6: ipv6!,
devicePort: deviceVmservicePort,
hostPort: hostVmservicePort,
logger: _logger,
);
_logger.printStatus('Waiting for a connection from Flutter on ${device.name}...');
observatoryUri = observatoryDiscovery.uris;
// Determine ipv6 status from the scanned logs.
usesIpv6 = observatoryDiscovery.ipv6;
}
} else {
// ...
}
// ...
}
断点调试发现走到了最后的 ProtocolDiscovery
逻辑,再去其内部看下有啥
class PrototolDiscovery {
final _BufferedStreamController<Uri> _uriStreamController = _BufferedStreamController<Uri>();
Stream<Uri> get uris {
final Stream<Uri> uriStream = _uriStreamController.stream
.transform(_throttle<Uri>(
waitDuration: throttleDuration,
));
return uriStream.asyncMap<Uri>(_forwardPort);
}
}
可以看到 uris
对应的 _uriStreamController.stream
,所以可能就是在这里触发了 stream 的 close,搜索 _uriStreamController.close()
再加上断点跑一遍。
从上图的堆栈中不能直接看出方法的调用处,只能继续搜索,发现在 ProtocolDiscovery
的初始化方法中关联到了 logReader
的 logLines
中。
ProtocolDiscovery._(
this.logReader,
this.serviceName, {
this.portForwarder,
required this.throttleDuration,
this.hostPort,
this.devicePort,
required this.ipv6,
required Logger logger,
}) : _logger = logger,
assert(logReader != null) {
_deviceLogSubscription = logReader.logLines.listen(
_handleLine,
onDone: _stopScrapingLogs,
);
}
而 logReader
是 AdbLogReader
类型,logLines
也是一个 Stream,所以再找找他对应的 _linesController
的 close
方法。
class AdbLogReader extends DeviceLogReader {
void _start() {
// ...
unawaited(_adbProcess.exitCode.whenComplete(() {
if (_linesController.hasListener) {
_linesController.close();
}
}));
}
}
这里的 _adbProcess 直接退出了,想看看这个 _abdProcess 的内部实现,发现搜索不到其对应的类型_ProcessImpl
, 到这里思路有点断了,exitCode 也看不出啥有用的信息
正在一筹莫展之际,发现这里有 _adbProcess
执行对应的参数
就想着试试直接在命令行执行 adb 命令
$adb -s 8b9cc677 shell -x logcat -v time -s flutter
发现了一个 read: unexpected EOF
错误
再去 google 下,终于发现以下的解决方案:
stackoverflow.com/questions/4…
Go to Settings > System > Developer options > Logger buffer sizes and choose a higher value.
在开发者模式中将 log 缓存调高就可以了。
总结
从这个排查过程可以发现,flutter attach 的源码就在 flutter_tools 中,遇到无法解决的问题可以直接打开源码断点调试看看。而 attach Android 设备是通过 adb logcat 抓取其中 flutter 相关日志,从日志中解析服务地址来实现的。
转载自:https://juejin.cn/post/7384368081466081306