Вернуть значение из 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);
    }
  }  
Другие вопросы по тегам