我正在尝试构建一个应用程序,该应用程序可以持续聆听人们在电话中的谈话内容,并将转换后的语音转文本存储在一个文件中。用户还知道设备处于始终收听模式。
下面的代码有效!(虽然只是在一定程度上)。它连续运行语音识别:
打开应用程序时
即使设备屏幕为空白
即使我切换到不同的应用程序。
但是,一旦我按下主页按钮(Android 中的中心圆形软触摸按钮),它就会停止工作。该应用程序不会崩溃,但会停止接收音频。
当我单击此应用程序的通知卡(通过从手机顶部拉出)时,应用程序崩溃,指出“活动 com.truiton.foregroundservice.MainActivity 已泄漏 ServiceConnection android.speech.SpeechRecognizer$Connection@188bc34最初被绑定在这里”意味着音频设备已经被绑定。
我正在 Nexus 6P 手机上对其进行测试。
这是代码:
public class MainActivity extends AppCompatActivity implements OnClickListener {
private static final String TAG = "ForegroundMain";
private ForegroundService fservice;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int PERMISSION_ALL = 1;
String[] PERMISSIONS = {Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE};
if(!hasPermissions(this, PERMISSIONS)){
ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSION_ALL);
}
Button startButton = (Button) findViewById(R.id.button1);
Button stopButton = (Button) findViewById(R.id.button2);
startButton.setOnClickListener(this);
stopButton.setOnClickListener(this);
}
public static boolean hasPermissions(Context context, String... permissions) {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context != null && permissions != null) {
for (String permission : permissions) {
if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
}
return true;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:
fservice = new ForegroundService(this, new ForegroundService.onResultsReady() {
@Override
public void onResults(ArrayList<String> results) {
if (results != null && results.size() > 0) {
Log.d(TAG, "Speech-to-text: " + results.get(0));
writeToFile(results.get(0));
}
}
});
Intent startIntent = new Intent(MainActivity.this, ForegroundService.class);
startIntent.setAction(Constants.ACTION.STARTFOREGROUND_ACTION);
startService(startIntent);
break;
case R.id.button2:
Intent stopIntent = new Intent(MainActivity.this, ForegroundService.class);
stopIntent.setAction(Constants.ACTION.STOPFOREGROUND_ACTION);
startService(stopIntent);
break;
default:
break;
}
}
public void writeToFile(String data) {
final String directoryPath = Environment.getExternalStorageDirectory() + File.separator + "speechdata";
File fileDirectory = new File(directoryPath);
// Make sure the path directory exists.
if(!fileDirectory.exists()) {
fileDirectory.mkdirs();
}
final File file = new File(fileDirectory, "data.txt");
try {
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream fOut = new FileOutputStream(file, true);
OutputStreamWriter myOutWriter = new OutputStreamWriter(fOut);
myOutWriter.append(data + " /n ");
myOutWriter.close();
fOut.flush();
fOut.close();
} catch (IOException e) {
Log.e(TAG, "Exception: File write failed: " + e.toString());
}
}
}
调用语音识别的前台服务代码在这里:
public class ForegroundService extends Service {
private static final String TAG = "ForegroundService";
protected AudioManager mAudioManager;
protected SpeechRecognizer mSpeechRecognizer;
protected Intent mSpeechRecognizerIntent;
protected boolean mIsListening;
private boolean mIsStreamSolo;
private boolean mMute=true;
private onResultsReady mListener;
public ForegroundService() {}
public ForegroundService(Context context, onResultsReady listener) {
try {
mListener=listener;
}
catch(ClassCastException e) {
Log.e(TAG,e.toString());
}
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(context);
mSpeechRecognizer.setRecognitionListener(new SpeechRecognitionListener());
mSpeechRecognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE,
context.getPackageName());
startListening();
}
private void listenAgain() {
if(mIsListening) {
mIsListening = false;
mSpeechRecognizer.cancel();
startListening();
}
}
private void startListening() {
if(!mIsListening) {
mIsListening = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
// turn off beep sound
if (!mIsStreamSolo && mMute) {
mAudioManager.setStreamMute(AudioManager.STREAM_NOTIFICATION, true);
mAudioManager.setStreamMute(AudioManager.STREAM_ALARM, true);
mAudioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
mAudioManager.setStreamMute(AudioManager.STREAM_RING, true);
mAudioManager.setStreamMute(AudioManager.STREAM_SYSTEM, true);
mIsStreamSolo = true;
}
}
mSpeechRecognizer.startListening(mSpeechRecognizerIntent);
}
}
public void destroy() {
mIsListening=false;
if (!mIsStreamSolo) {
mAudioManager.setStreamMute(AudioManager.STREAM_NOTIFICATION, false);
mAudioManager.setStreamMute(AudioManager.STREAM_ALARM, false);
mAudioManager.setStreamMute(AudioManager.STREAM_MUSIC, false);
mAudioManager.setStreamMute(AudioManager.STREAM_RING, false);
mAudioManager.setStreamMute(AudioManager.STREAM_SYSTEM, false);
mIsStreamSolo = true;
}
Log.d(TAG, "onDestroy");
if (mSpeechRecognizer != null) {
mSpeechRecognizer.stopListening();
mSpeechRecognizer.cancel();
mSpeechRecognizer.destroy();
mSpeechRecognizer=null;
}
}
protected class SpeechRecognitionListener implements RecognitionListener {
@Override
public void onBeginningOfSpeech() {}
@Override
public void onBufferReceived(byte[] buffer) {}
@Override
public void onEndOfSpeech() {}
@Override
public synchronized void onError(int error) {
Log.d(TAG, "ERROR! ERROR!");
if(error==SpeechRecognizer.ERROR_RECOGNIZER_BUSY) {
if(mListener!=null) {
ArrayList<String> errorList=new ArrayList<String>(1);
errorList.add("ERROR RECOGNIZER BUSY");
if(mListener!=null)
mListener.onResults(errorList);
}
return;
}
if(error==SpeechRecognizer.ERROR_NO_MATCH) {
if(mListener!=null)
mListener.onResults(null);
}
if(error==SpeechRecognizer.ERROR_NETWORK) {
ArrayList<String> errorList=new ArrayList<String>(1);
errorList.add("STOPPED LISTENING");
if(mListener!=null)
mListener.onResults(errorList);
}
Log.d(TAG, "error = " + error);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
listenAgain();
}
},100);
}
@Override
public void onEvent(int eventType, Bundle params) {}
@Override
public void onPartialResults(Bundle partialResults) {}
@Override
public void onReadyForSpeech(Bundle params) {}
@Override
public void onResults(Bundle results) {
if(results!=null && mListener!=null)
mListener.onResults(results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION));
listenAgain();
}
@Override
public void onRmsChanged(float rmsdB) {}
}
public boolean ismIsListening() { return mIsListening; }
public interface onResultsReady {
public void onResults(ArrayList<String> results);
}
public void mute(boolean mute) { mMute=mute; }
public boolean isInMuteMode() { return mMute; }
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction().equals(Constants.ACTION.STARTFOREGROUND_ACTION)) {
Log.i(TAG, "Received Start Foreground Intent ");
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setAction(Constants.ACTION.MAIN_ACTION);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Intent previousIntent = new Intent(this, ForegroundService.class);
previousIntent.setAction(Constants.ACTION.PREV_ACTION);
PendingIntent ppreviousIntent = PendingIntent.getService(this, 0,
previousIntent, 0);
Bitmap icon = BitmapFactory.decodeResource(getResources(),
R.drawable.truiton_short);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("Truiton Music Player")
.setTicker("Truiton Music Player")
.setContentText("My Music")
.setSmallIcon(R.drawable.ic_launcher)
.setLargeIcon(
Bitmap.createScaledBitmap(icon, 128, 128, false))
.setContentIntent(pendingIntent)
.setOngoing(true)
.addAction(android.R.drawable.ic_media_previous,
"Previous", ppreviousIntent)
.build();
startForeground(Constants.NOTIFICATION_ID.FOREGROUND_SERVICE,
notification);
} else if (intent.getAction().equals(Constants.ACTION.PREV_ACTION)) {
Log.i(TAG, "Clicked Previous");
} else if (intent.getAction().equals(Constants.ACTION.STOPFOREGROUND_ACTION)) {
Log.d(TAG, "In onDestroy");
stopForeground(true);
stopSelf();
}
return START_STICKY;
}
@Override
public void onDestroy() {
Log.d(TAG, "In onDestroy");
super.onDestroy();
mIsListening=false;
if (!mIsStreamSolo) {
mAudioManager.setStreamMute(AudioManager.STREAM_NOTIFICATION, false);
mAudioManager.setStreamMute(AudioManager.STREAM_ALARM, false);
mAudioManager.setStreamMute(AudioManager.STREAM_MUSIC, false);
mAudioManager.setStreamMute(AudioManager.STREAM_RING, false);
mAudioManager.setStreamMute(AudioManager.STREAM_SYSTEM, false);
mIsStreamSolo = true;
}
Log.d(TAG, "onDestroy");
if (mSpeechRecognizer != null) {
mSpeechRecognizer.stopListening();
mSpeechRecognizer.cancel();
mSpeechRecognizer.destroy();
mSpeechRecognizer=null;
}
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "In onBind");
return null;
}
}