Метод get () AsyncTask: есть ли какой-нибудь сценарий, где это действительно лучший вариант?

Отвечая на этот вопрос, я получил сомнение в смысле / полезности использования метода get () класса AsyncTask для Android.

public final Result get () Waits if necessary for the computation to complete, and then retrieves its result. 

В принципе, это синхронное решение для класса AsyncTask, которое блокирует (замораживает) пользовательский интерфейс до завершения фоновых операций.

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

Если вам нужно, чтобы пользователь дождался завершения работы AsyncTask, вы можете показать диалог или прогрессDialog, каждый раз контролируя интерфейс. Я знаю, что это не совсем то же самое, но IMHO это гораздо лучший подход, чем использование метода get() .

AsyncTask – не единственный способ делать фоновые операции. В самом деле, в документации говорится, что AsyncTask следует использовать только для операций, AsyncTask не более нескольких секунд. Поэтому, если у вас есть задачи, которые занимают больше времени, они должны быть закодированы через классы, реализующие интерфейс runnable . Такие задачи в других (не AsyncTask) потоках вполне могут подождать AsyncTask , поэтому мне кажется, что идея о том, что нет ситуаций, когда вы хотите использовать AsyncTask.get() является ложью.

Обновление. В ответ на комментарий, чтобы подчеркнуть, что это может быть допустимым использованием AsyncTask.get() , возможно следующее:

  • AsyncTask существовать AsyncTask s, который инициируется из потока пользовательского интерфейса, который может включать обмен через Интернет, например, загрузку веб-страницы или связь с сервером. Независимо от результатов AsyncTask , некоторые (или все) результаты необходимы для обновления экрана. Следовательно, AsyncTask с его doInBackground за которым следует onPostExecute в потоке пользовательского интерфейса, имеет смысл.
  • Всякий раз, когда поток пользовательского интерфейса инициирует AsyncTask , он помещает объект AsyncTask в очередь, для дополнительной обработки отдельным фоновым потоком после получения результатов.
  • Для каждой очереди AsyncTask в очереди в фоновом потоке используется AsyncTask.get() чтобы дождаться завершения задачи, прежде чем выполнять дополнительную обработку. Одним из очевидных примеров дополнительной обработки может быть просто регистрация всех таких действий AsyncTask на сервере в Интернете, поэтому имеет смысл сделать это в фоновом режиме.

Следующий код показывает, что я имею в виду. Приложение вызывает KickOffAsynctask(...) когда захочет сделать AsyncTask, и есть фоновый поток, который автоматически выполнит задачу для последующей обработки после завершения задачи.

 public class MyActivity extends Activity { static class MyAsyncTaskParameters { } static class MyAsyncTaskResults { } Queue<MyAsyncTask> queue; // task queue for post-processing of AsyncTasks in the background BackgroundThread b_thread; // class related to the background thread that does the post-processing of AsyncTasks @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); queue = new ConcurrentLinkedQueue<MyAsyncTask>(); b_thread = new BackgroundThread(queue); b_thread.Start(); } void KickOffAsynctask(MyAsyncTaskParameters params) { MyAsyncTask newtask = new MyAsyncTask(); newtask.execute(params); synchronized(queue) { queue.add(newtask); } } static class MyAsyncTask extends AsyncTask<MyAsyncTaskParameters, Void, MyAsyncTaskResults> { @Override protected MyAsyncTaskResults doInBackground(MyAsyncTaskParameters... params) { MyAsyncTaskResults results = new MyAsyncTaskResults(); // do AsyncTask in background return results; } @Override protected void onPostExecute(MyAsyncTaskResults res){ // take required results from MyAsyncResults for use in the user interface } } static class BackgroundThread implements Runnable { // class that controls the post processing of AsyncTask results in background private Queue<MyAsyncTask> queue; private Thread thisthread; public boolean continue_running; public BackgroundThread(Queue<MyAsyncTask> queue) { this.queue=queue; thisthread = null; continue_running = true; } public void Start() { thisthread = new Thread(this); thisthread.start(); } @Override public void run() { try { do { MyAsyncTask task; synchronized(queue) { task = queue.poll(); } if (task == null) { Thread.sleep(100); } else { MyAsyncTaskResults results = task.get(); // post processing of AsyncTask results in background, eg log to a server somewhere } } while (continue_running); } catch(Throwable e) { e.printStackTrace(); } } } } 

Обновление2 . Другое возможное использование AsyncTask.get() произошло со мной. Стандартный совет – не использовать AsyncTask.get() из потока пользовательского интерфейса, поскольку он заставляет пользовательский интерфейс зависеть, пока результат не будет доступен. Однако для приложения, где требуется скрытность, это может быть именно то, что требуется. Итак, как насчет следующей ситуации: Джеймс Бонд врывается в гостиничный номер Le Chiffre и только имеет пару минут, чтобы извлечь все данные с телефона ворсина и установить вирус мониторинга. Он устанавливает приложение, предоставляемое Q, и запускает его, но он слышит, как кто-то идет, чтобы он скрывался. Ле Чифре входит в комнату и берет трубку, чтобы позвонить. В течение нескольких секунд телефон кажется немного невосприимчивым, но внезапно телефон просыпается, и он делает телефонный звонок без дальнейших размышлений. Конечно, причиной невнимательности был факт, что приложение Q работает. Ему было поручено выполнять различные задачи, и некоторые из них необходимо было выполнить в определенном порядке. Приложение использовало два потока для выполнения работы, а именно сам пользовательский интерфейс и единственный фоновый поток, который обрабатывает AsyncTask s. Пользовательский интерфейс полностью контролировал все задачи, но поскольку некоторые задачи необходимо выполнять перед другими задачами, в приложении присутствовали точки, в которых поток пользовательского интерфейса использовал AsyncTask.get() , ожидая завершения фоновой задачи: ).

