C# Xamarin Android后台实时语音识别功能实现可行性咨询
Absolutely, you can build this! Implementing background, real-time voice recognition to detect commands like "top" and "back" in your C# Xamarin Android app is totally feasible—here's a practical, step-by-step breakdown tailored to your use case:
1. First, Configure Permissions
You'll need to declare and request the necessary permissions to access the microphone and run background services.
Add Permissions to AndroidManifest.xml
Include these lines inside the <manifest> tag:
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.INTERNET" /> <!-- For cloud-based recognition --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
Request Runtime Permissions (Android 6.0+)
Add this code to your main activity to prompt users for microphone access:
private readonly string[] _requiredPermissions = { Manifest.Permission.RecordAudio }; private const int PermissionRequestId = 101; protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); // Your existing initialization code here if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.RecordAudio) != Permission.Granted) { ActivityCompat.RequestPermissions(this, _requiredPermissions, PermissionRequestId); } else { StartVoiceRecognitionService(); } } public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults) { base.OnRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == PermissionRequestId && grantResults.Any(r => r == Permission.Granted)) { StartVoiceRecognitionService(); } } private void StartVoiceRecognitionService() { StartForegroundService(new Intent(this, typeof(VoiceCommandService))); }
2. Build a Foreground Service for Continuous Listening
Android restricts background tasks, so we'll use a foreground service (with a persistent notification) to keep listening without being killed by the system.
Create the Voice Recognition Service
Make a new class that inherits from Service:
[Service(ForegroundServiceType = ForegroundService.TypeMicrophone)] public class VoiceCommandService : Service { private SpeechRecognizer _speechRecognizer; private Intent _recognitionIntent; private const int NotificationId = 1001; public override IBinder OnBind(Intent intent) => null; public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId) { // Create a persistent notification for the foreground service var notification = new NotificationCompat.Builder(this, "voice_command_channel") .SetContentTitle("Voice Listener Active") .SetContentText("Listening for 'top' or 'back'") .SetSmallIcon(Resource.Drawable.ic_mic) .SetPriority(NotificationCompat.PriorityLow) .Build(); // Create a notification channel (required for Android 8.0+) if (Build.VERSION.SdkInt >= BuildVersionCodes.O) { var channel = new NotificationChannel( "voice_command_channel", "Voice Command Service", NotificationImportance.Low) { Description = "Runs background voice recognition" }; var notificationManager = GetSystemService(NotificationService) as NotificationManager; notificationManager?.CreateNotificationChannel(channel); } StartForeground(NotificationId, notification); InitializeSpeechRecognizer(); return StartCommandResult.Sticky; } private void InitializeSpeechRecognizer() { _speechRecognizer = SpeechRecognizer.CreateSpeechRecognizer(this); _speechRecognizer.SetRecognitionListener(new VoiceCommandListener(this)); _recognitionIntent = new Intent(RecognizerIntent.ActionRecognizeSpeech); _recognitionIntent.PutExtra(RecognizerIntent.ExtraLanguageModel, RecognizerIntent.LanguageModelFreeForm); _recognitionIntent.PutExtra(RecognizerIntent.ExtraPartialResults, true); // Get real-time partial matches _recognitionIntent.PutExtra(RecognizerIntent.ExtraLanguage, Java.Util.Locale.Default); StartListening(); } private void StartListening() { try { _speechRecognizer.StartListening(_recognitionIntent); } catch (Exception ex) { Log.Error("VoiceService", $"Failed to start listening: {ex.Message}"); // Retry after a short delay if there's an error new Handler(Looper.MainLooper).PostDelayed(StartListening, 1000); } } public override void OnDestroy() { _speechRecognizer?.StopListening(); _speechRecognizer?.Destroy(); base.OnDestroy(); } // Custom listener to handle recognition results private class VoiceCommandListener : Java.Lang.Object, IRecognitionListener { private readonly VoiceCommandService _service; public VoiceCommandListener(VoiceCommandService service) { _service = service; } public void OnReadyForSpeech(Bundle @params) { } public void OnBeginningOfSpeech() { } public void OnRmsChanged(float rmsdB) { } public void OnBufferReceived(byte[] buffer) { } public void OnEndOfSpeech() { // Restart listening immediately after a recognition session ends _service.StartListening(); } public void OnError(RecognizerError error) { Log.Error("VoiceListener", $"Recognition error: {error}"); _service.StartListening(); } public void OnResults(Bundle results) { var matches = results.GetStringArrayList(SpeechRecognizer.ResultsRecognition); if (matches != null && matches.Any()) { ProcessCommand(matches[0].ToLower()); } } public void OnPartialResults(Bundle partialResults) { // Detect commands mid-speech for faster response var partialMatches = partialResults.GetStringArrayList(SpeechRecognizer.ResultsRecognition); if (partialMatches != null && partialMatches.Any()) { var partialText = partialMatches[0].ToLower(); if (partialText.Contains("top") || partialText.Contains("back")) { ProcessCommand(partialText); } } } public void OnEvent(int eventType, Bundle @params) { } private void ProcessCommand(string commandText) { if (commandText.Contains("top")) { // Send broadcast to trigger "top" action in your activity LocalBroadcastManager.GetInstance(_service) .SendBroadcast(new Intent("com.yourapp.VOICE_COMMAND_TOP")); } else if (commandText.Contains("back")) { // Send broadcast to trigger "back" action LocalBroadcastManager.GetInstance(_service) .SendBroadcast(new Intent("com.yourapp.VOICE_COMMAND_BACK")); } } } }
3. Receive Commands in Your Activity
Add a broadcast receiver to your main activity to listen for commands and execute corresponding actions:
private BroadcastReceiver _commandReceiver; protected override void OnResume() { base.OnResume(); _commandReceiver = new VoiceCommandReceiver(); var broadcastManager = LocalBroadcastManager.GetInstance(this); broadcastManager.RegisterReceiver( _commandReceiver, new IntentFilter("com.yourapp.VOICE_COMMAND_TOP")); broadcastManager.RegisterReceiver( _commandReceiver, new IntentFilter("com.yourapp.VOICE_COMMAND_BACK")); } protected override void OnPause() { base.OnPause(); LocalBroadcastManager.GetInstance(this).UnregisterReceiver(_commandReceiver); } private class VoiceCommandReceiver : BroadcastReceiver { public override void OnReceive(Context context, Intent intent) { switch (intent.Action) { case "com.yourapp.VOICE_COMMAND_TOP": // Replace with your "top" action (e.g., scroll a RecyclerView to top) Toast.MakeText(context, "'Top' command detected!", ToastLength.Short).Show(); break; case "com.yourapp.VOICE_COMMAND_BACK": // Replace with your "back" action (e.g., simulate back button press) Toast.MakeText(context, "'Back' command detected!", ToastLength.Short).Show(); break; } } }
Key Notes for Success
- Battery Optimization: Advise users to exclude your app from battery optimization settings—otherwise, Android may kill the foreground service during low-power modes.
- Recognition Accuracy: Adjust the language model or add keyword-specific filters (like regex) to reduce false positives.
- Resource Usage: Continuous listening uses battery and memory—add a toggle in your app to let users enable/disable the background listener.
内容的提问来源于stack exchange,提问作者Artur Lodklif

