Как передавать переменные в и из AsyncTasks?

Я не тратил много времени на работу с AsyncTasks в Android. Я пытаюсь понять, как передавать переменные в класс и из него. Синтаксис здесь:

class MyTask extends AsyncTask<String, Void, Bitmap>{

     // Your Async code will be here

}

он немного запутывается с синтаксисом < > в конце определения класса. Никогда не видел этот тип синтаксиса раньше. Кажется, что я ограничен только передачей одного значения в AsyncTask. Неужели я ошибаюсь в этом? Если мне еще нужно пройти, как мне это сделать?

Кроме того, как мне вернуть значения из AsyncTask?

Это класс, и если вы хотите его использовать, вы вызываете new MyTask().execute(), но фактический метод, который вы используете в классе, doInBackground(). Итак, где вы действительно что-то возвращаете?

Ответы

Ответ 1

Примечание. Вся информация, приведенная ниже, доступна на странице разработчиков Android AsyncTask. В этом примере заголовок Использование. Также взгляните на запись в блоге разработчиков Android безболезненного потока.

Посмотрите исходный код для AsynTask.


Смешная нотация < > позволяет настроить задачу Async. Скобки используются для поддержки generics в Java.

Есть 3 важные части задачи, которую вы можете настроить:

  • Тип переданных параметров - любое число, которое вы хотите
  • Тип того, что вы используете для обновления индикатора выполнения/индикатора
  • Тип для того, что вы возвращаете после выполнения фоновой задачи

И помните, что любой из вышеперечисленных может быть интерфейсами. Так вы можете передавать несколько типов по одному и тому же вызову!

Вы помещаете типы этих трех вещей в угловые скобки:

<Params, Progress, Result>

Итак, если вы собираетесь пройти через URL и использовать Integers для обновления прогресса и возврата логического успеха, который вы хотите записать:

public MyClass extends AsyncTask<URL, Integer, Boolean> {

В этом случае, если вы загружаете растровые изображения, например, вы будете обрабатывать то, что вы делаете с растровыми изображениями в фоновом режиме. Вы также можете просто вернуть HashMap Bitmaps, если хотите. Также помните, что переменные-члены, которые вы используете, не ограничены, поэтому не чувствуйте себя слишком привязанными параметрами, прогрессом и результатом.

Чтобы запустить экземпляр AsyncTask, а затем execute он либо последовательно, либо параллельно. В ходе выполнения вы передаете переменные. Вы можете перейти более чем к одному.

Обратите внимание, что вы не вызываете doInBackground() напрямую. Это связано с тем, что это приведет к нарушению магии AsyncTask, которая заключается в том, что doInBackground() выполняется в фоновом потоке. Вызов его напрямую, как есть, заставит его работать в потоке пользовательского интерфейса. Итак, вместо этого вы должны использовать форму execute(). Задачей execute() является запуск doInBackground() в фоновом потоке, а не в потоке пользовательского интерфейса.

Работа с нашим примером сверху.

...
myBgTask = new MyClass();
myBgTask.execute(url1, url2, url3, url4);
...

onPostExecute будет срабатывать, когда будут выполнены все выполняемые задачи.

myBgTask1 = new MyClass().execute(url1, url2);
myBgTask2 = new MyClass().execute(urlThis, urlThat);

Обратите внимание, как вы можете передать несколько параметров на execute(), который передает несколько параметров на doInBackground(). Это достигается с помощью varargs (вы знаете, как String.format(...). Многие примеры показывают только извлечение первого params с помощью params[0], но вы должны убедиться, что вы получили все параметры. Если вы передаете URL-адреса это было бы (взято из примера AsynTask, есть несколько способов сделать это):

 // This method is not called directly. 
 // It is fired through the use of execute()
 // It returns the third type in the brackets <...>
 // and it is passed the first type in the brackets <...>
 // and it can use the second type in the brackets <...> to track progress
 protected Long doInBackground(URL... urls) 
 {
         int count = urls.length;
         long totalSize = 0;

         // This will download stuff from each URL passed in
         for (int i = 0; i < count; i++) 
         {
             totalSize += Downloader.downloadFile(urls[i]);
             publishProgress((int) ((i / (float) count) * 100));
         }

         // This will return once when all the URLs for this AsyncTask instance
         // have been downloaded
         return totalSize;
 }

Если вы собираетесь выполнять несколько задач bg, вы должны учесть, что вышеуказанные вызовы myBgTask1 и myBgTask2 будут выполняться последовательно. Это здорово, если один вызов зависит от другого, но если вызовы независимы - например, вы загружаете несколько изображений, и вам все равно, какие из них поступают первыми, - тогда вы можете совершать вызовы myBgTask1 и myBgTask2 параллельно с THREAD_POOL_EXECUTOR:

myBgTask1 = new MyClass().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url1, url2);
myBgTask2 = new MyClass().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, urlThis, urlThat);

