2015-10-15 236 views
0

我有一个文本到语音片段。片段由按钮组成,如果按下按钮,tts会说出给定的文本。

当我试图运行与真正的设备这个片段,HTC HTL21(API 16),我得到“泄露ServiceConnection android.speech.tts.TextToSpeech $连接”

此泄漏错误之前,我可以看到TTS停止/关闭战斗。但我无法弄清楚我的代码有什么问题。

奇怪的是,我没有得到与AVD(Android虚拟设备)或Genymotion此waring /错误。

我非常赞赏的任何帮助...

这里的日志猫的一部分:

10-15 18:23:45.524 14811-14811/jp.miyamura.hds_r I/TextToSpeech: Sucessfully bound to com.google.android.tts 
10-15 18:23:45.574 14811-14811/jp.miyamura.hds_r W/TextToSpeech: stop failed: not bound to TTS engine 
10-15 18:23:45.574 14811-14811/jp.miyamura.hds_r W/TextToSpeech: shutdown failed: not bound to TTS engine 
10-15 18:23:45.654 14811-14811/jp.miyamura.hds_r I/TextToSpeech: Sucessfully bound to com.google.android.tts 
10-15 18:23:45.654 14811-14811/jp.miyamura.hds_r W/TextToSpeech: shutdown failed: not bound to TTS engine 
10-15 18:23:45.664 14811-14811/jp.miyamura.hds_r I/TextToSpeech: Sucessfully bound to com.google.android.tts 

10-15 18:23:45.794 14811-14811/jp.miyamura.hds_r E/ActivityThread: Activity jp.miyamura.hds_r.MainActivity has leaked ServiceConnection [email protected] that was originally bound here 
10-15 18:23:45.794 14811-14811/jp.miyamura.hds_r E/ActivityThread: android.app.ServiceConnectionLeaked: Activity jp.miyamura.hds_r.MainActivity has leaked ServiceConnection [email protected] that was originally bound here 

这是我的演讲片段:

package jp.miyamura.hds_r; 

import android.annotation.TargetApi; 
import android.app.Activity; 
import android.app.Fragment; 
import android.os.Build; 
import android.os.Bundle; 
import android.speech.tts.TextToSpeech; 
import android.speech.tts.UtteranceProgressListener; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.Button; 

import java.util.HashMap; 
import java.util.Locale; 


public class SpeechFragment extends Fragment implements TextToSpeech.OnInitListener, View.OnClickListener { 

    private int buttonID = 0; 
    private TextToSpeech tts; 
    private String queuedText; 
    private String splitChar; 

    // Instantiate Speech fragment itself 
    public static SpeechFragment newInstance(String speechString, String buttonString, int buttonID, String splitChar) { 

     // Store data to be passed to the fragment in bundle format 
     Bundle args = new Bundle(); 
     args.putString("Speech", speechString); 
     args.putString("ButtonTitle", buttonString); 
     args.putInt("ButtonID", buttonID); 
     args.putString("splitChar", splitChar); 

     // Instantiate Speech fragment and set arguments 
     SpeechFragment newFragment = new SpeechFragment(); 
     newFragment.setArguments(args); 
     return newFragment; 
    } 

    // Declare interface to communicate with it's container Activity 
    public interface onSpeechFragmentListener { 
     void onUtteranceStatus(boolean status); 
     void onClickStatus(int buttonID); 
    } 

    private onSpeechFragmentListener myCallback; 

    @Override 
    @SuppressWarnings("deprecation") 
    public void onAttach(Activity activity) { 
     super.onAttach(activity); 

     // Check container Activity implements interface listener or not 
     try { 
      myCallback = (onSpeechFragmentListener) activity; 
     } catch (ClassCastException e) { 
      throw new ClassCastException(activity.toString() 
        + " must implement onSpeechFragmentListener"); 
     } 
    } 

    @Override 
    public void onDetach() { 
     super.onDetach(); 
     myCallback = null; 
    } 

