Android:将函数引用传递给AsyncTask

use*_*376 15 java android android-asynctask

我是android的新手,非常习惯于web开发.在javascript中,当您要执行异步任务时,您将函数作为参数传递(回调):

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

我想知道我们是否可以对android做同样的事情AsyncTask,传递方法的函数引用onPostExecute(),它将运行它.

有什么建议 ?

Xav*_*ler 46

是的,回调的概念在Java中也非常存在.在Java中,您可以像这样定义一个回调:

public interface TaskListener {
    public void onFinished(String result);
}
Run Code Online (Sandbox Code Playgroud)

人们通常会将这些类型的侦听器定义嵌套在AsyncTask这样的内容中:

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

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

    ...
}
Run Code Online (Sandbox Code Playgroud)

并且在回调中的完整实现AsyncTask将如下所示:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

onPostExecute()在后台任务完成后立即调用.你可以使用这样的整个事情:

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

task.execute();
Run Code Online (Sandbox Code Playgroud)

或者您可以TaskListener像这样完全单独定义:

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();
Run Code Online (Sandbox Code Playgroud)

或者您可以TaskListener像这样子类:

public class ExampleTaskListener implements TaskListener {

    @Override
    public void onFinished(String result) {

    }
}
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它:

ExampleTask task = new ExampleTask(new ExampleTaskListener());    
task.execute();
Run Code Online (Sandbox Code Playgroud)

你当然可以覆盖它的onPostExecute()方法AsyncTask,但不建议这样做,在大多数情况下实际上是非常糟糕的做法.例如,你可以这样做:

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

        // Your code goes here
    }
};
Run Code Online (Sandbox Code Playgroud)

这将与上面的实现一样使用单独的侦听器接口,但是有一些问题:

首先,你可以真正打破ExampleTask所有这些.这一切都归结为super.onPostExecute()上面的呼吁.如果您作为开发人员覆盖onPostExecute()如上所述并忘记包含超级调用或只是删除它,无论出于何种原因,将不再调用原始onPostExecute()方法ExampleTask.例如,由于实现TaskListener了对回调的调用,因此整个监听器实现将突然不再起作用onPostExecute().你也可以TaskListener通过不知不觉或无意中影响其状态来打破许多其他方式,ExampleTask因此它将不再起作用.

如果你看一下覆盖这样的方法时实际发生的事情,那么它会变得更加清晰.通过重写onPostExecute()你正在创建一个新的子类ExampleTask.这与完成同样的事情:

public class AnotherExampleTask extends ExampleTask {

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

        // Your code goes here
    }
}
Run Code Online (Sandbox Code Playgroud)

所有这些都隐藏在称为匿名类的语言功能之后.突然重写这样的方法似乎不那么干净和快速了吗?

总结一下:

  • 覆盖这样的方法实际上会创建一个新的子类.你不只是添加一个回调,你正在修改这个类的工作方式,并且可以在不知不觉中破坏这么多东西.
  • 像这样的调试错误不仅仅是a**的痛苦.因为没有明显的原因突然之间ExampleTask可能会抛出Exceptions或根本不再工作,因为你从未真正修改过它的代码.
  • 每个类都必须在适当和有意的地方提供监听器实现.当然,您可以稍后通过覆盖添加它们,onPostExecute()但这总是非常危险.即使是拥有13k声望的@flup也忘了super.onPostExecute()在他的回答中包含这个电话,想象一下其他一些不像经验丰富的开发者可能做的事情!
  • 一点抽象从不伤害任何人.编写特定的侦听器可能会稍微多一些代码,但它是一个更好的解决方案.代码将更清晰,更易读,更易于维护.使用像覆盖这样的快捷方式onPostExecute()基本上牺牲了代码质量,以方便一点.从来没有一个好主意,只会造成问题.