Примечание:

Пример

Вот пример AsyncTask, который может принимать столько же типов, сколько требуется для одной и той же команды execute(). Ограничение состоит в том, что каждый тип должен реализовывать один и тот же интерфейс:

public class BackgroundTask extends AsyncTask<BackgroundTodo, Void, Void>
{
    public static interface BackgroundTodo
    {
        public void run();
    }

    @Override
    protected Void doInBackground(BackgroundTodo... todos)
    {
        for (BackgroundTodo backgroundTodo : todos)
        {
            backgroundTodo.run();

            // This logging is just for fun, to see that they really are different types
            Log.d("BG_TASKS", "Bg task done on type: " + backgroundTodo.getClass().toString());
        }
        return null;
    }
}

Теперь вы можете сделать:

new BackgroundTask().execute(this1, that1, other1); 

Где каждый из этих объектов - другой тип! (который реализует один и тот же интерфейс)

Ответ 2

Я понимаю, что это поздний ответ, но вот то, что я делал в последнее время.

Когда мне нужно передать кучу данных в AsyncTask, я могу либо создать свой собственный класс, передать его и затем получить доступ к его свойствам, например:

public class MyAsyncTask extends AsyncTask<MyClass, Void, Boolean> {

    @Override
    protected Boolean doInBackground(MyClass... params) {

        // Do blah blah with param1 and param2
        MyClass myClass = params[0];

        String param1 = myClass.getParam1();
        String param2 = myClass.getParam2();

        return null;
    }
}

а затем выполните следующие действия:

AsyncTask asyncTask = new MyAsyncTask().execute(new MyClass());

или я могу добавить конструктор в свой класс AsyncTask, например:

public class MyAsyncTask extends AsyncTask<Void, Void, Boolean> {

    private String param1;
    private String param2;

    public MyAsyncTask(String param1, String param2) {
        this.param1 = param1;
        this.param2 = param2;
    }

    @Override
    protected Boolean doInBackground(Void... params) {

        // Do blah blah with param1 and param2

        return null;
    }
}

а затем выполните следующие действия:

AsyncTask asyncTask = new MyAsyncTask("String1", "String2").execute();

Надеюсь, это поможет!

Ответ 3

Поскольку вы можете передать массив объектов в квадратной скобке, это лучший способ передать данные, на основе которых вы хотите выполнять обработку в фоновом режиме.

Вы можете передать ссылку своей деятельности или представление в конструкторе и использовать это для передачи данных в свою деятельность

class DownloadFilesTask extends AsyncTask<URL, Integer, List> {
    private static final String TAG = null;
    private MainActivity mActivity;
    public DownloadFilesTask(MainActivity activity) {
        mActivity = activity;
        mActivity.setProgressBarIndeterminateVisibility(true);
    }

    protected List doInBackground(URL... url) {
        List output = Downloader.downloadFile(url[0]);
        return output;
    }

    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }

    private void setProgressPercent(final Integer integer) {
        mActivity.setProgress(100*integer);
    }

    protected void onPostExecute(List output) {

        mActivity.mDetailsFragment.setDataList((ArrayList<Item>) output);

        //you could do other processing here
    }
}

Ответ 4

В качестве альтернативы вы можете просто использовать обычный поток и использовать обработчик для отправки данных обратно в поток ui, переопределив функцию handlemessage.

Ответ 5

Передача простой строки:

 public static void someMethod{ 
     String [] variableString= {"hello"};
     new MyTask().execute(variableString);
}

static class MyTask extends AsyncTask<String, Integer, String> {

        // This is run in a background thread
        @Override
        protected String doInBackground(String... params) {
            // get the string from params, which is an array
            final String variableString = params[0];

            Log.e("BACKGROUND", "authtoken: " + variableString);

            return null;
        }
    }