Android Non-UI to UI Thread Communications (Part 1 of 5)
Android UI Thread and ANR
On the Android platform, applications operate, by default, on one thread. This thread is called the UI thread. It is often called that because this single thread displays the user interface and listens for events that occur when the user interacts with the app.
Developers quickly learn that if code running on that thread hogs that single thread and prevents user interaction (for more than 5 seconds), it causes Android to throw up the infamous Android Not Responsive (ANR) error.
Head to the Android Developers site for more information and background on this issue.
Processing Threads Unable To Update the UI
So how do you prevent ANR? Your application must create other threads and put long running work on non-UI threads. There are options on how to accomplish the creation of alternate threads. You can create and start your own java.lang.Thread. You can create and start an AsyncTask – Android’s own thread simplification mechanism. The non-UI thread then handles long running processing – like downloading a file – while the UI thread sticks to displaying the UI and reacting to user events. Life seems good again.
However, there is a problem in paradise. Unfortunately, the user interface (UI) cannot be updated by non-UI threads. For example, after successfully downloading a file, a separate (non-UI) thread can’t show an AlertDialog, update a TextView widget, otherwise make a UI change to indicate the file has been successfully downloaded. If you attempt to update the UI from a non-UI thread, the application will compile, but you get a CalledFromWrongThreadException thrown from the point your non-UI thread attempts to make the UI change. As the exception message will inform you, “Only the original thread that created a view hierarchy can touch its views.”
This seems like a catch-22 situation. If you put long running work on the UI thread, you can get ANR errors. If you have multiple threads and put long running work on the non-UI threads, those non-UI threads can’t inform the user of what is happening.
Many Non-UI to UI Thread Communication Options
Well, as it turns out, there are several ways to have non-UI threads request updates to the UI through the UI thread. In fact, in the next posts, I plan to show you five ways to have the non-UI thread send UI update requests to be executed on the UI thread.
- Use runOnUiThread( ) method call
- Use post( ) method call
- Use the Handler framework
- Use a Broadcasts and BroadcastReceiver (optionally with LocalBroadcastManager)
- Use an AsyncTask’s onProgressUpdate( ) method
As with all options, there are considerations when making a selection from this list. Much depends on your design decisions about how/where the non-UI thread is created and launched.
Note: Android usually provides many options when designing/coding your app. I have provided these 5 approaches, but I encourage other Android developers reading this post to provide additional options (or adjustments/combo solutions, etc.) Also, please provide your thoughts on pros/cons of each approach.
A Sample App
In order to demonstrate the options, I have put together a very small app (it was created with a minimum SDK version of 17 and a target SDK of 19). The app has one activity (one screen), and it consists of two button widgets and a TextView widget. One button on the UI starts a separate thread (non-UI) and the other button stops the separate thread’s execution.
As long as it is running, the non-UI thread started by the Start button generates a random number and then sleeps for a number of seconds. This simulates some long running work. However, the non-UI thread also wants to display each random number it generates back on the UI in the TextView widget. As you now know, this can only occur by communicating a UI update through the UI thread.
The code for each example will be available with each post – starting with this post. The critical pieces are in the ShowSomethingActivity class and its DoSomethingThread inner class.
In the ShowSomethingActivity, an OnClickListener is created to react to the Start and Stop buttons being pressed. When the Start button is pressed, it calls startGenerating() in the ShowSomethingActivity.
OnClickListener listener = new OnClickListener() { @Override public void onClick(View v) { if (v == startButton) { Log.v(TAG, "Start Service Button clicked"); ((ShowSomethingActivity) getActivity()).startGenerating(); } else { Log.v(TAG, "Stop Service Button clicked"); ((ShowSomethingActivity) getActivity()).stopGenerating(); } } };
The startGenerating method creates the DoSomethingThread (non-UI thread) and starts it running.
private void startGenerating() { randomWork = new DoSomethingThread(); randomWork.start(); }
The DoSomethingThread inner class extends java.lang.Thread. Its run( ) method creates the random number with the desire to update the UI’s TextView widget. The code to update the UI will happen in the publishProgress() method (see commented out code below). This method will be used to hold UI communications and updates involved in the 5 options.
public class DoSomethingThread extends Thread { private static final String TAG = "DoSomethingThread"; private static final int DELAY = 5000; // 5 seconds private static final int RANDOM_MULTIPLIER = 10; @Override public void run() { Log.v(TAG, "doing work in Random Number Thread"); while (true) { int randNum = (int) (Math.random() * RANDOM_MULTIPLIER); // need to publish the random number back on the UI at this point in the code through the publishProgress(randNum) call // publishProgress(randNum); try { Thread.sleep(DELAY); } catch (InterruptedException e) { Log.v(TAG, "Interrupting and stopping the Random Number Thread"); return; } } } }
When the Stop button is pressed, the OnClickListener (shown above) calls stopGenerating in the ShowSomethingActivity.
private void stopGenerating() { randomWork.interrupt(); //next line shows "Generator is off" in the TextView updateResults(getString(R.string.service_off)); }
This causes the DoSomethingThread to be interrupted and stop updating the UI.
Option 1- Use the runOnUiThread() method
Again, the first option I want to cover in communications between the non-UI and UI threads is using the runOnUiThread() method. You can find this method defined in Android’s Activity class.
Using, a non-UI thread communicates the desire to request work be run on the UI thread. This is accomplished under the covers by publishing the requested action to the event queue of the UI thread. When it can, the UI thread picks up the action message in the event queue and performs the UI change.
In the DoSomethingThread’s publishProgress() method, the activity’s runOnUiThread method is invoked. It is passed a Runnable object containing the UI-updating code. In this case, it contains the code to update the TextView widget through a call to updateResults(text).
private void publishProgress(int randNum) { Log.v(TAG, "reporting back from the Random Number Thread"); final String text = String.format(getString(R.string.service_msg), randNum); runOnUiThread(new Runnable() { @Override public void run() { updateResults(text); } }); }
Here is the updateResult(text) method in the ShowSomethingActivity.
public void updateResults(String results) { mainFrag.getResultsTextView().setText(results); }
So the non-UI thread doesn’t actually update the UI (the TextView widget). It sends a message via the runOnUiThread() call to the UI event queue. The runOnUiThread() method is a convenience for completing this messaging operation. The UI thread watches the event queue and eventually reacts to the request.
Considerations of Option 1 – runOnUiThread() method
What are the pros/cons when considering using the runOnUiThread. First off, note that this method is defined on the Activity. That means that the non-UI thread must have some knowledge or means of getting the Activity in order to take advantage of this method. In this example, that is rather easy since the non-UI thread class is defined as an inner class of the Activity. What happens if the non-UI thread class is defined elsewhere?
You may not be able to see it now, but at the conclusion of the review of the five options, you will see that this code requires more knowledge of Threads, Runnables and concurrency issues. For those comfortable with Java thread and concurrency API, this may not be an issue. Others may like a little simpler API. For the latter, stay tuned to the other options. However, the runOnUiThread() method call is actually a convenience method in that it hides many of the details associated with event queue messaging.
Finally, the runOnUiThread does come with one nice feature. If the runOnUiThread method is called from code running on the UI thread, it executes immediately and does not post a message into the event message queue. This convenience means you don’t have to check what thread you are running on when using this option.
ALL FIVE ARTICLES IN SERIES: Android Non-UI to UI THread Communication
This is a vital topic ‘Android Non-UI to UI Thread Communications (Part 1 of 5) | Intertech Blog’. Diamonds and Ui thread give us a blinding glimpse of the innovations that are coming our way—and that they are helping to create.
HELP PLEASE!
I have a very basic application that needs to have a timer that runs event when the app is closed, but update the UI when the app is open. I have an Activity with an inner class that runs the timer code on a separate thread, then updates the UI Thread with a call to runOnUiThread(). I pass a runnable into that which updates some TextViews.
It all works fine until you close the app (“calling onDestroy()”) then reopen it. The logs show that the runnable is still running, but the TextViews in the UI no longer get updated.
Any thoughts on what I might be missing? I have been trying to solve this problem for weeks now.
James – thanks for taking the time to read my post and submit a question.
A bit tough to diagnose problems via comment communications, but your issue likely stems from the fact that the separate task was probably given the reference to the old Activity’s TextView which is gone when the onDestroy gets invoked. The running thread needs the new UI’s TextView.
You need to have the UI call on and identify itself to something running in the background when it opens itself (or opens itself a new).
There are several possible alternative solutions to your need(s). One I think might be a good fit (without knowing all the details) is to use Android 5’s (Lollipop) new job scheduling API (https://developer.android.com/reference/android/app/job/JobScheduler.html). It was specifically built to execute tasks when you want them to execute (based on a timer) or it can also defer them until other events happen – like a power source is connected or wifi is available. When the Activity or UI is shown again, the UI can request an update from the background processes that may have been running all along. Its at that time that some of the communication features I discuss in this series of posts can help.
I would also encourage you to study Android’s Service class, TimerTask, and Alarm Manager to see that you have lots of options. As with most Android needs, you’ll usually find there are many options to choose from. Best of luck and thanks again for your question.
Thanks a lot for bringing up this post.Its a real important concept and helped me to have a better understanding 🙂