泄漏的ServiceConnection android.speech.tts.TextToSpeech$Connection
我有一段文字到语音的片段。片段由按钮组成,如果按下按钮,tts将说出给定的文本 当我试图用真正的设备HTC HTL21(API 16)运行这个片段时,我得到了“泄漏的ServiceConnection android.speech.tts.TextToSpeech$Connection” 在此泄漏错误之前,我可以看到tts停止/关闭警告。但是我不知道我的代码出了什么问题 奇怪的是,我没有在AVD(Android虚拟设备)或Genymotion上看到这个警告/错误 非常感谢您的帮助 下面是日志cat的一部分:泄漏的ServiceConnection android.speech.tts.TextToSpeech$Connection,android,android-fragments,memory-leaks,Android,Android Fragments,Memory Leaks,我有一段文字到语音的片段。片段由按钮组成,如果按下按钮,tts将说出给定的文本 当我试图用真正的设备HTC HTL21(API 16)运行这个片段时,我得到了“泄漏的ServiceConnection android.speech.tts.TextToSpeech$Connection” 在此泄漏错误之前,我可以看到tts停止/关闭警告。但是我不知道我的代码出了什么问题 奇怪的是,我没有在AVD(Android虚拟设备)或Genymotion上看到这个警告/错误 非常感谢您的帮助 下面是日志ca
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 android.speech.tts.TextToSpeech$Connection@40fd08e8 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 android.speech.tts.TextToSpeech$Connection@40fd08e8 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);
}
});
}
}
包jp.miyamura.hds\r;
导入android.annotation.TargetApi;
导入android.app.Activity;
导入android.app.Fragment;
导入android.os.Build;
导入android.os.Bundle;
导入android.speech.tts.TextToSpeech;
导入android.speech.tts.utranceProgressListener;
导入android.view.LayoutInflater;
导入android.view.view;
导入android.view.ViewGroup;
导入android.widget.Button;
导入java.util.HashMap;
导入java.util.Locale;
公共类SpeechFragment扩展片段实现TextToSpeech.OnInitListener、View.OnClickListener{
私有int buttonID=0;
私密文本语音tts;
私有字符串队列文本;
私有字符串splitChar;
//实例化语音片段本身
公共静态SpeechFragment newInstance(String speechString、String buttonString、int buttonID、String splitChar){
//以捆绑格式存储要传递给片段的数据
Bundle args=新Bundle();
args.putString(“Speech”,speechString);
参数putString(“buttontile”,buttonString);
参数putInt(“ButtonID”,ButtonID);
args.putString(“splitChar”,splitChar);
//实例化语音片段并设置参数
SpeechFragment newFragment=新SpeechFragment();
setArguments(args);
返回新片段;
}
//声明接口以与其容器活动通信
公共接口onSpeechFragmentListener{
void onutternancestatus(布尔状态);
void onclick状态(int buttonID);
}
私有onSpeechFragmentListener myCallback;
@凌驾
@抑制警告(“弃用”)
公共事务主任(活动){
超级转速计(活动);
//检查容器活动是否实现接口侦听器
试一试{
myCallback=(onSpeechFragmentListener)活动;
}catch(ClassCastException e){
抛出新的ClassCastException(activity.toString()
+“必须实现onSpeechFragmentListener”);
}
}
@凌驾
公共无效连接(){
super.onDetach();
myCallback=null;
}
//必需的空公共构造函数
公共演讲片段(){}
@凌驾
创建时的公共void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
}
@凌驾
@抑制警告(“弃用”)
CreateView上的公共视图(布局、充气机、视图组容器、捆绑包保存状态){
//实例化TTS
如果(tts==null)tts=newtexttospeech(getActivity(),则为该值);
//检索片段的根视图
视图根视图=充气机。充气(R.layout.fragment\u语音,容器,false);
字符串按钮_title=“”;
splitChar=“”;
//从其容器活动接收参数
如果(getArguments()!=null){
queuedText=getArguments().getString(“语音”);
button_title=getArguments().getString(“Buttontile”);
buttonID=getArguments().getInt(“buttonID”);
splitChar=getArguments().getString(“splitChar”);
}
//检索按钮并设置onClick侦听器
Button Button=(Button)rootView.findViewById(R.id.speech_按钮);
按钮.setText(按钮标题);
button.setId(buttonID);
如果(Build.VERSION.SDK\u INT>=Build.VERSION\u CODES.M){//ToDo:应该是Build.VERSION\u CODES.MARSHMALLOW
button.setTextAppearance(android.R.style.TextAppearance_-Medium);
}否则{
setExtAppearance(getActivity(),android.R.style.TextAppearance_-Medium);
}
setOnClickListener(此);
返回rootView;
}
@凌驾
公共void onStop(){
如果(tts!=null){
tts.stop();
}
super.onStop();
}
@凌驾
公共无效onDestroyView(){
如果(tts!=null){
tts.shutdown();
}
super.onDestroyView();
}
@凌驾
公共无效onInit(int状态){
if(status==TextToSpeech.SUCCESS&&tts!=null){
//设置TTS区域设置
setLanguage(Locale.getDefault());
}
}
@凌驾
公共void onClick(视图v){
//tts不能为空
如果(tts!=null){
字符串outtanceId=this.hashCode()+“”;
//使用安卓操作系统版本的TTS分支
if(Build.VERSION.SDK\u INT>=Build.VERSION\u code.LOLLIPOP){
ttsGreater21(queuedText,outtanceid);
}否则{
ttsUnder20(队列文本,话语ID);
}
}
myCallback.onClickStatus(buttonID);
}
//棒棒糖前的语音方法
@抑制警告(“弃用”)
私有void ttsUnder20(字符串文本、字符串语句ID){
HashMap=newHashMap();
map.put(TextToSpeech.Engine.KEY_PARAM_outrance_ID,outranceid);
//使用新行字符拆分语音文本,并在每次分离时保持沉默
String[]splitSpeech=text.split(splitChar);
for(int i=0;i@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