Метод get никогда не должен использоваться в потоке пользовательского интерфейса, потому что он (как вы заметили) заморозит ваш пользовательский интерфейс. Он должен использоваться в фоновом потоке, чтобы дождаться конца вашей задачи, но, как правило, старайтесь избегать AsyncTask как можно больше ( ПОЧЕМУ? ).

Я бы предложил callback или eventbus в качестве замены.

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

Однако, я думаю, вам следует избегать запуска слишком много асинхронных задач параллельно, потому что это замедлит работу устройства.

Обычно я использую класс обработчика для выполнения трюка (загрузка асинхронного носителя). Подобно:

 public void setRightSongAdele(Song current) { RetriveCorrelateGenreSong rcgs = new RetriveCorrelateGenreSong(current); new Thread(rcgs).start(); } @SuppressLint("HandlerLeak") Handler updateRightAdeleHandler = new Handler() { @Override public void handleMessage(Message msg) { songNextText.setText(utils.reduceStringLength(rightSong.getTitle(), 15)); adeleNext.setImageBitmap(utils.getAlbumArtFromSong(rightSong.getPath(), getApplicationContext())); } }; 

Я отправляю свой собственный ответ, поскольку это то, что я думаю в этот момент, и никто не дал его.

Мое любопытство все еще живое, поэтому я не буду отмечать это правильно. Если кто-то ответит на этот вопрос блестяще, я буду отмечать его / ее ответ как правильный.

Мой ответ заключается в том, что на самом деле нет сценария, когда использование метода get() AsyncTask является лучшим решением.

ИМХО, используя этот метод, бессмысленно. Я имею в виду, что всегда кажется лучшим решением.

Аналогично, я бы сказал, что это семантически шокирует, так как его обращение изменяет вашу AsyncTask в эффективную «SyncTask».

Поскольку документация стоит:

«Ожидает, если это необходимо для завершения вычисления, а затем извлекает его результат».

Поэтому обязательно, чтобы вы не вызывали его в NetworkOnMainThreadException пользовательского интерфейса, иначе вы получите NetworkOnMainThreadException . Я бы использовал его в тестовой среде, которая включает в себя сетевые операции или подождать еще одну задачу.

EDIT: я ошибся в NetworkOnMainThreadException .

Кажется, AsyncTask.get () блокирует поток вызывающего, где AsyncTask.execute () не делает этого.

Вы можете использовать AsyncTask.get () для тестовых примеров, где вы хотите протестировать определенный вызов веб-службы, но вам не нужно, чтобы он был асинхронным, и вы хотите контролировать, сколько времени потребуется для завершения. Или в любое время, когда вы хотите протестировать свой веб-сервис в наборе тестов.

Синтаксис такой же, как и для выполнения:

  private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) * 100)); } return totalSize; } protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } } new DownloadFilesTask().get(5000, TimeUnit.MILLISECONDS); 

Вы можете использовать его в своем адаптере адаптера AutoCompleteTextView.

 private class Adapter extends BaseAdapter implements Filterable{ getFilter(){ return new Filter() { @Override protected FilterResults performFiltering(CharSequence constraint) { List<String> suggestions = MyApiSuggestionsApi(constraint).get(); return packageAsFilterResults(suggestions); } } } 

У меня есть приложение для редактирования фотографий, пользователь может открывать фотографии из URL-адресов.

Я использовал AsyncTask для загрузки фотографии с URL-адреса, в течение этого времени пользовательский интерфейс должен быть заблокирован. Что я сделал, я показал, что ProgressBar запускает метод get () AsyncTask, чем скрывает ProgressBar.

Это казалось лучшим и самым простым решением для меня.