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

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...";
  });
}

额外注意事项

  1. 必须填写有效的Azure语音服务订阅密钥和区域,否则识别会直接失败。
  2. 测试时确保设备联网,Azure语音转文本依赖网络服务。
  3. 每次启动识别前,确保旧的SpeechRecognizer已被销毁,避免资源泄漏。

内容的提问来源于stack exchange,提问作者Gaurab Awal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 19:20:40