2014-04-15 1 views
2

텍스트를 말하기 위해 공용 함수를 작성했지만 다음 코드는 좋지 않다고 생각하지만 개선 방법을 모르겠습니다. 제안을 해 주실 수 있습니까? 감사!누출을 방지하기 위해 TextToSpeech의 정적 var을 해제하는 방법은 무엇입니까?

나는 정적 var TextToSpeech tts가 누출을 야기 할 수 있다고 생각하는데, 나는 그것을 풀어 낼 방법을 모른다.

public class SpeechTxt { 

    private static TextToSpeech tts; 

    public static void SpeakOut(final Context myContext, String s) {   

     tts = new TextToSpeech(myContext, new TextToSpeech.OnInitListener(){ 
      @Override 
      public void onInit(int status) { 
       // TODO Auto-generated method stub 
       if (status == TextToSpeech.SUCCESS) { 

        int result = tts.setLanguage(Locale.US); 

        if (result == TextToSpeech.LANG_MISSING_DATA 
          || result == TextToSpeech.LANG_NOT_SUPPORTED) { 
         Toast.makeText(myContext, "Language is not supported",Toast.LENGTH_SHORT).show(); 
        } else { 
         tts.speak("Hello, the world! "+s, TextToSpeech.QUEUE_ADD, null); 
        }   

       }else { 
        Toast.makeText(myContext, "Initilization Failed",Toast.LENGTH_SHORT).show(); 
       } 
      } 

     }); 

     /* I must comment the code, or phone can't speak 
     if (tts != null) { 
      tts.stop(); 
      tts.shutdown(); 
     } ; 
     */ 

     Toast.makeText(myContext, "This is a test",Toast.LENGTH_SHORT).show(); 
    } 

} 
+0

새 인스턴스를 생성하기 전에 종료하고 'null'합니다. –

+0

더 좋은 코드가 있습니까? – HelloCW

+0

onCreate 메소드에서 객체를 초기화하고 onStop에서 객체를 종료하는 방법은 무엇입니까? – nasch

답변

5

당신은, 그들은 일반적으로 활동 컨텍스트에서 호출 할 수 있습니다 정적 메서드에 TextToSpeech를 정의 할 경우에도 할 수 있습니다 여전히 종료 소위 TextToSpeech를 사용하는 활동이 파괴 될 때마다 TextToSpeech. 문서 상태는 : 그것은 활동의 들의 OnDestroy() 메소드에서이 메소드를 호출하는 예를 들어 좋은 방법이다

그래서 TextToSpeech 엔진은 이 완전히 정지 될 수 있습니다. 각 후 그것을 종료 할 필요는 없습니다 한편

http://developer.android.com/reference/android/speech/tts/TextToSpeech.html#shutdown()

내가 한 활동이 실행되는 초기화 TextToSpeech 객체를 유지하는 것이 좋습니다 그래서 사용합니다. 이렇게하면 각 "말하기"작업 전에 TextToSpeech가 초기화되지 않습니다. 기기의 TextToSpeech가 초기화 된 후에는 일반적으로 매우 무거운 작업이 아니지만 여전히 얻을 수있는 몇 밀리 초입니다.

NTSus 5에서 TTS를 시작하는 데는 처음 약 1.3 초가 걸리고 그 후에 각 인스턴스화에는 50 ~ 80ms가 소요되며 실제로 저장할 수 있습니다.

메모리 누수가 염려되는 경우 일반적으로 Activity 컨텍스트가되는 컨텍스트 대신 context.getApplicationContext()를 사용하여 응용 프로그램 컨텍스트를 사용하여 TTS를 초기화합니다. nKn가 제안 - -

