فهرست منبع

build(android): 配置阿里云maven镜像并替换gradle下载源

调整android构建的仓库源为阿里云镜像,加快依赖下载速度;修改gradle分发地址使用阿里云镜像源。

fix: 修复Android设备连接本地服务端的问题
- 适配Android模拟器和物理设备的本地连接地址,将默认host改为适配安卓的局域网IP
- 处理本地保存的localhost地址在安卓上无法解析的问题
- 修改websocket ping消息为标准json格式
- 更新测试用例的连接地址,优化连接失败的错误提示逻辑

style: 修复android build.gradle.kts的缩进格式
refactor: 优化RecordingState的可选参数处理逻辑,使用_unset标记未传入的参数
wenhongquan 3 هفته پیش
والد
کامیت
6640ac014e

+ 26 - 0
flutter_asr_client/.vscode/launch.json

@@ -0,0 +1,26 @@
+{
+    // 使用 IntelliSense 了解相关属性。 
+    // 悬停以查看现有属性的描述。
+    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+    
+        {
+            "name": "flutter_asr_client",
+            "request": "launch",
+            "type": "dart"
+        },
+        {
+            "name": "flutter_asr_client (profile mode)",
+            "request": "launch",
+            "type": "dart",
+            "flutterMode": "profile"
+        },
+        {
+            "name": "flutter_asr_client (release mode)",
+            "request": "launch",
+            "type": "dart",
+            "flutterMode": "release"
+        }
+    ]
+}

+ 3 - 1
flutter_asr_client/android/build.gradle.kts

