18

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 ?

user47376
  • 2,263
  • 3
  • 21
  • 26
  • Thats not how you do it ^_^ . You create a background thread using async task and your code aka httprequest goes in doInBackground() method and you define the returntype for it. After the background operation finishes onPostExecute() is called in which you write the code to handle the response as now you get to update views and all. There is a progressupdate method which is triggered when defined progress is reached. I will get the documentation link in a while – Rico Oct 05 '14 at 18:43
  • http://developer.android.com/reference/android/os/AsyncTask.html – Rico Oct 05 '14 at 18:44

4 Answers4

48

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.
Xaver Kapeller
  • 49,491
  • 11
  • 98
  • 86
  • This answer could be improved by using a generic `interface TaskListener` and `onFinished(T result)` – OneCricketeer Jun 09 '16 at 15:06
  • @cricket_007 I wouldn't call that an improvement. Each task should have its own strictly defined listener. Everything else tends to lead to bad design and implementation issues, even when ignoring type erasure. – Xaver Kapeller Jun 09 '16 at 15:08
  • It is more flexible than limiting yourself to only `String` responses, though. I've answered a couple question using the callback approach on AsyncTask. [For example](http://stackoverflow.com/a/36105515/2308683), you say that leads to bad design? – OneCricketeer Jun 09 '16 at 15:12
  • @cricket_007 If you expect any other response besides a String you would use that instead of String in the callback. The code in this answer is of course just an example. And yes, I think that leads to bad design. For example what happens if you execute two tasks, one returning ModelA and the other one returning ModelB. You can't implement AsyncResponse and AsyncResponse on the same class. And you also can't give the callbacks any meaningful name because the method name is fixed regardless of which task uses the callback. – Xaver Kapeller Jun 09 '16 at 15:16
  • @cricket_007 So overall you may save yourself two or three lines of code here and there, but you introduce other problems and limitations which you otherwise just wouldn't have to deal with. Writing specific callback interfaces, for each and every task is always better. – Xaver Kapeller Jun 09 '16 at 15:18
  • So, [Volley's](https://developer.android.com/training/volley/request-custom.html) `Request` and `Response` are no good? And I don't see why `AsyncResponse` and `AsyncResponse` could not both be implemented, is it not the same as an overloaded method? – OneCricketeer Jun 09 '16 at 15:32
  • @cricket_007 Well frameworks like Volley are very different. By definition they have to return many different types of objects. But that is not the case with an `AsyncTask` you wrote. You know exactly what type of object is returned and the listener for your `AsyncTask` should return exactly that type. Having a layer of ambiguity here with the addition of generics serves no purpose but making your code more complicated. – Xaver Kapeller Jun 09 '16 at 15:38
  • @cricket_007 And no you cannot implement `AsyncResponse` and `AsyncResponse` on the same class. Just try it. Define a generic interface and try to implement it on the same class like this: `public class Example implements AsyncResponse, AsyncResponse`. It won't and can't work. Java != C#. Type erasure prevents anything like this from working. Of course you can create fields for each of the callbacks or use them inline, but I think its very hard to argue that this makes code more readable. – Xaver Kapeller Jun 09 '16 at 15:39
2

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.

Community
  • 1
  • 1
flup
  • 26,937
  • 7
  • 52
  • 74
  • Overriding `onPostExecute()` like this is rarely a good idea. Classes have to provide listener functionality where it is appropriate and intended. What you are doing is dangerous in many ways, mostly because it could break `RequestTask` all together. See the bottom part of my answer. – Xaver Kapeller Nov 29 '14 at 10:08
  • @XaverKapeller Actually, the super class's `onPostExecute` doesn't do a thing and calling it is quite pointless. The `onPostExecute` *is* the callback, not some internal implementation method you can sneakily hook into. Its purpose is notification of the result to the original creator of the task. – flup Nov 29 '14 at 22:31
  • Well yes that is true if you are implementing a new `AsyncTask`, but if you already subclassed `AsyncTask` like the `RequestTask` in your answer suggests than you could break any functionality that was already implemented in the `RequestTask`. That is what I meant in my previous comment. – Xaver Kapeller Nov 29 '14 at 22:40
  • But neither class does a thing in the `onPostExecute` callback method, which is the way it should be, since it'll be called on the UI thread. The only reason of existence for the RequestTask is that it implements the HTTP stuff for you. The callback method is still to be implemented depending what you want to do with the results. – flup Nov 30 '14 at 21:33
1

in Kotlin

Firstly, create class AsyncTaskHelper as below.

class AsyncTaskHelper() : AsyncTask<Callable<Void>, Void, Boolean>() {

    var taskListener: AsyncListener? = null

    override fun doInBackground(vararg params: Callable<Void>?): Boolean {
        params.forEach {
            it?.call()
        }
        return true
    }

    override fun onPreExecute() {
        super.onPreExecute()
    }

    override fun onPostExecute(result: Boolean?) {
        super.onPostExecute(result)
        taskListener?.onFinished(result as Any)
    }

}

interface AsyncListener {
    fun onFinished(obj: Any)
}

the code below you can use when you want to use async task.

    AsyncTaskHelper().let {
        it.execute(Callable<Void> {
                //this area is the process do you want to do it in background
                // dosomething()
                }
            }
            null
        })
        it.taskListener = object : AsyncListener{
            override fun onFinished(obj: Any) {
                // this area is for the process will want do after finish dosomething() from Callable<Void> callback


            }

        }

