使用LeakCanary解决TTS内存泄漏问题

3
这是LeakCanary检测到的内存泄漏描述:(发生在按下Android返回按钮时)
1 APPLICATION LEAKS

    References underlined with "~~~" are likely causes.
    Learn more at https://squ.re/leaks.

    356182 bytes retained by leaking objects
    Displaying only 1 leak trace out of 2 with the same signature
    Signature: 2276fec44ae233e0d5bb5b82648d5836c07e3b33
    ┬───
    │ GC Root: Global variable in native code
    │
    ├─ android.speech.tts.TextToSpeech$Connection$1 instance
    │    Leaking: UNKNOWN
    │    Anonymous subclass of android.speech.tts.ITextToSpeechCallback$Stub
    │    ↓ TextToSpeech$Connection$1.this$1
    │                                ~~~~~~
    ├─ android.speech.tts.TextToSpeech$Connection instance
    │    Leaking: UNKNOWN
    │    ↓ TextToSpeech$Connection.this$0
    │                              ~~~~~~
    ├─ android.speech.tts.TextToSpeech instance
    │    Leaking: UNKNOWN
    │    ↓ TextToSpeech.mContext
    │                   ~~~~~~~~
    ╰→ com.example.price.SignUpDisplay instance
    ​     Leaking: YES (ObjectWatcher was watching this because com.example.price.SignUpDisplay received Activity#onDestroy() callback and Activity#mDestroyed is true)key = 6965e004-28de-464b-87d7-2668461623e7
    ​     watchDurationMillis = 30282retainedDurationMillis = 25282

这是我在活动中初始化 tts 的方法:
protected fun initializeTextToSpeech() {
        mtts = TextToSpeech(this, TextToSpeech.OnInitListener { status ->
            // If a success, set the language
            if (status == TextToSpeech.SUCCESS) {
                res = mtts.setLanguage(Locale.UK)
            } else {
                Toast.makeText(
                    this,
                    "Feature not supported in your device", Toast.LENGTH_SHORT
                ).show()
            }
        })
    }

这是 onDestroy 方法:

override fun onDestroy() {
        if (mtts != null) {
            mtts.stop()
            mtts.shutdown()
        }
        super.onDestroy()
    }

为什么会发生泄漏,我该如何修复?

日志中也出现了这个警告 - 我不确定它有多严重或是否与泄漏有关:

W/TextToSpeech: stop failed: not bound to TTS engine

https://dev59.com/9W_Xa4cB1Zd3GeqP7PzM#20863947 - Nerdy Bunz
他们似乎建议将tts.shutdown()放在onDestory()中,这也是我所做的。 - le0p4rd
他们还提到了使用应用程序上下文而不是活动上下文来初始化tts的实践...这可能会解决问题。 - Nerdy Bunz
1个回答

4
问题在于TextToSpeech在其构造函数中保留了上下文的强引用,一个Connection实例保持对TextToSpeech实例(因为它是外部类)的强引用,并且本地引用保留对Connection的引用。

解决这个问题的一种方法是使用应用程序上下文而不是活动上下文绑定TTS服务。

initializeTextToSpeech() 中,改为使用以下代码:

    mtts = TextToSpeech(this, TextToSpeech.OnInitListener { status ->

试试这个:

    mtts = TextToSpeech(getApplicationContext(), TextToSpeech.OnInitListener { status ->

1
谢谢您的清晰回复!然而,我仍然遇到相同的错误。我在onDestroy中所做的是否足够?我还在片段中使用TTS实例 - 这会导致错误吗?例如,在片段中调用(activity as BaseDisplay).mtts.speak()。 - le0p4rd
如果你应用了这个修复方法,那么你不应该再得到完全相同的错误,但是可能会遇到其他类似的错误,这意味着你需要在其他地方执行相同的修复操作? - Pierre-Yves Ricau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接