Android:传递函数对 AsyncTask 的引用

2022-09-02 19:25:23

我是Android的新手,非常习惯于网络开发。在javascript中,当你想要执行异步任务时,你将一个函数作为参数(回调)传递:

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

我想知道我们是否可以用android做同样的事情,将函数引用传递给该方法,它将运行它。AsyncTaskonPostExecute()

有什么建议吗?


答案 1

是的,回调的概念在Java中也非常多。在Java中,你定义一个回调,如下所示:

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

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

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

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

    ...
}

回调的完整实现如下所示: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);
        }
    }
}

onPostExecute()在后台任务完成后立即调用。你可以像这样使用整个东西:

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

task.execute();

或者你可以完全单独地定义,如下所示: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();

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

public class ExampleTaskListener implements TaskListener {

    @Override
    public void onFinished(String result) {

    }
}

然后像这样使用它:

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

当然,您可以只覆盖 的方法,但不建议这样做,在大多数情况下,实际上非常糟糕的做法。例如,您可以执行以下操作:onPostExecute()AsyncTask

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

        // Your code goes here
    }
};

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

首先,你实际上可以打破一切。这一切都归结为上面的电话。如果您作为开发人员覆盖了上述内容,并且忘记包含超级调用或简单地将其删除,则无论出于何种原因,都不会再调用中的原始方法。例如,带有 的整个侦听器实现将突然不再工作,因为对回调的调用是在 中实现的。您还可以通过不知不觉或不知不觉地影响状态来以许多其他方式打破它,因此它将不再起作用。ExampleTasksuper.onPostExecute()onPostExecute()onPostExecute()ExampleTaskTaskListeneronPostExecute()TaskListenerExampleTask

如果你看看当你重写这样的方法时实际发生了什么,那么它就会变得更加清楚发生了什么。通过重写,您可以创建 一个新的子类。这与这样做完全相同:onPostExecute()ExampleTask

public class AnotherExampleTask extends ExampleTask {

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

        // Your code goes here
    }
}

所有这些都隐藏在一个名为匿名类的语言功能后面。突然覆盖这样的方法似乎不再那么干净和快速了,不是吗?

总结一下:

  • 重写这样的方法实际上会创建一个新的子类。你不只是在添加一个回调,你正在修改这个类的工作方式,并且可以在不知不觉中破坏很多东西。
  • 像这样的调试错误可能不仅仅是一个痛苦**。因为突然可以无缘无故地抛出或根本无法再工作,因为你从未真正修改过它的代码。ExampleTaskExceptions
  • 每个类都必须在适当和预期的位置提供侦听器实现。当然,您可以稍后通过覆盖来添加它们,但这总是非常危险的。即使@flup拥有13k的声誉也忘记了在回答中包含呼叫,想象一下其他一些不有经验的开发人员可能会做什么!onPostExecute()super.onPostExecute()
  • 一点点的抽象从来不会伤害任何人。编写特定的侦听器可能稍微多一些代码,但这是一个更好的解决方案。代码将更清晰,更具可读性,更易于维护。使用像覆盖这样的快捷方式基本上牺牲了代码质量,以获得一点便利。从长远来看,这绝不是一个好主意,只会造成问题。onPostExecute()

答案 2

在Java中,函数不像JavaScript那样是一等公民。AsyncTask 将回调作为类中的方法提供,您应该重写该方法。

请参阅使用 android 发出 HTTP 请求,了解 AsyncTask 的子类,该子类具有发出 Web 请求的 doInBackground 的实现。

如果要使用不同的回调执行多个 HTTP 请求,则可以覆盖 RequestTask 并使用不同的回调实现 onPostExecute。您可以使用匿名类来模拟 JavaScript 回调通常使用的闭包:

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

正如Xaver所示,您还可以为监听器创建一个完整的界面。这似乎只对我说有用,如果你希望实现几个默认的 onPostExecute 函数,并为特定调用选择其中一个默认实现。


推荐