我是Android开发新手,之前主要从事Web开发工作。在JavaScript中,当你想要执行一个异步任务时,你需要将一个函数作为参数传递(即回调函数):
http.get('www.example.com' , function(response){
//some code to handle response
});
我在想我们是否可以在Android的AsyncTask
中做同样的事情,将函数引用传递给onPostExecute()
方法,并使其运行。
有什么建议吗?
我是Android开发新手,之前主要从事Web开发工作。在JavaScript中,当你想要执行一个异步任务时,你需要将一个函数作为参数传递(即回调函数):
http.get('www.example.com' , function(response){
//some code to handle response
});
我在想我们是否可以在Android的AsyncTask
中做同样的事情,将函数引用传递给onPostExecute()
方法,并使其运行。
有什么建议吗?
是的,在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()
会在后台任务完成后立即调用。您可以像这样使用整个内容:
onPostExecute()
方法会在后台任务完成后立即被调用。
您可以按照以下方式使用整个内容:
onPostExecute()
在后台任务完成后立即被调用,您可以在此处执行任何必要的UI更新。
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();
当然,您可以覆盖 AsyncTask
的 onPostExecute()
方法,但这并不被推荐,在大多数情况下实际上是相当糟糕的做法。例如,您可以尝试以下方法:
ExampleTask task = new ExampleTask() {
@Override
public void onPostExecute(String result) {
super.onPostExecute(result);
// Your code goes here
}
};
这种实现方式和使用单独的监听器接口一样有效,但是存在几个问题:
首先,你可能会完全破坏ExampleTask
。这归结于上面的super.onPostExecute()
调用。如果开发人员像上面那样重写onPostExecute()
,但忘记包含super调用或者出于某种原因将其删除,则ExampleTask
中的原始onPostExecute()
方法将不再被调用。例如,整个带有TaskListener
的监听器实现将突然不起作用,因为回调调用是在onPostExecute()
中实现的。您还可以通过无意中影响ExampleTask
的状态以使TaskListener
失效。
如果您看一下重写此类方法时实际发生的事情,那么就会更清楚发生了什么。通过重写onPostExecute()
,您正在创建ExampleTask
的新子类。这将完全相同,就像这样做一样:
public class AnotherExampleTask extends ExampleTask {
@Override
public void onPostExecute(String result) {
super.onPostExecute(result);
// Your code goes here
}
}
所有这些都只是隐藏在语言特性“匿名类”背后。突然间像这样覆盖一个方法就不再那么干净和快速了,是吧?
总结:
ExampleTask
可以抛出异常或者根本停止工作,而没有任何明显的原因,因为你从未实际修改过它的代码。onPostExecute()
来添加它们,但这总是非常危险的。即使@flup拥有13k的声望,他也曾经在答案中忘记包含super.onPostExecute()
调用,想象一下其他不那么有经验的开发人员会做什么!onPostExecute()
这样的快捷方式实质上牺牲了代码质量以换取一点便利。这永远不是一个好主意,只会在长期运行中引起问题。interface TaskListener<T>
和 onFinished(T result)
来改进。 - OneCricketeerRequest<T>
和 Response<T>
不好用吗?而且我不明白为什么不能同时实现 AsyncResponse<ModelA>
和 AsyncResponse<ModelB>
,这难道不就是重载方法吗? - OneCricketeerAsyncResponse<ModelA>
和 AsyncResponse<ModelB>
。试试定义一个通用接口并尝试像这样在同一个类上实现它:public class Example implements AsyncResponse<ModelA>, AsyncResponse<ModelB>
。这是行不通的。Java != C#。类型擦除阻止了任何类似这样的操作。当然,你可以为每个回调创建字段或者内联使用它们,但我认为这很难证明这会使代码更易读。 - Xaver Kapellernew RequestTask(){
@Override
public void onPostExecute(String result) {
// Implementation has read only access to
// final variables in calling scope.
}
}.execute("http://stackoverflow.com");
onPostExecute()
通常不是一个好主意。类必须在适当和预期的情况下提供监听器功能。你所做的事情在很多方面都是危险的,主要是因为它可能会完全破坏RequestTask
。请参见我的答案底部。 - Xaver KapelleronPostExecute
方法并没有做任何事情,调用它是相当无意义的。onPostExecute
就是回调函数,而不是你可以偷偷钩入的内部实现方法。它的目的是通知任务的原始创建者结果。 - flup在Kotlin中
首先,按照以下方式创建AsyncTaskHelper类。
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)
}
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
}
}
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.la的Kotlin解决方案做出贡献,使其更加通用,以便您只对异步执行函数.execute()
和从AsyncTask
获取结果.get()
感兴趣。
我将解释分成几个部分,以使其更易于理解。
如果您只想复制粘贴,请直接跳到第二步 :)
注意:请记住,AsyncTask
现在已被弃用。但是,由于兼容性等原因,我认为它会继续存在一段时间。
步骤1
这是泛型类的样子:
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()
}
}
如你所见,AsyncTask
现在接受任何传递给它的函数(Callable<Any>
),其中Any
和null
都可以是函数的结果。因此,在这里没有人关心AsyncTask
需要处理或返回什么类型的值,或者传递的函数是什么样子。
你可以像这样使用它:
// 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>?
}
正如您所看到的,我们只需进行强制转换即可处理Any
的困境。这是有效的,因为除了null
之外,所有类型都派生自Any
。为了处理后者,我们使用as?
进行安全转换。因此,返回类型为null
也被处理。
步骤2
然而,最终我们甚至可以使其更加通用:
// 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? { ... }
}