Flutter与Java交互问题:EventChannel无法传输Azure语音转文本数据
Flutter与Azure语音转文本:Native端无法向Flutter发送识别结果的解决方案
问题根源分析
你的代码无法通过attachEvent.success向Flutter传递语音识别结果,核心问题集中在以下几点:
- 权限与识别启动时机冲突:权限请求是异步操作,你在请求后立即启动识别,此时用户可能还未完成授权,导致麦克风权限未获取,语音识别无法正常触发事件。
- 识别器初始化过早:在类成员变量位置初始化
SpeechRecognizer,此时Activity上下文未完全就绪,易引发初始化失败。 - 空指针风险未处理:事件回调中直接使用
attachEvent,若用户已点击Stop按钮,attachEvent已被置空,会抛出异常中断逻辑。 - 重复绑定监听器:每次点击Record都会重复添加事件监听器,导致事件被多次处理,引发异常。
- 停止逻辑不完善:
stopSpeechToText仅添加监听器,未主动调用停止识别的方法,无法及时终止流程。
修复方案及修改后代码
MainActivity.java
package com.example.sounddiaryapp; import androidx.annotation.NonNull; import io.flutter.embedding.android.FlutterActivity; import io.flutter.embedding.engine.FlutterEngine; import com.microsoft.cognitiveservices.speech.*; import com.microsoft.cognitiveservices.speech.audio.*; import java.util.concurrent.ExecutionException; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import android.os.Bundle; import android.util.Log; import static android.Manifest.permission.*; import android.os.Handler; import java.util.Objects; import io.flutter.plugin.common.EventChannel; public class MainActivity extends FlutterActivity { private static final String STREAM = "com.chamelalaboratory.demo.flutter_event_channel/eventChannel"; final String TAG_NAME = "From_Native"; private EventChannel.EventSink attachEvent; private Handler handler; private SpeechRecognizer speechRecognizer; private static final int PERMISSION_REQUEST_CODE = 5; private final Runnable runnable = new Runnable() { @Override public void run() { // 先检查权限状态 if (ContextCompat.checkSelfPermission(MainActivity.this, RECORD_AUDIO) != android.content.pm.PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(MainActivity.this, INTERNET) != android.content.pm.PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{RECORD_AUDIO, INTERNET}, PERMISSION_REQUEST_CODE); } else { initSpeechRecognizer(); startRecognition(); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); new EventChannel(Objects.requireNonNull(getFlutterEngine()).getDartExecutor(), STREAM).setStreamHandler( new EventChannel.StreamHandler() { @Override public void onListen(Object args, final EventChannel.EventSink events) { Log.w(TAG_NAME, "Adding listener"); attachEvent = events; handler = new Handler(); runnable.run(); } @Override public void onCancel(Object args) { Log.w(TAG_NAME, "Cancelling listener"); try{ stopSpeechToText(); } catch (Exception ex) { Log.e(TAG_NAME, "Stop error: " + ex.getMessage()); } handler.removeCallbacks(runnable); handler = null; attachEvent = null; } } ); } // 延迟初始化识别器,仅在权限获取后执行 private void initSpeechRecognizer() { String YourSubscriptionKey = "你的Azure订阅密钥"; String YourServiceRegion = "你的Azure服务区域"; SpeechConfig speechConfig = SpeechConfig.fromSubscription(YourSubscriptionKey, YourServiceRegion); AudioConfig audioConfig = AudioConfig.fromDefaultMicrophoneInput(); speechRecognizer = new SpeechRecognizer(speechConfig, audioConfig); // 绑定识别成功事件 speechRecognizer.recognized.addEventListener((s, e) -> { if (e.getResult().getReason() == ResultReason.RecognizedSpeech) { String receivedText = e.getResult().getText(); Log.d(TAG_NAME, "RECOGNIZED: Text=" + receivedText); // 判空后发送数据到Flutter if (attachEvent != null) { attachEvent.success(receivedText); } } else if (e.getResult().getReason() == ResultReason.NoMatch) { Log.d(TAG_NAME, "NOMATCH: Speech could not be recognized."); stopSpeechToText(); } }); // 绑定取消事件 speechRecognizer.canceled.addEventListener((s, e) -> { Log.d(TAG_NAME, "CANCELED: Reason=" + e.getReason()); if (e.getReason() == CancellationReason.Error) { Log.e(TAG_NAME, "CANCELED: ErrorCode=" + e.getErrorCode()); Log.e(TAG_NAME, "CANCELED: ErrorDetails=" + e.getErrorDetails()); } stopSpeechToText(); if (attachEvent != null) { attachEvent.endOfStream(); } }); // 绑定会话结束事件 speechRecognizer.sessionStopped.addEventListener((s, e) -> { Log.d(TAG_NAME, "Session stopped event."); stopSpeechToText(); }); } // 启动连续语音识别 private void startRecognition() { try { speechRecognizer.startContinuousRecognitionAsync().get(); } catch (InterruptedException | ExecutionException e) { Log.e(TAG_NAME, "Start recognition error: " + e.getMessage()); stopSpeechToText(); } } // 停止识别并释放资源 public void stopSpeechToText() { if (speechRecognizer != null) { try { speechRecognizer.stopContinuousRecognitionAsync().get(); speechRecognizer.close(); speechRecognizer = null; } catch (InterruptedException | ExecutionException e) { Log.e(TAG_NAME, "Stop recognition error: " + e.getMessage()); } } } // 处理权限请求结果 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == PERMISSION_REQUEST_CODE) { boolean allGranted = true; for (int result : grantResults) { if (result != android.content.pm.PackageManager.PERMISSION_GRANTED) { allGranted = false; break; } } if (allGranted) { initSpeechRecognizer(); startRecognition(); } else { Log.e(TAG_NAME, "Permissions not granted"); if (attachEvent != null) { attachEvent.error("PERMISSION_DENIED", "麦克风或网络权限未授予", null); } } } } @Override protected void onDestroy() { super.onDestroy(); stopSpeechToText(); if (handler != null) { handler.removeCallbacks(runnable); } attachEvent = null; } }
Flutter端代码(小优化)
void _cancelListener() { _streamSubscription.cancel(); setState(() { _currentValue = "Start Recording..."; }); }
额外注意事项
- 必须填写有效的Azure语音服务订阅密钥和区域,否则识别会直接失败。
- 测试时确保设备联网,Azure语音转文本依赖网络服务。
- 每次启动识别前,确保旧的
SpeechRecognizer已被销毁,避免资源泄漏。
内容的提问来源于stack exchange,提问作者Gaurab Awal
相关产品推荐
相关产品推荐

