Вернуть значение из AsyncTask в Android
Один простой вопрос: возможно ли вернуть значение в 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()
РЕДАКТИРОВАТЬ: Это было задано давно, когда я не был знаком с Ja va, теперь, когда я лучше с ней, я сделаю краткое резюме:
Суть асинхронной задачи заключается в том, что задача asynchronous
Это означает, что после того, как вы позвоните execute()
в задаче задача начинает выполняться в собственном потоке. возвращать значение из async task было бы бессмысленно, поскольку исходный вызывающий поток уже выполнял другие действия (таким образом, задача асинхронная).
Подумайте о времени. В какой-то момент вы запустили задачу, которая будет выполняться параллельно с основным потоком. Когда параллельное выполнение задачи завершено, время в главном потоке также истекло. Параллельная задача не может вернуться назад во времени, чтобы вернуть значение в основной поток.
Я приехал из Си, поэтому я не знал об этом много. Но, похоже, у многих людей один и тот же вопрос, поэтому я решил немного прояснить его.
9 ответов
Почему бы не вызвать метод, который обрабатывает значение?
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;
}
}
Это то что onPostExecute()
для. Он работает в потоке пользовательского интерфейса, и вы можете доставить свой результат оттуда на экран (или в любое другое место). Он не будет вызван, пока не будет получен окончательный результат. Если вы хотите получить промежуточные результаты, посмотрите на onProgressUpdate()
Самый простой способ - передать вызывающий объект в асинхронную задачу (при ее создании, если хотите):
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);
}
}
И в вызывающем классе (ваша деятельность или фрагмент) выполните задачу:
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();
}
}
Пример кода: Activity использует AsyncTask для получения значения в фоновом потоке, затем AsyncTask возвращает результат обратно в Activity, вызывая processValue:
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);
}
}
}
Вы можете попробовать это: myvalue = new myTask().execute().get();
минус в том, что он заморозит процесс до тех пор, пока асинкрон не будет закончен;
Вам необходимо использовать "протоколы" для делегирования или предоставления данных AsynTask
,
Делегаты и источники данных
Делегат - это объект, который действует от имени или в координации с другим объектом, когда этот объект встречает событие в программе. ( Определение Apple)
протоколы - это интерфейсы, которые определяют некоторые методы для делегирования некоторых типов поведения.
DELEGATE: захват событий из объекта в фоновом потоке
AsynkTask:
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, и делегат пытается отредактировать некоторое представление, это приведет к сбою, потому что представлением может управлять только основной поток.
EXTRA
DATASOURCE: предоставить данные для объекта в фоновом потоке
AsyncTask:
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();
}
}
Используйте общие параметры
AsyncTask<Params, Progress, Result>
Params
- тип входных данных задачиProgress
- как сообщить миру о прогрессеResult
- тип выходных данных задачи
Думай как
Result = task(Params)
пример
нагрузка YourObject
по строковому URL:
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/");
Поскольку вы получаете результат от 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...
У меня была эта проблема с использованием .get()
от AsyncTask и ProgressBar просто не работает с get()
на самом деле, это работает только после завершения doInBackground.
Я надеюсь, что это поможет вам.
Передайте 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);
}
}