Saturday 17 September 2011

Android: Modifying UI elements from inside a Thread

Just don't do what the title says :)

Modifying anything that extends the View class from inside a thread is bound to fail. This is very common in any widget/UI SDK and Google brilliantly provided us with a few options.

The first one - that I will not talk about much - is Activity.runOnUIThread. Just create a class extending Runnable and call this method. If the current thread is not the UI thread, the runnable will be posted to the main thread event queue and processed later. So DON'T abuse this call or you may overwhelm the main thread.

A much more flexible and interesting approach is to use AsyncTask, which allows you to neatly organize code that must run asynchronously an in the main (UI) thread.
Again you'll have to create your on class extending AsyncTaks with 3 generic types: Parameters, Progress and Result.

The most common use of your class would be:
  1. override the method doInBackground implementing all that stuff that has to happen in background;
  2. override onPostExecute implementing the code that MUST run in the UI thread
  3. Call execute(Params...) to start the task;

For instance:


public class CalculateMD5 extends AsyncThread<String, int, String>{
  @Override
  protected String doInBackground(String... filename){
    // running in background
    // calculate md5 of files
    ...
  }

  @Override
  protected onPostExecute(String result){
    // running in the UI thread
    myTextView.setText("MD5: "+result);
  }
}

 

if you wan to display the progress to the user, call publishProgress from doInBackground and override the method onProgressUpdated, which also runs in the UI thread.

public class CalculateMD5 extends AsyncThread<String, int, String>{
  @Override
  protected String doInBackground(String... filename){
    // running in background
    // calculate md5 of files
    publishProgress(number_of_bytes_processed/total_bytes);
    ...
  }

  @Override
  protected onPostExecute(String result){
    // running in the UI thread
    myTextView.setText("MD5: "+result);
  }

  @Override
  protected void onProgressUpdate(int... progress){
    //% processed ...
  }
}

 
and last but nor least, if you need to prepare things before the background processing starts, override onPreExecute, another method that runs in the UI thread.

You must start the task from the UI thread.
You should always check if the task is not cancelled in your doInBackground method. Use isCancelled for that.

Simple isn't it? So recaping what runs where:
  • UI Thread
    • onPreExecute
    • onProgressUpdate
    • onPostExecute
    • onCancelled
  • Backgroun thread
    • doInBackground
---
Programming tricks and tips for android developers