If you found this post via a Google search, chances are that your Android app just crashed after you added an innocent-looking timer. You probably got this error:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
You may have also found people saying that you can’t use a timer to update the GUI. Seems odd, right? Why would Google make a timer and then not let you use it for hardly anything?
Well, those people are wrong. While you can’t update the GUI directly from your timer, you can do so with a few little tweaks.
The error above happens because Android doesn’t want other threads, such as your timer, messing with its GUI. So, what you need to do is politely ask Android to make the update for you. To do that, you need to add a handler and a runnable. Android will monitor the handler for a signal from your timer. When it gets the signal, it will run the code in the runnable. It sounds complicated, but it amounts to only a few lines of code.
First, move the code that updates the GUI out of your timer and into a runnable. A runnable is a mechanism where one thread can tell another thread to run a block of code. In this example, there is one line of code which sets the TextView tv to the value of the global variable i:
final Runnable myRunnable = new Runnable() {
public void run() {
tv.setText(String.valueOf(i));
}
};
Creating a handler is very simple:
final Handler myHandler = new Handler();
And in the timer, we pass the runnable to the handler:
private void UpdateGUI() {
i++;
//tv.setText(String.valueOf(i)); //This causes a runtime error.
myHandler.post(myRunnable);
}
As an example, I took the Android “Hello World” app, and modified it into a counter that updates once per second:
public class HelloActivity extends Activity {
int i=0;
TextView tv;
final Handler myHandler = new Handler();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tv=(TextView) findViewById(R.id.tv1);
Timer myTimer = new Timer();
myTimer.schedule(new TimerTask() {
@Override
public void run() {UpdateGUI();}
}, 0, 1000);
}
private void UpdateGUI() {
i++;
//tv.setText(String.valueOf(i));
myHandler.post(myRunnable);
}
final Runnable myRunnable = new Runnable() {
public void run() {
tv.setText(String.valueOf(i));
}
};
}
Why does Android do this, while other operating systems do not? The fact is that none of the major operating systems have thread-safe GUI frameworks, and updating the GUI from a thread is a bad idea that can lead to difficult-to-trace bugs.
For example, suppose you have a thread that updates a text control on a window, and the user closes the window. Your thread may still be running, and cause an error when it tries to update the text control that no longer exists. It is possible to code around this problem, but it requires that your thread be constantly checking to see if the controls it wants to update still exist.
So, Android eliminates a whole class of bugs with its thread/GUI policy. And you will likely see more of this in the future. For example, the Xojo language now follows the same policy.
I use the code above in my BlubberPatrol weight-loss app. So, you can install that from the Google Play Store and see how well it works.