androidloopsghost-blog

Block execution over a variable (TTS Use-Case), other than log statements (spooky)


I wish to have a Boolean be true before executing a block of code, and I want it to actually block the thread it is on. The use-case is simple enough. I have a use-case for TTS in my app, and i wish to have a sequential execution, where a TTS speaks, then something happens and it speaks again and so on, but the thing is that this is entirely time-sensitive. I need to start a timer as soon as the TTS finishes talking, but it turns out the speak(...) method is non-blocking (apparently), and so the moment it speaks, the control shifts to the next line.

To handle this, I got hold of a variable isSpeaking implemented using the deprecated OnUtteranceCompletedListener, and that seems to work just fine, as always.

However, it is a SUPER-PECULIAR execution that I see in my implementation. The variable, as I mentioned earlier tracks the speech just fine, but when I apply my loop like so,

while(speaking){
  continue
}

IT BLOCKS UNTIL SPEECH OCCURS, AND THEN THE CONTROL JUST VANISHES!!!,

I put a log statement right after this loop, and once the condition becomes false, i.e., the loop breaks, it never logs that part of the code. In fact, it does nothing at all afterwards. It is like the execution is "lost", somewhere after that. The app does not even freeze. I use Jetpack-Compose so all the infinite-repeatable animations work just fine, but the app doesn't do anything afterwards.

But, here's the thing -- if i replace my continue with just a Log statement, it magically works. EVERYTHING WORKS, ABSOLUTELY NO UNUSUAL OBSERVATIONS UPON THE ADDITION OF ONE LOG STATMENT.

I remember I observed this as a child, but was not in touch with the overflow community back in the day, and so I never posted that. I never made a TTS-using app afterwards so I couldn't share anything.

What I am saying is, this works:

while(speaking){
 Log.i("WHY?", "speaking")
}

This hurts, since I can't use Log calls in my production softwares, the official docs strictly prohibit it. Help!


Solution

  • You're mishandling a multithreading condition and that's going to be a problem. The loop you're using is waiting for a variable to change on another thread. First off, that only works if the variable is volatile. If not the interpreter doesn't know it needs to reload from memory and this loop will never end. Secondly, what you're doing is called busy waiting and is a BAD idea for battery issues (and if done on the UI thread can cause your app to become non responsive or even be killed by a watchdog timer). Instead, if this is not the UI thread you should be using a signaling mechanism like a semaphore. That would allow you to wait, without idling the CPU, until the signal is given. If this is the UI thread, you shouldn't do it at all. Instead you should have the speech listener post a message to a Handler to the UI thread, and perform whatever is after the loop in response to that message.