@@ -1,5 +1,7 @@
 allprojects {
 allprojects {
-    repositories {
+     repositories {
+        maven("https://maven.aliyun.com/repository/public/")
+        maven("https://maven.aliyun.com/repository/google/")
         google()
         google()
         mavenCentral()
         mavenCentral()
     }
     }

+ 1 - 1
flutter_asr_client/android/gradle/wrapper/gradle-wrapper.properties

@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip
+distributionUrl=https://mirrors.aliyun.com/gradle/distributions/v8.14.0/gradle-8.14-all.zip

+ 2 - 0
flutter_asr_client/android/settings.gradle.kts

@@ -11,6 +11,8 @@ pluginManagement {
     includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
     includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
 
 
     repositories {
     repositories {
+        maven("https://maven.aliyun.com/repository/public/")
+        maven("https://maven.aliyun.com/repository/google/")
         google()
         google()
         mavenCentral()
         mavenCentral()
         gradlePluginPortal()
         gradlePluginPortal()

+ 8 - 1
flutter_asr_client/lib/common/constants.dart

@@ -1,5 +1,12 @@
+import 'dart:io';
+
 abstract final class AppConstants {
 abstract final class AppConstants {
-  static const defaultHost = 'localhost';
+  // On the Android emulator `localhost` points at the emulator itself, not the
+  // dev machine. `10.0.2.2` is the emulator's alias for the host loopback, so
+  // it is the correct default there. Physical devices must set the host
+  // explicitly to the dev machine's LAN IP via the server settings dialog.
+  static String get defaultHost =>
+      Platform.isAndroid ? '192.168.199.195' : '127.0.0.1';
   static const defaultPort = 8765;
   static const defaultPort = 8765;
   static const targetSampleRate = 16000;
   static const targetSampleRate = 16000;
   static const bufferSize = 4096;
   static const bufferSize = 4096;

+ 30 - 10
flutter_asr_client/lib/pages/recording/notifiers/recording_notifier.dart

@@ -17,6 +17,11 @@ import 'package:asr_client/services/websocket_service.dart';
 enum RecordingStatus { idle, recording, paused, ending }
 enum RecordingStatus { idle, recording, paused, ending }
 
 
 @immutable
 @immutable
+const _unset = _Unset();
+final class _Unset {
+  const _Unset();
+}
+
 final class RecordingState {
 final class RecordingState {
   const RecordingState({
   const RecordingState({
     this.status = RecordingStatus.idle,
     this.status = RecordingStatus.idle,
@@ -48,11 +53,11 @@ final class RecordingState {
     double? audioLevel,
     double? audioLevel,
     List<ConversationItem>? items,
     List<ConversationItem>? items,
     bool? isConnected,
     bool? isConnected,
-    String? errorMessage,
-    String? sessionId,
-    String? projectName,
-    String? taskName,
-    String? templateName,
+    Object? errorMessage = _unset,
+    Object? sessionId = _unset,
+    Object? projectName = _unset,
+    Object? taskName = _unset,
+    Object? templateName = _unset,
   }) {
   }) {
     return RecordingState(
     return RecordingState(
       status: status ?? this.status,
       status: status ?? this.status,
@@ -60,11 +65,17 @@ final class RecordingState {
       audioLevel: audioLevel ?? this.audioLevel,
       audioLevel: audioLevel ?? this.audioLevel,
       items: items ?? this.items,
       items: items ?? this.items,
       isConnected: isConnected ?? this.isConnected,
       isConnected: isConnected ?? this.isConnected,
-      errorMessage: errorMessage ?? this.errorMessage,
-      sessionId: sessionId ?? this.sessionId,
-      projectName: projectName ?? this.projectName,
-      taskName: taskName ?? this.taskName,
-      templateName: templateName ?? this.templateName,
+      errorMessage:
+          identical(errorMessage, _unset) ? this.errorMessage : errorMessage as String?,
+      sessionId:
+          identical(sessionId, _unset) ? this.sessionId : sessionId as String?,
+      projectName: identical(projectName, _unset)
+          ? this.projectName
+          : projectName as String?,
+      taskName: identical(taskName, _unset) ? this.taskName : taskName as String?,
+      templateName: identical(templateName, _unset)
+          ? this.templateName
+          : templateName as String?,
     );
     );
   }
   }
 
 
@@ -107,6 +118,15 @@ final class RecordingNotifier extends AutoDisposeAsyncNotifier<RecordingState> {
     );
     );
 
 
     await _connectWebSocket();
     await _connectWebSocket();
+    if (_webSocketService.currentState != ConnectionState.connected) {
+      state = AsyncData(
+        initialState.copyWith(
+          status: RecordingStatus.idle,
+          errorMessage: '无法连接服务器,请检查服务端地址与网络后重试',
+        ),
+      );
+      return state.value ?? initialState;
+    }
     await _startRecording(initialState);
     await _startRecording(initialState);
     return state.value ?? initialState;
     return state.value ?? initialState;
   }
   }

+ 11 - 1
flutter_asr_client/lib/services/settings_service.dart

@@ -1,3 +1,5 @@
+import 'dart:io';
+
 import 'package:shared_preferences/shared_preferences.dart';
 import 'package:shared_preferences/shared_preferences.dart';
 import 'package:asr_client/common/constants.dart';
 import 'package:asr_client/common/constants.dart';
 
 
@@ -23,7 +25,15 @@ final class SharedPreferencesSettingsService implements SettingsService {
   @override
   @override
   Future<String> getHost() async {
   Future<String> getHost() async {
     final prefs = await _preferences;
     final prefs = await _preferences;
-    return prefs.getString(_hostKey) ?? AppConstants.defaultHost;
+    final raw = prefs.getString(_hostKey);
+    if (raw == null || raw.trim().isEmpty) return AppConstants.defaultHost;
+    // `localhost` does not resolve reliably on Android and triggers
+    // "no address associated with hostname". Map it to the emulator's
+    // host-loopback alias so a previously saved `localhost` still works.
+    if (raw.trim().toLowerCase() == 'localhost' && Platform.isAndroid) {
+      return '10.0.2.2';
+    }
+    return raw.trim();
   }
   }
 
 
   @override
   @override

+ 2 - 1
flutter_asr_client/lib/services/websocket_service.dart

@@ -1,4 +1,5 @@
 import 'dart:async';
 import 'dart:async';
+import 'dart:convert';
 import 'dart:io';
 import 'dart:io';
 import 'dart:math' as math;
 import 'dart:math' as math;
 
 
@@ -104,7 +105,7 @@ final class WebSocketServiceImpl implements WebSocketService {
   void _startPingTimer() {
   void _startPingTimer() {
     _pingTimer?.cancel();
     _pingTimer?.cancel();
     _pingTimer = Timer.periodic(AppConstants.pingInterval, (_) {
     _pingTimer = Timer.periodic(AppConstants.pingInterval, (_) {
-      _channel?.sink.add('ping');
+      _channel?.sink.add(jsonEncode({'type': 'ping'}));
     });
     });
   }
   }
 
 

+ 1 - 1
flutter_asr_client/test/unit/services/websocket_service_runtime_test.dart

@@ -38,7 +38,7 @@ void main() {
         markTestSkipped('ASR server is not running on localhost:8765');
         markTestSkipped('ASR server is not running on localhost:8765');
       }
       }
 
 
-      await service.connect('ws://localhost:8765');
+      await service.connect('ws://192.168.199.195:8765');
 
 
       final connected = await service.messageStream
       final connected = await service.messageStream
           .firstWhere((msg) => msg is ConnectedMessage)
           .firstWhere((msg) => msg is ConnectedMessage)