5

I have a small Android application in which I need to do some FTP stuff every couple of seconds. After learning the hard way that running network stuff on the UI thread is something Android does not really like, I've come to this solution:

// This class gets declared inside my Activity
private class CheckFtpTask extends AsyncTask<Void, Void, Void> {
    protected Void doInBackground(Void... dummy) {
        Thread.currentThread().setName("CheckFtpTask");
        // Here I'll do the FTP stuff       
        ftpStuff();
        return null;
    }
}

// Member variables inside my activity
private Handler checkFtpHandler;
private Runnable checkFtpRunnable;

// I set up the task later in some of my Activitiy's method:
checkFtpHandler = new Handler();
checkFtpRunnable = new Runnable() {
    @Override
    public void run() {
        new CheckFtpTask().execute((Void[])null);
        checkFtpHandler.postDelayed(checkFtpRunnable, 5000);
    }
};
checkFtpRunnable.run();

Is this good practice to perform a recurring task that cannot run on the UI thread directly? Furthermore, instead of creating a new AsyncTask object all the time by calling

new CheckFtpTask().execute((Void[])null);

would it be an option to create the CheckFtpTask object once and then reuse it? Or will that give me side effects?

Thanks in advance, Jens.

Jens
  • 93
  • 9

3 Answers3

1

would it be an option to create the CheckFtpTask object once and then reuse it? Or will that give me side effects?

No, there will be side-effects. Quoting the docs Threading Rules:

The task can be executed only once (an exception will be thrown if a second execution is attempted.)

You will just need to create a separate instance of the task each time you want to run it.

And I'm not sure why you need the Runnable or Handler. AsyncTask has methods that run on the UI Thread (all but doInBackground(), actually) if you need to update the UI.

Check this answer if you need a callback to update the UI when the task has finished.

Community
  • 1
  • 1
codeMagic
  • 44,549
  • 13
  • 77
  • 93
  • 1
    Why do I need the `Runnable` and the `Handler`? Good question. I thought it would be a good way to make the task recurring using the `postDelayed`. Would it be better to call `Thread.sleep()` inside a loop in the `doInBackground()`? Anyway, just as I'm writing this I found an error: It does not make sense to call the `postDelayed()` in the `Runnable`. If a task takes more than 5 seconds, a new one will be created before the old one finishes. This is not what I want so I moved the `checkFtpHandler.postDelayed(checkFtpRunnable, 5000);` into the `onPostExecute()`of the `AsyncTask`. – Jens Sep 13 '13 at 23:19
  • The whole task (as by now!) does _nothing_ on the UI. I've simply chosen the combination of `AsyncTask` plus `Runnable`/`Handler` because I need a _recurring_ task that is _not on the UI thread_ with the (later) option to do some UI stuff. Does that sound reasonable? – Jens Sep 13 '13 at 23:28
  • You can call `Thread.sleep()` in the `doInBackground()`, say in a `for loop` or `while loop` to have it recurring. – codeMagic Sep 13 '13 at 23:31
  • But when I have an "inifinte loop" with `Thread.sleep()` in it I loose the ability to do UI stuff, right? – Jens Sep 13 '13 at 23:36
  • No, not at all. You can update the `UI` on any of the other `AsyncTask` methods like `onProgressUpdate()` or `onPostExecute()`. `doInBackground()` is the only `AsyncTask` method which doesn't run on the `UI` – codeMagic Sep 13 '13 at 23:38
  • I don't get this, I guess... When running in an infinte loop, neither `onPostExecute()`nor `onProgressUpdate()` do get called. So how can I update the UI? – Jens Sep 13 '13 at 23:41
  • Which part don't you get and I will try to help? – codeMagic Sep 13 '13 at 23:43
  • `onPostExecute()` won't until you return a value from `doInBackground()`. But `onProgressUpdate()` will anytime you call `publishProgress()` from `doInBackground()` – codeMagic Sep 13 '13 at 23:45
  • You said that I can update the UI in the other methods. But when I have an infinite loop in `doInBackground()`, the other methods (`onProgressUpdate()` and `onPostExecute()`) are never called, are they? Or is `onProgressUpdate()` in my example just not called because I created the class as `AsyncTask`? – Jens Sep 13 '13 at 23:48
  • Right, you have to give the second `param` the datatype that `onProgressUpdate()` will take and it will only be called when you call `publishProgress()` – codeMagic Sep 13 '13 at 23:49
  • Got it now. For some weird reason I thought that `onProgressUpdate()` should get called automatically, but of course you have to call `publishProgress()` for this to happen. :-) – Jens Sep 13 '13 at 23:53
  • So, from a resource-efficient approach, would you say that using only the `AsyncTask` in an infinite loop (with `Thread.sleep()`) is superior to my first version with the `Handler`/`Runnable`? – Jens Sep 13 '13 at 23:55
  • I don't know about "superior" but with `AsyncTask`, the others aren't generally needed or wanted – codeMagic Sep 13 '13 at 23:59
0

You should create a new Async task for every call.

See the Android Documentation: AsyncTask. According to this documentation:

The task can be executed only once (an exception will be thrown if a second execution is attempted.)

Specifically check out the threading rules section. There is a similar answer here, https://stackoverflow.com/a/18547396/2728623

Community
  • 1
  • 1
pfairbairn
  • 451
  • 2
  • 8
0

Java ThreadPools and the ExecutorFramework allows you to execute threads as needed and reduces thread creation overhead. Check out the singleThreadExecutor. The thread pool usage is pretty easy too!

anguyen
  • 467
  • 4
  • 17