1

I'm writing an Activity which fetches some XML from the web via HTTP and then parses it into a DOM. It then pulls some required data from the DOM.

As you can imagine, this takes a few moments, so I put that code into it's own thread and then tried to set up a ProgressDialog to display while the user is waiting for that to complete.

The problem is that the ProgressDialog doesn't display at all. If I remove the call to dismiss() then it displays after the work is done and, obviously, just sits there...

Here is my code:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    progressDialog = ProgressDialog.show(this, "", "Loading...", true);
    Thread downloadThread = new Thread(new DownloadVerseThread());
    downloadThread.start();
    setContentView(R.layout.main);
    TextView verseView = (TextView) findViewById(R.id.displayFighterVerse);
    TextView dateView = (TextView) findViewById(R.id.displayDateRange);
    TextView referenceView = (TextView) findViewById(R.id.displayReference);
    try {
        downloadThread.join();
    } catch (InterruptedException e) {
        Log.e(TAG, "Attempted to set the UI values before we were ready.");
        return;
    }
    Log.d(TAG, "Date: " + date);
    Log.d(TAG, "Reference: " + reference);
    Log.d(TAG, "Scripture: " + scripture);
    progressDialog.dismiss();
    dateView.setText(date);
    referenceView.setText(reference);
    verseView.setText(scripture);
}



private class DownloadVerseThread implements Runnable {


    @Override
    public void run() {

        try {
            FighterVerseDownloader downloader = new FighterVerseDownloader();
            date = downloader.getDateRange();
            reference = downloader.getReference();
            scripture = downloader.getScripture();
        } catch (FighterVersesException e) {
            Log.e("FighterVerses", "Failed to get the Fighter Verse", e);
        }           
    }

}

The constructor of the FighterVersesDownloader is where all the work in terms of HTTP and DOM is done.

Any ideas on what I'm missing? I've seen a few similar threads on this using AsyncTask, but I wanted to use good old fashioned Threads directly and the solutions suggested don't apply.

Paul Hunnisett
  • 898
  • 1
  • 17
  • 36

2 Answers2

3

I would recommend using AsyncTask for that kind of things.

Macarse
  • 91,829
  • 44
  • 175
  • 230
  • I'dbe interested to know what the community thinks the advantages of an AsyncTask are over "old school" threading... – Paul Hunnisett Aug 13 '10 at 15:33
  • There are a lot of questions about it. Just to link one: http://stackoverflow.com/questions/2523459/handler-vs-asynctask – Macarse Aug 13 '10 at 15:39
  • The Async Task removes all the need for a handler you can just do a quick and dirty anonymous Async task that calls the work code in the background method and calls the UI updates after it is finished in the onPostExecute function without you worrying about which thread is doing what. – Janusz Aug 13 '10 at 16:08
  • There's also this http://brainflush.wordpress.com/2009/11/16/introducing-droid-fu-for-android-betteractivity-betterservice-and-betterasynctask/ – Falmarri Aug 13 '10 at 20:34
3

I think the problem may be related to how you're doing the threading. When you call downloadThread.join(), you're basically taking that expensive code and slapping it right in the middle of the method, blocking the UI thread in the process.

You need to use a callback method to handle the UI changes when the thread is finished. Your code should look something like this:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    progressDialog = ProgressDialog.show(this, "", "Loading...", true);
    Thread downloadThread = new Thread(new DownloadVerseThread());
    Handler downloadHandler = new Handler();
    Runnable downloadCallback = new Runnable (){
        public void run(){
                setContentView(R.layout.main);
            TextView verseView = (TextView) findViewById(R.id.displayFighterVerse);
            TextView dateView = (TextView) findViewById(R.id.displayDateRange);
            TextView referenceView = (TextView) findViewById(R.id.displayReference);
            Log.d(TAG, "Date: " + date);
            Log.d(TAG, "Reference: " + reference);
            Log.d(TAG, "Scripture: " + scripture);
            progressDialog.dismiss();
            dateView.setText(date);
            referenceView.setText(reference);
            verseView.setText(scripture);
        }
    };
    downloadThread.start();
}

private class DownloadVerseThread implements Runnable {
    @Override
    public void run() {

        try {
            FighterVerseDownloader downloader = new FighterVerseDownloader();
            date = downloader.getDateRange();
            reference = downloader.getReference();
            scripture = downloader.getScripture();
            downloadHandler.post(downloadCallback);
        } catch (FighterVersesException e) {
            Log.e("FighterVerses", "Failed to get the Fighter Verse", e);
        }           
    }
}

This will keep the downloading in the background thread until it is finished, rather than almost immediately shunting it back into the UI thread as it was previously.

EDIT: Here's where I learned this.

iandisme
  • 6,346
  • 6
  • 44
  • 63