Возврат значения из 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() 

EDIT: Это было задано давно, когда я не был знаком с Java, теперь, когда мне будет лучше, я сделаю краткое резюме:

Точка задачи async заключается в том, что задача asynchronous, а это означает, что после вызова execute() в задаче задача запускается в потоке. Возвращение значения из asynctask было бы бессмысленным, потому что исходный поток вызовов уже выполнял другие вещи (при этом задача была асинхронной).

Подумайте о времени: В какой-то момент вы запустили задачу, которая будет работать параллельно с основным потоком. Когда выполнение параллельной задачи завершено, время также продолжается по основному потоку. Параллельная задача не может вернуться во времени, чтобы вернуть значение в основной поток.

Я приехал из C, поэтому я мало знал об этом. Но похоже, что у многих людей возникает один и тот же вопрос, поэтому я подумал, что немного проясню это.

Ответ 1

Почему бы не вызвать метод, который обрабатывает значение?

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;
    }
}

Ответ 2

Что для onPostExecute(). Он работает в потоке пользовательского интерфейса, и вы можете доставить свой результат оттуда на экран (или где-нибудь еще, что вам нужно). Он не будет вызываться до тех пор, пока окончательный результат не будет доступен. Если вы хотите предоставить промежуточные результаты, посмотрите onProgressUpdate()

Ответ 3

Самый простой способ - передать вызывающий объект в задачу async (при его построении, если хотите):

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();
    }
}

Ответ 4

вы можете попробовать следующее: myvalue = new myTask().execute().get(); минус - это заморозит процесс до тех пор, пока асинхрон не будет закончен;

Ответ 5

Пример кода 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);
    }
  }
}

Ответ 6

Вам необходимо использовать "протоколы для делегирования или предоставления данных 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...
    void onTaskEndWithResult(int success) {

    }

    //handle data to show them in activity...
    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...
    Object objectAtIndex(int index) {
        return new Data(index);
    }

    //send more information...
    int indexOfObject(Object object) {
        return new object.getIndex();
    }
}

Ответ 7

Для получения результата от 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. Я надеюсь, что это поможет вам.

Ответ 8

Использовать общие параметры

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/");

Ответ 9

Передайте класс 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);
    }
  }