I've been having app crashes due to an out-of-memory condition (in the program, not the programmer). MAT shows that copies of my Activity were sometimes being retained across screen rotations, and the only object keeping the bogus copies alive was each instance's TextToSpeech object. I can duplicate this behaviour using this snippet:
public class MainActivity extends Activity {
TextToSpeech mTts;
char[] mBigChunk = new char[1000000]; // not used; just makes MainActivity instances easier to see in MAT
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onStart() {
super.onStart();
if (mTts==null) // shouldn't be necessary and doesn't make any difference
mTts = new TextToSpeech(this, null); // commenting this out fixes the leak
}
@Override
public void onStop() {
super.onStop();
if (mTts != null) {
mTts.shutdown();
mTts = null; // shouldn't be necessary and doesn't make any difference
}
}
}
After 30 orientation changes, MAT lists between one and eight instances of net.catplace.tts_leak.MainActivity, and also multiple instance of various TTS objects; eg:
Class Name | Shallow Heap | Retained Heap | Percentage
------------------------------------------------------------------------------------------------------------------
android.speech.* | | |
android.speech.tts.TextToSpeech$Connection$1 @ 0x42de94c8 Native Stack| 24 | 2,052,664 | 11.85%
android.speech.tts.TextToSpeech$Connection$1 @ 0x431dd500 Native Stack| 24 | 2,052,664 | 11.85%
android.speech.tts.TextToSpeech$Connection$1 @ 0x435cc438 Native Stack| 24 | 552 | 0.00%
android.speech.tts.TextToSpeech$Connection @ 0x441b3698 | 32 | 528 | 0.00%
android.speech.tts.TextToSpeech @ 0x43fb3c00 | 64 | 496 | 0.00%
android.speech.tts.TextToSpeech$Connection @ 0x43fb4420 | 32 | 48 | 0.00%
android.speech.tts.TextToSpeech$Connection$1 @ 0x43fb4440 Native Stack| 24 | 24 | 0.00%
android.speech.tts.TextToSpeech$Connection$1 @ 0x441b36b8 Native Stack| 24 | 24 | 0.00%
Total: 8 entries (13,079 filtered) | | |
------------------------------------------------------------------------------------------------------------------
MAT indicates that the bogus copies of MainActivity are being retained by TTS:
Class Name | Shallow Heap | Retained Heap
---------------------------------------------------------------------------------------------------------------------
| |
net.catplace.tts_leak.MainActivity @ 0x437c6068 | 200 | 2,001,352
'- mContext android.speech.tts.TextToSpeech @ 0x431de6d8 | 64 | 496
'- this$0 android.speech.tts.TextToSpeech$Connection @ 0x441b3698 | 32 | 528
'- this$1 android.speech.tts.TextToSpeech$Connection$1 @ 0x441b36b8 Native Stack| 24 | 24
---------------------------------------------------------------------------------------------------------------------
I get this behavour across a range of real devices and AVDs. The above results are from a Nexus 7.
I've tried different TTS engines, using different events to create and destroy mTts, etc.
My hypothesis is that TextToSpeech doesn't always null its reference to the context that created it, resulting in leaked copies of the context (Activity). But I'm new at this; is there something I'm doing wrong?
Taking a look at the source code of TextToSpeech here, you'll notice that it actually binds a service to the context passed and the shutdown method actually unbinds it. Now rest ahead is guess, since Service has its own life cycle,the TextToSpeech might be holding back the context.If you did some research keeping this in mind that you're actually running a service,you might crack the question.
Now I'm not also sure what this might imply but I am open for any new findings from your side. Given that TextToSpeech is a service you might want to pass it the application context, since the service would still be running when activity gets destroyed.
Also for further reading ______________
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With