How to Update the Android GUI From a Timer

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));
      }
   };
}