There are several ways. One common pattern is to use AsyncTask, but if you need to do several intermediate progress updates, AsyncTask is mostly suitable if each step is similar (such as downloading a file and updating the UI accordingly for every x kB downloaded). But for many many cases this will be fine.
If your task is more complex than that, constisting of several completely different steps, each one requiring the UI to be changed in some way, you may either divide your task into several AsyncTasks, or simply spawn a worker thread and use a Handler (created in the UI thread before the task starts) to post Runnables with whatever code you need to run in the UI thread.
final Handler handler = new Handler();
new Thread() {
public void run() {
// do time-consuming step 1
handler.post(new Runnable() {
public void run() {
// update UI after step 1
}
});
// do time-consuming step 2
handler.post(new Runnable() {
public void run() {
// update UI after step 2
}
});
// do time-consuming step 3
handler.post(new Runnable() {
public void run() {
// update UI after step 3
}
});
}
}.start();
Really, it's up to the level of control you need whether AsyncTask fits your needs or not.