From the code above. if you want to separate your process to several task. you can do as this code below.

AsyncTaskHelper().let {
            it.execute(Callable<Void> {
                // task 1 do in background
                null
            },Callable<Void>{
                // task 2 do in background
                null
            },Callable<Void>{
                // task 3 do in background
                null
            })

            it.taskListener = object : AsyncListener {
                override fun onFinished(obj: Any) {
                    // when task1,task2,task3 has been finished . it will do in this area
                }

            }
        }
Sup.Ia
  • 277
  • 2
  • 8
0

I also want to contribute to Sup.la's Kotlin solution by making it a little more generic in case you're only interested in .execute()ing a function asnychronously and .get()ting a result from an AsyncTask. I split up the explanation in order to make it better understandable.

If you only want to copy paste, just skip to Step2 :)

Note: Keep in mind that AsyncTask is deprecated now. However, I think it'll stay around for while due to compatibility and stuff.


Step 1

This is how the generic class can look like:

import android.os.AsyncTask

@Suppress("DEPRECATION") // As mentioned, AsyncTask is deprecated now
class GenericAsyncTask() : AsyncTask<Callable<Any>, Void, Any?>() {
    override fun doInBackground(vararg params: Callable<Any>): Any? {
        // params receives vararg, meaning its an Array.
        // In this example we only want pass one function to easen up the example (-> [0]).
        return params[0]?.call()
    }
}

As can be seen, the AsyncTask now takes any function passed to it (Callable<Any>), whereby Any and null can be the result of the function. Thus, here nobody cares about the types the AsyncTask has to handle or return or how the passed function looks like.

You can now use it like this:

// In this example lets assume we have a method which returns a list of Strings
var res: List<String>? = GenericAsyncTask().let {
        it.execute(
            Callable<Any> { your_random_function() }
        ).get() as? List<String>?
    }

As you can see, we handle the Any dilemma just by casting it. This works since all Types are deriving from Any except null. To handle the latter, we're doing a safe cast using as?. Thus, also null as return-type is handled.


Step2

However, ultimatively we can even make this more generic:

// The same example result as shown above
// The casting will be inferred directly from your_random_function() return-type
val res: List<String>? = GenericAsyncTask().async(::your_random_function)

// For the sake of completeness, this is valid as well
val res: List<String>? = GenericAsyncTask().async<List<String>?> { your_random_function() }


class GenericAsyncTask() : AsyncTask<Callable<Any>, Void, Any?>() {

    // This function takes any method you provide and even infers the result-type, thus, spares you the casting
    public fun<T> async(method: () -> T): T? {
        return this.let {
            it.execute(Callable<Any> { method() }).get() as? T?
        }
    }

    override fun doInBackground(vararg params: Callable<Any>): Any? { ... }
}
Markus
  • 2,265
  • 5
  • 28
  • 54