0

So when running the following method in doInBackground() inside of AsyncTask derived class:

void waitUntilButtonClicked(){
            while(true){
            synchronized (buttonClicked){
                if(buttonClicked) return;
                                        }
                try{Thread.sleep(1);} catch(InterruptedException e){};

                             }

without the very last line of code which makes the background thread sleep for 1 ms, the User Interface doesn't work (there is an EditText widget which when tapped on doesn't respond at all). I should mention that when waitUntilButtonClicked() runs the UI thread doesn't run anything (or rather not any of my code).

My problem is that I HAD to add the last line for everything to work. I thought that a background thread cannot block the UI thread unless there is a huge error on the programmer's part. Why does it happen? And yes I've figured out a 'way' to overcome that, is my solution a common method of doing that? Is there a better way?

Yoni Keren
  • 1,170
  • 2
  • 13
  • 24

1 Answers1

1

Why does it happen?

You are tying up the CPU in a busy spin loop, starving everything else of CPU cycles.

And yes I've figured out a 'way' to overcome that, is my solution a common method of doing that?

No, as sleeping to make a busy spin loop less busy generally is considered to be poor form.

Is there a better way?

Let's assume, given your code, that you have a Button, and when the Button is clicked, you want to do work in a background thread.

If that is the case, you could:

  • Only fork the thread once the Button is clicked, in the onClick() for the Button

  • Maintain a thread pool (e.g., Executors.newSingleThreadExecutor()), and post a job to that thread pool in the onClick() for the Button

  • Use startService() to kick off an IntentService in the onClick() for the Button, where the IntentService does the work in onHandleIntent() (which is called on a background thread)

  • Use a HandlerThread and post() events to it from onClick() for the Button

  • Use mid-level blocking mechanisms, like CountDownLatch or Semaphore, where you trigger your background thread that way instead of via the boolean

  • Use seriously low-level blocking mechanisms, like Object#wait()

Probably there are other options as well, but those six would be a good starting point. All of these cases use OS-level blocking primitives to mark a thread as suspended until necessary, rather than your approach of waking up the thread unnecessarily every millisecond.

Community
  • 1
  • 1
CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • First of all nice answer I can, and will, take a lot from it. However there is just one point which I did ask, is important and you have missed: even though that loop is a bad form, why does it starve the other (UI) thread?! It confuses me since..even if the OS doesn't prioritize the main thread, shouldn't it switch between the different threads,especially on interrupts such as the user touching the EditText widget? – Yoni Keren May 01 '16 at 21:39
  • @YoniKeren: "even if the OS doesn't prioritize the main thread" -- AFAIK, the main application thread is normal priority. Your job when creating a background thread is to reduce its priority, which helps a bit but is no performance cure-all. "shouldn't it switch between the different threads,especially on interrupts such as the user touching the EditText widget?" -- it is, though there seems to be some event loss when the main application thread is sluggish to process events. – CommonsWare May 01 '16 at 21:55
  • @YoniKeren: All that being said, I'm no expert on enough of Android's implementation to give you a complete explanation. However, this is not an Android-specific problem; this sort of thing has been an issue since the invention of threads. Hence, I tend not to worry about the details. :-) – CommonsWare May 01 '16 at 21:57