- 添加完整的 GitHub Actions 构建指南文档 - BUILD_GUIDE.md: Android 详细构建指南 - MULTIPLATFORM_GUIDE.md: 多平台构建指南 - HOW_TO_BUILD.md: 分步操作教程 - QUICKSTART.md: 3步快速开始指南 - INDEX.md: 文档总览索引 - README.md: 基础说明 - 创建 docs/ 目录存放项目文档 - 锁定 libcore 子模块到 f993a57 (v3.1.7) - 防止在线编译时使用最新版本 - 确保构建稳定性和一致性
8.4 KiB
8.4 KiB
Clash Meta 故障排查指南
常见运行时问题
问题 1: VPN 连接失败,日志显示 "PermissionMonitor error 22 (EINVAL)"
症状:
E/VPNService: PermissionMonitor error 22 (EINVAL)
E/Clash: TUN 设备启动失败
原因:
- VPN Builder 配置的路由规则无效
- 缺少 bypass-LAN 路由配置
- 路由 CIDR 格式错误
解决方案:
- 检查
getAndroidVpnOptions()返回的路由列表:
final options = await KRClashImp().getAndroidVpnOptions();
print('路由数量: ${options?['routeAddress']?.length}');
print('路由列表: ${options?['routeAddress']}');
- 确认配置包含完整的 bypass-LAN 路由:
# clash_config.yaml
tun:
route-exclude-address:
- 10.0.0.0/8
- 100.64.0.0/10
- 127.0.0.0/8
- 169.254.0.0/16
- 172.16.0.0/12
- 192.0.0.0/24
- 192.0.2.0/24
- 192.88.99.0/24
- 192.168.0.0/16
- 198.18.0.0/15
- 198.51.100.0/24
- 203.0.113.0/24
- 224.0.0.0/3
- fc00::/7
- fe80::/10
- ff00::/8
- 验证 VPN Builder 配置:
// VPNService.kt
builder
.addAddress("172.19.0.1/30")
.addRoute("0.0.0.0/1") // ✅ 拆分默认路由
.addRoute("128.0.0.0/1") // ✅ 避免 0.0.0.0/0
.addRoute("10.0.0.0/8") // ✅ bypass-LAN
// ...
参考: lib/app/services/clash_imp/kr_clash_imp.dart:192
问题 2: FFI 初始化崩溃 "Go runtime already initialized"
症状:
F/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR)
E/Clash: Go runtime already initialized
原因:
- 多线程并发调用
_ensureInitialized() - Go 运行时被重复初始化
解决方案:
已在 kr_clash_imp.dart 中修复,使用 Completer 实现并发安全:
Future<void> _ensureInitialized() async {
if (_initialized) return;
if (_initLock != null) {
await _initLock!.future; // ✅ 等待其他初始化完成
return;
}
_initLock = Completer<void>();
// ... 执行初始化
}
验证修复:
// 并发测试
await Future.wait([
KRClashImp().start(...),
KRClashImp().getAndroidVpnOptions(),
KRClashImp().getTraffic(),
]);
// ✅ 应该不会崩溃
参考: lib/app/services/clash_imp/kr_clash_imp.dart:43
问题 3: 模拟器无法联网 "Network unreachable"
症状:
E/Clash: 网络不可达
W/VPNService: 底层网络丢失
原因:
- 模拟器需要显式设置底层网络
- 缺少
setUnderlyingNetworks()调用
解决方案:
已在 VPNService.kt 中实现:
// Android P+ 需要设置底层网络
private val defaultNetworkCallback by lazy {
object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
setUnderlyingNetworks(arrayOf(network)) // ✅ 关键!
}
}
}
// 注册回调
connectivityManager.registerDefaultNetworkCallback(defaultNetworkCallback)
验证修复:
# 在 MuMu 模拟器测试
adb logcat | grep "底层网络"
# 应该看到: "底层网络可用: NetworkIdentity{...}"
参考: android/.../bg/VPNService.kt:32-66
问题 4: 配置文件解析失败 "invalid YAML"
症状:
E/Clash: 启动失败: yaml: unmarshal errors:
line 10: cannot unmarshal !!str `8388` into int
原因:
- YAML 类型不匹配 (字符串 vs 整数)
- 配置格式错误
解决方案:
检查 ClashConfigGenerator.generate():
proxies:
- name: "Server-1"
type: ss
server: "example.com"
port: 8388 # ✅ 整数,不要引号
password: "password" # ✅ 字符串,使用引号
cipher: "aes-256-gcm"
调试方法:
// 打印生成的配置
final config = ClashConfigGenerator.generate(...);
print('配置预览:\n$config');
// 保存到文件手动检查
File('/tmp/clash_debug.yaml').writeAsStringSync(config);
参考: lib/app/services/clash_imp/clash_config_generator.dart
问题 5: "quickStart timeout" 启动超时
症状:
W/Clash: 启动超时 (10秒)
E/Clash: quickStart 未收到回调
原因:
- Go 核心启动耗时过长
- ReceivePort 未正确监听
- 回调被阻塞
解决方案:
- 增加超时时间:
_isRunning = await completer.future.timeout(
const Duration(seconds: 20), // ✅ 增加到 20 秒
onTimeout: () {
print('⚠️ 启动超时');
return false;
},
);
- 检查 Go 核心日志:
// ClashService.kt - 添加日志回调
tempMethodChannel.setMethodCallHandler { call, result ->
if (call.method == "log") {
Log.d(TAG, "Go 核心日志: ${call.arguments}")
}
}
- 使用 Service Isolate 避免阻塞:
// ClashService.kt:102 - 已实现
serviceEngine?.dartExecutor?.executeDartEntrypoint(entrypoint)
参考: lib/app/services/clash_imp/kr_clash_imp.dart:145
问题 6: 流量统计不更新
症状:
I/Clash: 流量统计: {"upload": 0, "download": 0} # 始终为 0
原因:
- TUN 设备未正确启动
- 流量未通过代理
解决方案:
- 确认 TUN 启动成功:
final tunStarted = await KRClashImp().startTun(fd, protectCallback);
print('TUN 启动状态: $tunStarted'); // 应该为 true
- 检查路由配置:
# 在设备上检查路由表
adb shell "ip route show table all | grep tun"
- 测试代理连接:
# 使用 curl 测试
adb shell "curl -v http://www.google.com"
# 应该看到代理日志
参考: lib/app/services/clash_imp/kr_clash_imp.dart:261
日志分析
启用详细日志
// lib/app/services/clash_imp/kr_clash_imp.dart
// 添加更详细的日志
print('📨 [Clash] 收到消息: ${jsonEncode(message)}');
print('📋 [Clash] 配置完整内容:\n$config');
Android Logcat 过滤
# 只看 Clash 相关日志
adb logcat -s "A/Clash" "E/Clash" "W/Clash"
# 实时监控 FFI 调用
adb logcat | grep -E "(ClashFFI|quickStart|getAndroidVpnOptions)"
# 保存日志到文件
adb logcat -d > clash_debug.log
Dart Observatory 调试
# 启用 Dart Observatory
flutter run --observatory-port=8181
# 在浏览器打开
open http://localhost:8181
# 查看 Isolate 状态
# - Main Isolate (UI)
# - Service Isolate (Clash 后台)
性能问题
内存泄漏
症状:
- 应用内存持续增长
- 最终 OOM 崩溃
排查:
// 确保调用 dispose()
@override
void dispose() {
KRClashImp().dispose();
super.dispose();
}
// 检查 ReceivePort 是否关闭
receivePort.listen((message) {
// ...
receivePort.close(); // ✅ 必须关闭!
});
CPU 占用过高
症状:
- 设备发热
- 电池消耗快
排查:
# 查看 CPU 占用
adb shell "top -m 10"
# 使用 Profiler
flutter run --profile
# DevTools → CPU Profiler
优化:
// 降低流量统计频率
Timer.periodic(const Duration(seconds: 2), (timer) { // ✅ 2秒而非1秒
final traffic = await KRClashImp().getTraffic();
// ...
});
崩溃分析
获取崩溃堆栈
# Native 崩溃
adb logcat -d | grep "backtrace"
# 使用 ndk-stack 解析
adb logcat | ndk-stack -sym android/app/build/intermediates/merged_native_libs/debug/out/lib/arm64-v8a
常见崩溃模式
SIGSEGV (段错误)
原因: FFI 指针使用错误
示例:
// ❌ 错误: 释放后使用
final ptr = 'hello'.toNativeUtf8();
malloc.free(ptr);
_ffi!.someFunction(ptr); // 崩溃!
// ✅ 正确: 使用后释放
final ptr = 'hello'.toNativeUtf8();
_ffi!.someFunction(ptr);
malloc.free(ptr);
SIGABRT (异常终止)
原因: Go panic 未恢复
解决: 在 Go 侧添加 recover:
//export quickStart
func quickStart(...) {
defer func() {
if r := recover(); r != nil {
log.Println("Panic recovered:", r)
}
}()
// ...
}
获取帮助
收集诊断信息
创建 Bug 报告时请包含:
- 系统信息:
adb shell "getprop ro.build.version.release" # Android 版本
adb shell "getprop ro.product.cpu.abi" # CPU 架构
- 应用日志:
adb logcat -d > full_log.txt
- 配置文件:
// 脱敏后的 clash_config.yaml
print(config.replaceAll(RegExp(r'password:.*'), 'password: ***'));
- 复现步骤:
- 详细操作流程
- 预期结果 vs 实际结果
- 复现概率