    // Required empty public constructor 
    public SpeechFragment() {} 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
    } 

    @Override 
    @SuppressWarnings("deprecation") 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 

     // Instantiate TTS 
     if (tts == null) tts = new TextToSpeech(getActivity(), this); 

     // Retrieve root view of fragment 
     View rootView = inflater.inflate(R.layout.fragment_speech, container, false); 

     String button_title = ""; 
     splitChar = ""; 

     // Receive arguments from it's container Activity 
     if (getArguments() != null) { 
      queuedText = getArguments().getString("Speech"); 
      button_title = getArguments().getString("ButtonTitle"); 
      buttonID = getArguments().getInt("ButtonID"); 
      splitChar = getArguments().getString("splitChar"); 
     } 

     // Retrieve button and set onClick listener 
     Button button = (Button) rootView.findViewById(R.id.speech_Button); 
     button.setText(button_title); 
     button.setId(buttonID); 

     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // ToDo: Should be Build.VERSION_CODES.MARSHMALLOW 
      button.setTextAppearance(android.R.style.TextAppearance_Medium); 
     } else { 
      button.setTextAppearance(getActivity(), android.R.style.TextAppearance_Medium); 
     } 

     button.setOnClickListener(this); 

     return rootView; 
    } 

    @Override 
    public void onStop() { 

     if(tts !=null) { 

      tts.stop(); 
     } 
     super.onStop(); 
    } 

    @Override 
    public void onDestroyView() { 

     if(tts !=null) { 

      tts.shutdown(); 
     } 
     super.onDestroyView(); 
    } 

    @Override 
    public void onInit(int status) { 

     if (status == TextToSpeech.SUCCESS && tts != null) { 

      // Set TTS Locale 
      tts.setLanguage(Locale.getDefault()); 
     } 
    } 

    @Override 
    public void onClick(View v) { 

     // tts must not be null 
     if (tts != null) { 

      String utteranceId = this.hashCode() + ""; 

      // TTS Branch with Android OS Version 
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
       ttsGreater21(queuedText, utteranceId); 
      } else { 
       ttsUnder20(queuedText, utteranceId); 
      } 
     } 

     myCallback.onClickStatus(buttonID); 
    } 

    // Speech method before LOLLIPOP 
    @SuppressWarnings("deprecation") 
    private void ttsUnder20(String text, String utteranceId) { 

     HashMap<String, String> map = new HashMap<>(); 
     map.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, utteranceId); 

     // Split speech text with new line character and speak silence for each separation 
     String[] splitSpeech = text.split(splitChar); 
     for (int i = 0; i < splitSpeech.length; i++) { 

      if (i == 0) { // Use for the first split text to flush on audio stream 
       tts.speak(splitSpeech[i].trim(),TextToSpeech.QUEUE_FLUSH, map); 
      } else { // add the new text on previous then play the TTS 
       tts.speak(splitSpeech[i].trim(), TextToSpeech.QUEUE_ADD,map); 
      } 
      // Play Silence between text split 
      tts.playSilence(750, TextToSpeech.QUEUE_ADD, null); // ToDo Length of silence should be optimized 
     } 

     setTTSListener(); 
    } 

    // Speech method for LOLLIPOP and after 
    @TargetApi(Build.VERSION_CODES.LOLLIPOP) 
    private void ttsGreater21(String text, String utteranceId) { 

     // Split speech text with new line character and speak with silence 
     String[] splitSpeech = text.split(splitChar); 
     for (int i = 0; i < splitSpeech.length; i++) { 

      if (i == 0) { // Use for the first split text to flush on audio stream 
       tts.speak(splitSpeech[i].trim(), TextToSpeech.QUEUE_FLUSH, null, utteranceId); 
      } else { // add the new text on previous then play the TTS 
       tts.speak(splitSpeech[i].trim(), TextToSpeech.QUEUE_ADD, null, utteranceId); 
      } 
      // Play Silence between text split 
      tts.playSilentUtterance(750, TextToSpeech.QUEUE_ADD, null); // ToDo Length of silence should be optimized 
     } 

     setTTSListener(); 
    } 

    // TTS Status listener method 
    private void setTTSListener(){ 
     tts.setOnUtteranceProgressListener(new UtteranceProgressListener() { 
      @Override 
      public void onStart(String utteranceId) { 
      } 

      @Override 
      public void onDone(String utteranceId) { 
       myCallback.onUtteranceStatus(true); 
      } 

      @Override 
      public void onError(String utteranceId) { 
       myCallback.onUtteranceStatus(false); 
      } 
     }); 
    } 
} 

回答

0

TTS停止/关机失败是由屏幕旋转引起的。一旦停止/关闭失败,就会触发泄漏错误。所以没有任何与上述片段代码相关...

在我的活动代码中,我重新创建了片段旋转的片段。我重新创建的原因是我忘记继承onSavedinstancestate。然后通过屏幕旋转丢失片段。

所以对策是:

@Override 
public void onSaveInstanceState(@NonNull Bundle savedInstanceState) { 
    super.onSaveInstanceState(savedInstanceState); // Add this line 

    savedInstanceState.putString("inputWords", tv_input_word.getText().toString()); 
} 

而且

if (savedInstanceState == null) { // Add this line 

    // Setup Fragment 
    transaction = getFragmentManager().beginTransaction(); 
    transaction.replace(R.id.fragment_container1, 
       SpeechFragment.newInstance(speechString, buttonString, buttonID1, splitChar)); 
    transaction.commit(); 
} // Add this line