은 또한 GC 그것이 VM이 메모리가 부족 실행해야 재활용 할 수 있도록하기 위해하는 SoftReference를 사용 (이 VM이 OutOfMemoryError를 던졌습니다 전에 모든 SoftReferences 재활용 될 것으로 보장된다, 참조 : http://docs.oracle.com/javase/7/docs/api/java/lang/ref/SoftReference.html)를.

코드를 더욱 향상 시키려면 사용자가 언어를 설치하도록 허용하여 누락 된 언어 사례를 처리해야합니다. 그 전이나 개체가 파괴 된 이후에 수행하지 않은 경우 TextToSpeech 만 인스턴스화 볼 수 있듯이

public class SpeechTxt { 

    private static SoftReference<TextToSpeech> sTts; 

    public static void speakOut(final Context context, final String s) { 
     final Context appContext = context.getApplicationContext(); 
     if (sTts == null) { 
      sTts = new SoftReference<TextToSpeech>(new TextToSpeech(appContext, new TextToSpeech.OnInitListener(){ 
       @Override 
       public void onInit(int status) { 
        if (status == TextToSpeech.SUCCESS) { 
         speak(appContext, s); 
        } 
        else { 
         loadText2SpeechData(appContext); 
        } 
       } 
      })); 
     } 
     else { 
      speak(appContext, s); 
     } 
    } 

    public static void destroyTTS(Context context) { 
     if (sTts != null && ! sTts.get().isSpeaking()) { 
      sTts.get().shutdown(); 
      sTts = null; 
     } 
    } 

    private static void speak(Context context, String s) { 
     if (sTts != null) { 
      switch (sTts.get().setLanguage(Locale.getDefault())) { 
      case TextToSpeech.LANG_COUNTRY_AVAILABLE: 
      case TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE: 
      case TextToSpeech.LANG_AVAILABLE: { 
       sTts.get().speak(s, TextToSpeech.QUEUE_ADD, null); 
       break; 
      } 
      case TextToSpeech.LANG_MISSING_DATA: { 
       loadText2SpeechData(context); 
       break; 
      } 
      case TextToSpeech.LANG_NOT_SUPPORTED: // not much to do here 
      } 
     } 
    } 

    private static void loadText2SpeechData(Context context) { 
     try { 
      Intent installIntent = new Intent(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA); 
      installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
      context.startActivity(installIntent); 
     } 
     catch (ActivityNotFoundException ignore) {} 
    } 
} 

:

다음은 코드를 향상시킬 수있는 방법을 내 제안입니다. 또한 응용 프로그램 컨텍스트 만 사용하므로 메모리 누수가 없습니다. FLAG_ACTIVITY_NEW_TASK를 사용하고 있기 때문에 응용 프로그램 컨텍스트로 활동을 시작하는 것도 문제가되지 않습니다.

비 활동 컨텍스트 (예 : BroadCastReceiver)에서 TTS를 사용하는 경우에도 TextToSpeech 개체를 초기화하고 소멸 (및 기본 리소스를 해제) 할 수있는 일종의 수명주기가 남아 있습니다. IMO는 SoftReference를 사용할 때 특히 필요하지 않습니다.

브로드 캐스트 리시버 오브젝트가 onReceive 호출 기간 (문맥, 의도)에 대해서만 유효하십시오 브로드 캐스트 리시버는 라이프 사이클을 가지고있는 동안

참고 문서는 것을 말한다있다. 이 함수에서 코드가 반환되면 시스템은 객체가 완료된 것으로 간주하고 더 이상 을 활성화하지 않습니다.

이것은 당신이 onReceive에서 무엇을 할 수 있는지에 중요한 영향 (문맥, 의도) 구현이있다 : 당신이 비동기 작업을 처리하기 위해 함수에서 반환해야하므로, 비동기 작업을 필요로 사용할 수없는 것을, 그러나 에서 BroadcastReceiver는 더 이상 활성 상태가 아니므로 이 완료되기 전에 시스템은 프로세스를 종료 할 수 있습니다.

http://developer.android.com/reference/android/content/BroadcastReceiver.html#ReceiverLifecycle

이것은 말하는 호출이 비동기이기 때문에 당신이 브로드 캐스트 리시버에 TTS를 중지 할 수 있으며이 후 TTS 오브젝트를 파괴에 완료 될 때까지 기다릴 수 없음을 의미합니다. TTS 객체를 파괴하려면 (다시는 필요 없다고 생각하는 경우) 예를 들어 시작해야합니다. 서비스 (또는 UI가없는 활동). 이 서비스는 speak 메서드를 호출하고 OnUtteranceCompletedListener (또는 OnUtteranceProgressListener)가 반환 할 때까지 기다립니다. 그랬 으면 :

그런데 각각의 말을 듣고 나면 TTS 객체를 파괴하려면 실제로 정적 코드를 사용할 필요가 없습니다.

+0

감사! 나는 비 활동 컨텍스트 (BroadCastReceiver)에서 TextToSpeech를 사용하므로 정적 함수 "public static void SpeakOut()"을 작성하므로 BroadCastReceiver에 파괴가 없습니다. TextToSpeech 객체는 어디에 파기 할 수 있습니까? – HelloCW

+0

BroadcastReceiver가 비동기 작업을 허용하지 않기 때문에이 작업을 수행 할 수 없습니다. 그에 따라 내 대답이 바뀌 었습니다. 내 질문에 아직도 왜 그것을 파괴하고 싶습니까? 필요하지 않은 경우 귀중한 자원을 낭비합니다. –

+0

감사! 아마도 sTts.get(). isSpeaking() 및 sTts.get(). shutdown() 코드가 잘못되었습니다. – HelloCW

1

new으로 만든 모든 개체는 힙에 저장됩니다. GC이 실행될 때마다 해제 될 수있는 객체를 해제합니다. 즉, (다른 상황과 함께) 다른 참조에서 다시 연결할 수없는 객체를 해제합니다.

사실 누수가 발생할 수 있다는 사실을 염두에 둔다면 null으로 설정하면됩니다. 또 다른 방법은 해당 개체에 SoftReference을 사용하는 것입니다. 그러면 GC에게이 개체에 해제 할 환경 설정이 있음을 알릴 것이므로 Android OS에 메모리가 부족하면 다른 개체보다 더 쉽게 해제 할 수 있습니다.

private static SoftReference<TextToSpeech> tts; 
... 
tts = new SoftReference<TextToSpeech>(...); 

그런 개체가 tts.get() 대신 단지 tts에 도달 할 것입니다 :이 경우, 당신은 단순히 이런 일을해야한다. 이 방법을 사용하는 경우이 객체를 사용할 때마다 null인지 여부를 확인해야합니다. 이제는 GC에 의해 해제 될 가능성이 높습니다. 그러나

if (tts != null) { ... } 

, 메모리 누수가 경험적인 방법으로 그것을 시험하고있다 여부를 알 수있는 가장 좋은 방법. 이를 위해 나는 DDMS + HPROF을 사용하여 메모리 덤프를 작성하여 분석하고 오브젝트에 필요한 것보다 많은 양의 메모리가 할당되어 있는지 확인합니다. 이 주제는 매우 강하고 실습이 필요하지만이 주제에 관해 많은 학습을 돕는 몇 가지 링크를 남겨 두겠습니다.

+0

감사! 안드로이드 시스템 메모리 누수를 피할 수 있지만 그 샘플 코드를 추가해야하는 이유를 모르겠다면 (tts! = null) { tts.stop(); tts.shutdown(); }; – HelloCW

+0

TextToSpeech 개체를 중지하고 종료하지 않으면 백그라운드에서 계속 실행되고 메모리 리소스가 발생합니까? – HelloCW

+0

이것은 깨끗한 방법 일 것이고, 현재 발화를 멈추고 모든 취해진 자원을 확보 할 것이므로 권장되는 방법입니다. 그렇지 않으면 (구현에 따라) 메모리 누수가 발생할 수 있습니다. 그러나, 당신이 그것을 확인하는 가장 좋은 방법은'DDMS' +'HPROF' 테스트를하는 것입니다. 하나의 객체가 얼마나 많은 양을 메모리에 차지해야하는지, 그리고 실제로 얼마나 많이 차지했는지를 볼 수 있습니다.하지만 일단 닫은 후에는'tts = null '을 설정하면 누출이 없어야합니다 (최소한 그 부분과 함께). – nKn

관련 문제