× {{alert.msg}} Never ask again
Get notified about new tutorials RECEIVE NEW TUTORIALS

Android: pass function reference to AsyncTask

Xaver Kapeller
Mar 13, 2015
<p>Yes the concept of callbacks also very much exists in Java. In Java you define a callback like this:</p> <pre><code>public interface TaskListener { public void onFinished(String result); } </code></pre> <p>One would often nest these kind of listener definitions inside the <code>AsyncTask</code> like this:</p> <pre><code>public class ExampleTask extends AsyncTask&lt;Void, Void, String&gt; { public interface TaskListener { public void onFinished(String result); } ... } </code></pre> <p>And a complete implementation of the callback in the <code>AsyncTask</code> would look like this:</p> <pre><code>public class ExampleTask extends AsyncTask&lt;Void, Void, String&gt; { 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); } } } </code></pre> <p><code>onPostExecute()</code> is called as soon as the background task finishes. You can use the whole thing like this:</p> <pre><code>ExampleTask task = new ExampleTask(new ExampleTask.TaskListener() { @Override public void onFinished(String result) { // Do Something after the task has finished } }); task.execute(); </code></pre> <p>Or you can define the <code>TaskListener</code> completely separately like this:</p> <pre><code>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(); </code></pre> <p>Or you can subclass <code>TaskListener</code> like this:</p> <pre><code>public class ExampleTaskListener implements TaskListener { @Override public void onFinished(String result) { } } </code></pre> <p>And then use it like this:</p> <pre><code>ExampleTask task = new ExampleTask(new ExampleTaskListener()); task.execute(); </code></pre> <hr> <p>You can of course just override the <code>onPostExecute()</code> method of the <code>AsyncTask</code>, but that is not recommended and in most cases actually pretty bad practice. For example you could do this:</p> <pre><code>ExampleTask task = new ExampleTask() { @Override public void onPostExecute(String result) { super.onPostExecute(result); // Your code goes here } }; </code></pre> <p>This will work just as well as the implementation above with a separate listener interface, but there are a few problems with this:</p> <p>First and foremost you can actually break the <code>ExampleTask</code> all together. It all comes down to the <code>super.onPostExecute()</code> call above. If you as a developer override <code>onPostExecute()</code> like above and forget to include the super call or simply delete it for whatever reason that the original <code>onPostExecute()</code> method in the <code>ExampleTask</code> will not be called anymore. For example the whole listener implementation with the <code>TaskListener</code> would suddenly not work anymore since the call to the callback is implemented in <code>onPostExecute()</code>. You can also break the <code>TaskListener</code> in many other ways by unknowingly or unwittingly influencing the state of the <code>ExampleTask</code> so it won't work anymore.</p> <p>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 <code>onPostExecute()</code> you are creating a new subclass of <code>ExampleTask</code>. It would be the exact same thing as doing this:</p> <pre><code>public class AnotherExampleTask extends ExampleTask { @Override public void onPostExecute(String result) { super.onPostExecute(result); // Your code goes here } } </code></pre> <p>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?</p> <p>To summarise: </p> <ul> <li>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.</li> <li>Debugging errors like this can be much more than just a pain in the a**. Because suddenly <code>ExampleTask</code> could throw <code>Exceptions</code> or simply not work anymore for no apparent reason, because you never actually modified its code.</li> <li>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 <code>onPostExecute()</code> but that is always very dangerous. Even @flup with his 13k reputation has forgotten to include the <code>super.onPostExecute()</code> call in his answer, imagine what some other not as experienced developer might do! </li> <li>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 <code>onPostExecute()</code> essentially sacrifices code quality for a little bit convenience. That is never a good idea an will just cause problems in the long run.</li> </ul> <p>This tip was originally posted on <a href="http://stackoverflow.com/questions/26202568/Android:%20pass%20function%20reference%20to%20AsyncTask/26202602">Stack Overflow</a>.</p>
comments powered by Disqus