从Android中的AsyncTask返回一个值[重复]
Posted
技术标签:
【中文标题】从Android中的AsyncTask返回一个值[重复]【英文标题】:Return a value from AsyncTask in Android [duplicate] 【发布时间】:2012-03-16 12:38:44 【问题描述】:一个简单的问题:是否可以在AsyncTask
中返回一个值?
//AsyncTask is a member class
private class MyTask extends AsyncTask<Void, Void, Void>
protected Void doInBackground(Void... params)
//do stuff
return null;
@Override
protected void onPostExecute(Void result)
//do stuff
//how to return a value to the calling method?
然后在我的Activity
/Fragment
:
// The task is started from activity
myTask.execute()
// something like this?
myvalue = myTask.getvalue()
编辑: 很久以前有人问过我对Java不熟悉的地方,现在我对它更好,我将做一个简短的总结:
异步任务的重点是任务是asynchronous
,意思是你在任务上调用execute()
后,任务开始在自己的线程上运行。从 asynctask 返回一个值是没有意义的,因为原来的调用线程已经在做其他事情(因此任务是异步的)。
想想时间: 在某个时间点,您启动了一项将与主线程并行运行的任务。当并行运行的任务完成时,主线程上的时间也过去了。并行任务无法及时返回给主线程返回值。
我来自 C 语言,所以我对此知之甚少。但似乎很多人都有同样的问题,所以我想我会澄清一下。
【问题讨论】:
关于您的编辑;一组密切相关的基于线程的对象确实具有返回某些东西的概念;线程池和期货。当您将任务提交到线程池时,您将获得未来。在未来某个时间(希望在 runnable 完成之后),您向未来询问它的返回值。如果runnable已经完成,你会立即得到返回值,如果没有,线程会一直等到它完成。我经常用它把东西分成几个部分,同时运行它们,然后得到结果(通常只在几行之后) @RichardTingle 你能给我一些关于你在说什么的链接......示例代码会更好。在此先感谢您,请不要忘记在您的评论中提及我。 我可以得到像监听器这样的结果吗? 是的,但要小心不要泄露它,使用WeakReference
或类似的东西
【参考方案1】:
您需要使用"protocols"来委托或提供数据给AsynTask
。
委托和数据源
委托是一个对象,当另一个对象遇到程序中的事件时,它代表另一个对象或与之协调。 (Apple definition)
协议是定义一些方法来委托某些行为的接口。
DELEGATE:在后台线程中从对象中捕获事件
异步任务:
public final class TaskWithDelegate extends AsyncTask<..., ..., ...>
//declare a delegate with type of protocol declared in this task
private TaskDelegate delegate;
//here is the task protocol to can delegate on other object
public interface TaskDelegate
//define you method headers to override
void onTaskEndWithResult(int success);
void onTaskFinishGettingData(Data result);
@Override
protected Integer doInBackground(Object... params)
//do something in background and get result
if (delegate != null)
//return result to activity
delegate.onTaskFinishGettingData(result);
@Override
protected void onPostExecute(Integer result)
if (delegate != null)
//return success or fail to activity
delegate.onTaskEndWithResult(result);
活动:
public class DelegateActivity extends Activity implements TaskDelegate
void callTask ()
TaskWithDelegate task = new TaskWithDelegate;
//set the delegate of the task as this activity
task.setDelegate(this);
//handle success or fail to show an alert...
@Override
void onTaskEndWithResult(int success)
//handle data to show them in activity...
@Override
void onTaskFinishGettingData(Data result)
编辑:如果您在 doInBackground 中调用委托,并且委托尝试编辑某些视图,则会崩溃,因为视图只能由主线程操作。
// this refers to Activity
this.runOnUiThread(new Runnable()
@Override
public void run()
// Here you can edit views when task notify some changes from background thread
textView.setText(someValue);
);
额外
DATASOURCE:在后台线程中向对象提供数据
异步任务:
public final class TaskWithDataSource extends AsyncTask<..., ..., ...>
//declare a datasource with type of protocol declared in this task
private TaskDataSource dataSource;
private Object data;
//here is the task protocol to can provide data from other object
public interface TaskDataSource
//define you method headers to override
int indexOfObject(Object object);
Object objectAtIndex(int index);
@Override
protected void onPreExecute(Integer result)
if (dataSource != null)
//ask for some data
this.data = dataSource.objectAtIndex(0);
@Override
protected Integer doInBackground(Object... params)
//do something in background and get result
int index;
if (dataSource != null)
//ask for something more
index = dataSource.indexOfObject(this.data);
活动:
public class DataSourceActivity extends Activity implements TaskDataSource
void callTask ()
TaskWithDataSource task = new TaskWithDataSource;
//set the datasource of the task as this activity
task.setDataSource(this);
//send some data to the async task when it is needed...
@Override
Object objectAtIndex(int index)
return new Data(index);
//send more information...
@Override
int indexOfObject(Object object)
return new object.getIndex();
【讨论】:
也就是说,只要使用一个接口,需要通知的人就注册为监听器。 是的,简而言之就是这样。 我发现这是最干净的答案。 Task Class 不应该知道 Activity 的实现细节。另一个重要的好处是代码重用,你可以为任何其他活动/片段调用这个类【参考方案2】:代码示例:Activity使用AsyncTask在后台线程中获取值,然后AsyncTask通过调用processValue将结果返回给Activity:
public class MyClass extends Activity
private void getValue()
new MyTask().execute();
void processValue(Value myValue)
//handle value
//Update GUI, show toast, etc..
private class MyTask extends AsyncTask<Void, Void, Value>
@Override
protected Value doInBackground(Void... params)
//do stuff and return the value you want
return Value;
@Override
protected void onPostExecute(Value result)
// Call activity method with results
processValue(result);
【讨论】:
【参考方案3】:对于您从 AsyncTask 获取结果,它需要在 super.onPostExcecute(result)
之后执行;
因为这意味着后台和AsyncTask也已经完成了。
例如:
... into your async task
@Override
protected void onPostExecute(MyBeanResult result)
if (dialog.isShowing())
dialog.dismiss();
if (mWakeLock.isHeld())
mWakeLock.release();
super.onPostExecute(result);
MyClassName.this.checkResponseForIntent(result);
方法器 checkResponseForIntent 可能是这样的:
private void checkResponseForIntent(MyBeanResult response)
if (response == null || response.fault != null)
noServiceAvailable();
return;
... or do what ever you want, even call an other async task...
我在使用 AsyncTask 中的 .get()
时遇到了这个问题,而 ProgressBar 根本不适用于 get()
,实际上,它只有在 doInBackground 完成后才有效。
希望对你有帮助。
【讨论】:
是的,进度对话框无法使用 get() 方法【参考方案4】:使用泛型参数
AsyncTask<Params, Progress, Result>
Params
— 任务的输入数据类型
Progress
— 如何向全世界通报进展
Result
— 任务的输出数据类型
想想
Result = task(Params)
示例
通过字符串 URL 加载 YourObject
:
new AsyncTask<String, Void, YourObject>()
@Override
protected void onPreExecute()
/* Called before task execution; from UI thread, so you can access your widgets */
// Optionally do some stuff like showing progress bar
;
@Override
protected YourObject doInBackground(String... url)
/* Called from background thread, so you're NOT allowed to interact with UI */
// Perform heavy task to get YourObject by string
// Stay clear & functional, just convert input to output and return it
YourObject result = callTheServer(url);
return result;
@Override
protected void onPostExecute(YourObject result)
/* Called once task is done; from UI thread, so you can access your widgets */
// Process result as you like
.execute("http://www.example.com/");
【讨论】:
【参考方案5】:将 MainActivity 传递给 Async 类,因此您将在内部类中访问 MainActivity 函数, 这对我来说很好:
public class MainActivity extends ActionBarActivity
void callAsync()
Async as = new Async(this,12,"Test");
as.execute();
public void ReturnThreadResult(YourObject result)
// TO DO:
// print(result.toString());
public class Async extends AsyncTask<String, String, Boolean>
MainActivity parent;
int param1;
String param2;
public Async(MainActivity parent,int param1,String param2)
this.parent = parent;
this.param1 = param1;
this.param2 = param2;
@Override
protected void onPreExecute();
@Override
protected YourObject doInBackground(String... url)
return result;
@Override
protected void onPostExecute(YourObject result)
// call an external function as a result
parent.ReturnThreadResult(result);
【讨论】:
【参考方案6】:为什么不调用处理值的方法?
public class MyClass extends Activity
private class myTask extends AsyncTask<Void, Void, Void>
//initiate vars
public myTask()
super();
//my params here
protected Void doInBackground(Void... params)
//do stuff
return null;
@Override
protected void onPostExecute(Void result)
//do stuff
myMethod(myValue);
private myHandledValueType myMethod(Value myValue)
//handle value
return myHandledValueType;
【讨论】:
我如何访问 myMethod? 它不起作用,例如我的任务 mTask = new myTask(); mTask.execute();不会返回 myHandledValueType。 格式很差,但基本上 AsyncTask 调用活动方法:带有 myValue 的 myMethod(据说是在 doInBackground 中设置的类变量)。在 myMethod 中,您处理 AsyncTask 的结果(这里没有返回值的原因,只需在此方法中处理结果即可)。更好的实现是扩展 AsyncTask你可以试试这个:myvalue = new myTask().execute().get();
减号是它会冻结进程,直到异步不会完成;
【讨论】:
任何其他方式来避免这种冻结的 UI 不鼓励在 UI 线程中执行繁重的任务,这可能会导致 ANR。此外,在主线程上执行网络操作会导致 NetworkOnMainThreadException 并按设计立即崩溃。查看那篇不错的文章:developer.android.com/training/articles/perf-anr#java【参考方案8】:最简单的方法是将调用对象传递给异步任务(如果你愿意,在构造它时):
public class AsyncGetUserImagesTask extends AsyncTask<Void, Void, Void>
private MyImagesPagerFragment mimagesPagerFragment;
private ArrayList<ImageData> mImages = new ArrayList<ImageData>();
public AsyncGetUserImagesTask(MyImagesPagerFragment imagesPagerFragment)
this.mimagesPagerFragment = imagesPagerFragment;
@Override
public Void doInBackground(Void... records)
// do work here
return null;
@Override
protected void onPostExecute(Void result)
mimagesPagerFragment.updateAdapter(mImages);
并且在调用类(你的activity或fragment)中执行任务:
public class MyImagesPagerFragment extends Fragment
@Override
public void onCreate(Bundle savedInstanceState)
AsyncGetUserImagesTask mGetImagesTask = new AsyncGetUserImagesTask(this);
mGetImagesTask.execute();
然后 onPostExecuteMethod 将调用您喜欢的原始类的任何方法,例如:
public void updateAdapter(List<ImageData> images)
mImageAdapter.setImages(images);
mImageAdapter.notifyDataSetChanged();
【讨论】:
这本质上是依赖注入 此解决方案有效。除非这将被其他片段重用,否则将异步任务设为私有内部类(或匿名)而不传递片段会更简单。此外,您可以指定图像数组,而不是使用 Void 作为结果。 doInBackground 将返回图像数组,并在执行后将其作为参数,这将消除类变量传递数据的需要。 @jt-gilkeson 是正确的,但这也不能重用,因为您必须在您拥有的每个 Activity 类中包含该内部类。怎样才能做到这一点?制作一个可重用的 AsyncTask 类? @DIEGOF.G。正确 - 因此“除非这将是可重复使用的”资格。如果您想要一个可重用的解决方案 - 您可以创建一个嵌入了内部 asynctask 类的自定义片段类 - 然后从该类扩展需要此功能的片段。 或者你可以让 AsyncGetUserImagesTask 构造函数接受一个接口而不是一个具体的片段 - 然后任何实现接口的东西都可以使用这个类(即依赖注入)。【参考方案9】:这就是onPostExecute()
的用途。它在 UI 线程上运行,您可以将结果从那里传送到屏幕(或您需要的任何其他地方)。在最终结果可用之前不会调用它。如果您想提供中间结果,请查看onProgressUpdate()
【讨论】:
是的,我知道 doInBackground() 返回数据并将其放在那里,但是如何以可变形式将数据传输到我的主要活动? @Tom91136 - 只需覆盖onPostExecute()
即可将结果存储在您选择的变量中。请注意,您的原始代码没有意义,因为(假设的)方法 myTask.getValue()
将在 before 结果可用之前被调用。您也可以调用 AsyncTask 的 get()
方法来获取结果,但是在您确定结果可用之前,您不应该从 UI 线程执行此操作。否则,它会阻塞 UI 线程,首先破坏了使用 AsyncTask 的目的。
@TedHopp 您说要将结果存储在您在onPostExecute()
中选择的变量中,但是您如何将该变量返回到您的活动中?例如,如果您希望您的任务连接到 Internet 并下载一些信息,然后您想用这些信息做一些事情......你如何 .execute()
AsyncTask
然后在接下来的情况下用这些信息做一些事情代码行在AsyncTask
完成之前运行?
@Jakobud - 你不能那样做。您需要将“下一行代码”移到其他地方(例如,onPostExecute()
)。我使用这个类比:您不会编写等待用户输入的代码(例如,按下按钮);相反,您编写的事件处理程序会在用户提供一些输入时做出反应。将 onPostExecute()
视为 AsyncTask 的结果可用时的事件处理程序。这就是您放置代码(或调用方法)的地方,除非结果确实可用,否则这些代码将不起作用。
所以 AsyncTask
不应该被认为是一个简单地获取和返回一些信息的实用函数,而是一个更大的东西,它可以获取信息并在获取后操纵 UI(或其他)那个信息?以上是关于从Android中的AsyncTask返回一个值[重复]的主要内容,如果未能解决你的问题,请参考以下文章