Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: pass function reference to AsyncTask

I'm new to android and very used to web developing. in javascript when you want to perform an asynchronous task you pass a function as an argument (a callback):

http.get('www.example.com' , function(response){
   //some code to handle response
});

I was wondering if we can do the same with android's AsyncTask , pass a function reference to the onPostExecute() method , and it will run it.

any suggestions ?

like image 970
user47376 Avatar asked Oct 05 '14 12:10

user47376


People also ask

How to call AsyncTask in android?

To start an AsyncTask the following snippet must be present in the MainActivity class : MyTask myTask = new MyTask(); myTask. execute(); In the above snippet we've used a sample classname that extends AsyncTask and execute method is used to start the background thread.

Why AsyncTask is deprecated?

This class was deprecated in API level 30.AsyncTask was intended to enable proper and easy use of the UI thread. However, the most common use case was for integrating into UI, and that would cause Context leaks, missed callbacks, or crashes on configuration changes.

How to cancel Async task android?

A task can be cancelled at any time by invoking cancel(boolean). Invoking this method will cause subsequent calls to isCancelled() to return true. After invoking this method, onCancelled(Object), instead of onPostExecute(Object) will be invoked after doInBackground(Object[]) returns.

What are the parameters in AsyncTask?

Google's Android Documentation Says that : An asynchronous task is defined by 3 generic types, called Params, Progress and Result, and 4 steps, called onPreExecute, doInBackground, onProgressUpdate and onPostExecute.


2 Answers

Yes the concept of callbacks also very much exists in Java. In Java you define a callback like this:

public interface TaskListener {
    public void onFinished(String result);
}

One would often nest these kind of listener definitions inside the AsyncTask like this:

public class ExampleTask extends AsyncTask<Void, Void, String> {

    public interface TaskListener {
        public void onFinished(String result);
    }

    ...
}

And a complete implementation of the callback in the AsyncTask would look like this:

public class ExampleTask extends AsyncTask<Void, Void, String> {

    public interface TaskListener {
        public void onFinished(String result);
    }

    // This is the reference to the associated listener
    private final TaskListener taskListener;

    public ExampleTask(TaskListener listener) {
        // The listener reference is passed in through the constructor
        this.taskListener = listener;
    }

    @Override
    protected String doInBackground(Void... params) {
        return doSomething();
    }

    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(result);

        // In onPostExecute we check if the listener is valid
        if(this.taskListener != null) {

            // And if it is we call the callback function on it.
            this.taskListener.onFinished(result);
        }
    }
}

onPostExecute() is called as soon as the background task finishes. You can use the whole thing like this:

ExampleTask task = new ExampleTask(new ExampleTask.TaskListener() {
    @Override
    public void onFinished(String result) {
        // Do Something after the task has finished
    }
});

task.execute();

Or you can define the TaskListener completely separately like this:

ExampleTask.TaskListener listener = new ExampleTask.TaskListener() {
    @Override
    public void onFinished(String result) {
        // Do Something after the task has finished
    }
};

ExampleTask task = new ExampleTask(listener);    
task.execute();

Or you can subclass TaskListener like this:

public class ExampleTaskListener implements TaskListener {

    @Override
    public void onFinished(String result) {

    }
}

And then use it like this:

ExampleTask task = new ExampleTask(new ExampleTaskListener());    
task.execute();

You can of course just override the onPostExecute() method of the AsyncTask, but that is not recommended and in most cases actually pretty bad practice. For example you could do this:

ExampleTask task = new ExampleTask() {
    @Override
    public void onPostExecute(String result) {
        super.onPostExecute(result);

        // Your code goes here
    }
};

This will work just as well as the implementation above with a separate listener interface, but there are a few problems with this:

First and foremost you can actually break the ExampleTask all together. It all comes down to the super.onPostExecute() call above. If you as a developer override onPostExecute() like above and forget to include the super call or simply delete it for whatever reason that the original onPostExecute() method in the ExampleTask will not be called anymore. For example the whole listener implementation with the TaskListener would suddenly not work anymore since the call to the callback is implemented in onPostExecute(). You can also break the TaskListener in many other ways by unknowingly or unwittingly influencing the state of the ExampleTask so it won't work anymore.

If you look at what's actually happening when you override a method like this than it becomes much more clear what's going on. By overriding onPostExecute() you are creating a new subclass of ExampleTask. It would be the exact same thing as doing this:

public class AnotherExampleTask extends ExampleTask {

    @Override
    public void onPostExecute(String result) {
        super.onPostExecute(result);

        // Your code goes here
    }
}

All this is just hidden behind a language feature called anonymous classes. Suddenly overriding a method like this doesn't seem so clean and quick anymore does it?

To summarise:

  • Overriding a method like this actually creates a new subclass. You are not just adding a callback, you are modifying how this class works and can unknowingly break oh so many things.
  • Debugging errors like this can be much more than just a pain in the a**. Because suddenly ExampleTask could throw Exceptions or simply not work anymore for no apparent reason, because you never actually modified its code.
  • Each class has to provide listener implementations at places where it is appropriate and intended. Sure you can just add them later on by overriding onPostExecute() but that is always very dangerous. Even @flup with his 13k reputation has forgotten to include the super.onPostExecute() call in his answer, imagine what some other not as experienced developer might do!
  • A little abstraction never hurt anybody. Writing specific listeners might be slightly more code, but it is a much better solution. The code will be cleaner, more readable and a lot more maintainable. Using shortcuts like overriding onPostExecute() essentially sacrifices code quality for a little bit convenience. That is never a good idea an will just cause problems in the long run.
like image 75
Xaver Kapeller Avatar answered Sep 29 '22 20:09

Xaver Kapeller


In Java, functions are less of a first class citizen than in JavaScript. The AsyncTask provides the callback as a method in the class, which you should override.

See Make an HTTP request with android for a subclass of AsyncTask with an implementation of the doInBackground which makes a web request.

If you want to do multiple HTTP requests with different callbacks, you can override RequestTask and implement onPostExecute with the different callback implementations. You can use an anonymous class to simulate the closure a JavaScript callback commonly uses:

new RequestTask(){
    @Override
    public void onPostExecute(String result) {
        // Implementation has read only access to 
        // final variables in calling scope. 
    }
}.execute("http://stackoverflow.com");

As Xaver shows, you can also create a full-blown interface for the listener. This seems only useful to me if you wish to implement a couple default onPostExecute functions and pick one of these default implementations for a particular call.

like image 39
flup Avatar answered Sep 29 '22 18:09

flup