Обработчик против AsyncTask против потока - PullRequest
375 голосов
/ 06 августа 2011

Я немного запутался по поводу различий между Handlers, AsyncTask и Threads в Android.Я прочитал довольно много блогов и вопросов здесь в stackoverflow.

Handler - это фоновые потоки, которые обеспечивают связь с пользовательским интерфейсом.Например, обновление индикатора выполнения должно быть выполнено с помощью Handler.Используя обработчики, вы получаете преимущество MessagingQueues, поэтому, если вы хотите запланировать сообщения или обновить несколько элементов пользовательского интерфейса или иметь повторяющиеся задачи.

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

Thread, однако не можете взаимодействовать с пользовательским интерфейсом, обеспечить более «базовую» многопоточность, и вы пропустите все абстракции AsyncTask.

ОднакоЯ хотел бы, чтобы в сокете работало соединение с сокетом.Должно ли это выполняться в обработчике или потоке, или даже в AsyncTask?Взаимодействие с интерфейсом вообще не нужно.Имеет ли это значение с точки зрения производительности, которую я использую?

Между тем документация была значительно улучшена.

Ответы [ 12 ]

350 голосов
/ 21 марта 2012

Если мы посмотрим на исходный код, то увидим, AsyncTask и Handler написаны исключительно на Java. (Однако есть некоторые исключения. Но это не важный момент)

Так что в AsyncTask или Handler нет магии. Эти занятия облегчают нашу жизнь как разработчика.

Например: если программа A вызывает метод A (), метод A () может выполняться в другом потоке с программой A. Мы можем легко проверить с помощью следующего кода:

Thread t = Thread.currentThread();    
int id = t.getId();

Почему мы должны использовать новый поток для некоторых задач? Вы можете Google для этого. Множество причин, например: тяжелые, продолжительные работы.

Итак, в чем разница между Thread, AsyncTask и Handler?

AsyncTask и Handler написаны на Java (внутренне они используют Thread), поэтому все, что мы можем сделать с Handler или AsyncTask, мы можем достичь с помощью Thread.

Что может Handler и AsyncTask реально помочь?

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

Вот почему мы должны использовать Handler и AsyncTask. Эти классы выполняют большую часть работы за нас, нам нужно только знать, какие методы переопределить.

Разница между Handler и AsyncTask составляет: Используйте AsyncTask, когда Резьба вызывающего абонента является Нить пользовательского интерфейса . Вот что говорится в документе Android:

AsyncTask позволяет правильно и легко использовать поток пользовательского интерфейса. Этот класс позволяет выполнять фоновые операции и публиковать результаты в пользовательском интерфейсе поток без необходимости манипулировать потоками и / или обработчиками

Я хочу подчеркнуть два момента:

1) Простое использование потока пользовательского интерфейса (поэтому используйте, когда поток вызывающего абонента является потоком пользовательского интерфейса).

2) Нет необходимости манипулировать обработчиками. (означает: вы можете использовать Handler вместо AsyncTask, но AsyncTask - более простой вариант).

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

@: когда вы читаете документ Android, вы увидите:

Обработчик позволяет отправлять и обрабатывать объекты Message и Runnable связанный с сообщением MessageQueue

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

Complicated? Просто помните, что Handler может безопасно общаться с вызывающим потоком.

53 голосов
/ 15 апреля 2015

Как показано в руководстве по фоновой обработке Android с обработчиками, AsyncTask и загрузчиками на сайте Vogella:

Класс Handler может использоваться для регистрации в потоке и обеспечивает простой канал для отправки данных в этот поток.

Класс AsyncTask инкапсулирует создание фонового процесса и синхронизацию с основным потоком. Он также поддерживает отчеты о ходе выполнения задач.

А Thread - это основной элемент многопоточности, который разработчик может использовать со следующим недостатком:

Если вы используете потоки Java, вы должны выполнить следующие требования в вашем собственном коде:

  • Синхронизация с основным потоком, если вы публикуете результаты обратно в пользовательский интерфейс
  • Нет по умолчанию для отмены потока
  • Нет пула потоков по умолчанию
  • Нет по умолчанию для обработки изменений конфигурации в Android

А что касается AsyncTask, то в Справочнике разработчика Android это звучит так:

AsyncTask обеспечивает правильное и простое использование потока пользовательского интерфейса. Этот класс позволяет выполнять фоновые операции и публиковать результаты в пользовательском интерфейсе поток без необходимости манипулировать потоками и / или обработчиками.

AsyncTask разработан как вспомогательный класс для Thread и Handler и не представляет собой общую структуру потоков. AsyncTasks в идеале следует использовать для коротких операций (несколько секунд на большинство.) Если вам нужно, чтобы потоки работали в течение длительного времени, Настоятельно рекомендуется использовать различные API, предоставляемые java.util.concurrent пакет, такой как Executor, ThreadPoolExecutor и FutureTask.

Обновление май 2015: Я нашел превосходную серию лекций , охватывающих эту тему.

Это поиск Google: Лекция Дугласа Шмидта по параллелизму и синхронизации Android

Это видео первой лекции на YouTube

Все это является частью CS 282 (2013): Системное программирование для Android из Университета Вандербильта . Вот Плейлист YouTube

Дуглас Шмидт кажется отличным лектором

Важное замечание: Если вы находитесь в точке, где вы планируете использовать AsyncTask для решения своих проблем с потоками, вам следует сначала проверить ReactiveX/RxAndroid для более подходящего шаблона программирования. Очень хороший ресурс для получения обзора - Изучение RxJava 2 для Android на примере .

52 голосов
/ 09 мая 2014

После глубокого взгляда все сразу.

AsyncTask:

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

Используется для небольших операций ожидания, таких как:

  1. Извлечение некоторых данных из веб-сервисов и отображение поверх макета.
  2. Запрос к базе данных.
  3. Когда вы понимаете, что запущенная операция никогда не будет вложена.

Handler:

Когда мы устанавливаем приложение в Android, оно создает поток для этого приложения с именем MAIN UI Thread. Все действия выполняются в этой ветке. По правилу однопоточной модели Android мы не можем получить доступ к элементам пользовательского интерфейса (растровое изображение, текстовое представление и т. Д.) Напрямую для другого потока, определенного внутри этого действия.

Обработчик позволяет вам общаться с потоком пользовательского интерфейса из другого фонового потока. Это полезно в Android, так как Android не позволяет другим потокам напрямую взаимодействовать с потоком пользовательского интерфейса. Обработчик может отправлять и обрабатывать объекты Message и Runnable, связанные с MessageQueue потока. Каждый экземпляр обработчика связан с одним потоком и очередью сообщений этого потока. Когда создается новый обработчик, он привязывается к потоку / очереди сообщений потока, который его создает.

Лучше всего подходит для:

  1. Позволяет создавать очереди сообщений.
  2. Планирование сообщений.

Thread:

Теперь пришло время поговорить о теме.

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

22 голосов
/ 06 августа 2011

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

Вы можете создать фон Thread и передавать сообщения обратно в основной поток, используя Handlerpost метод.

9 голосов
/ 24 марта 2014

Тема

Android поддерживает стандарт Java Threads . Вы можете использовать стандартные потоки и инструменты из пакета «java.util.concurrent», чтобы поместить действия в фоновый режим. Единственным ограничением является то, что вы не можете напрямую обновлять пользовательский интерфейс из фонового процесса.

Если вам нужно обновить пользовательский интерфейс из фоновой задачи, вам нужно использовать некоторые классы для Android. Вы можете использовать класс «android.os.Handler» для этого или класс «AsyncTask»

Обработчик

Класс «Handler» может обновлять пользовательский интерфейс. Дескриптор предоставляет методы для получения сообщений и для запуска. Чтобы использовать обработчик, вы должны создать его подкласс и переопределить handleMessage() для обработки сообщений. Для обработки Runable вы можете использовать метод post();. В вашей активности вам нужен только один экземпляр обработчика.

Вы можете отправлять сообщения с помощью метода sendMessage(Message msg) или sendEmptyMessage.

AsyncTask

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

Для получения дополнительной информации вы можете посмотреть по этим ссылкам.

http://mobisys.in/blog/2012/01/android-threads-handlers-and-asynctask-tutorial/

http://www.slideshare.net/HoangNgoBuu/android-thread-handler-and-asynctask

6 голосов
/ 27 августа 2017

Thread:

Вы можете использовать новый Thread для длительных фоновых задач без влияния на поток пользовательского интерфейса.В ветке Java вы не можете обновить ветку пользовательского интерфейса.

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

Вы можете найти ответы на свои вопросы в Потоковая производительность страница документации.

Обработчик :

A Handler позволяетотправлять и обрабатывать объекты Message и Runnable, связанные с MessageQueue потока.Каждый экземпляр Handler связан с одним потоком и очередью сообщений этого потока.

Существует два основных варианта использования Handler:

  1. Для планирования сообщений ивыполняемые объекты, которые должны быть выполнены как некая точка в будущем;

  2. Чтобы поставить в очередь действие, которое будет выполнено в потоке, отличном от вашего.

AsyncTask :

AsyncTask обеспечивает правильное и простое использование потока пользовательского интерфейса.Этот класс позволяет выполнять фоновые операции и публиковать результаты в потоке пользовательского интерфейса без необходимости манипулировать потоками и / или обработчиками.

Недостатки:

  1. По умолчанию приложение помещает все AsyncTask объекты, которые оно создает, в один поток.Поэтому они выполняются последовательно и, как и в случае основного потока, особенно длинный рабочий пакет может блокировать очередь.По этой причине используйте AsyncTask для обработки рабочих элементов короче 5 мс .

  2. AsyncTask объекты также являются наиболее распространенными нарушителями для проблем неявных ссылок,AsyncTask объекты также представляют риски, связанные с явными ссылками.

HandlerThread :

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

ThreadPoolExecutor :

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

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

Однако я бы хотел, чтобы в сервисе работало сокетное соединение.Должно ли это выполняться в обработчике или потоке, или даже в AsyncTask?Взаимодействие с интерфейсом вообще не нужно.Имеет ли это значение с точки зрения производительности, которую я использую?

Поскольку взаимодействие с пользовательским интерфейсом не требуется, вы не можете перейти на AsyncTask.Обычные темы не очень полезны и, следовательно, HandlerThread - лучший вариант.Поскольку вы должны поддерживать сокетное соединение, обработчик в главном потоке вообще бесполезен.Создайте HandlerThread и получите Handler от петлителя HandlerThread.

 HandlerThread handlerThread = new HandlerThread("SocketOperation");
 handlerThread.start();
 Handler requestHandler = new Handler(handlerThread.getLooper());
 requestHandler.post(myRunnable); // where myRunnable is your Runnable object. 

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

final Handler responseHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            //txtView.setText((String) msg.obj);
            Toast.makeText(MainActivity.this,
                    "Foreground task is completed:"+(String)msg.obj,
                    Toast.LENGTH_LONG)
                    .show();
        }
    };

в вашем Runnable вы можете добавить

responseHandler.sendMessage(msg);

Более подробную информацию о реализации можно найти здесь:

Android: Toast in aнить

5 голосов
/ 24 марта 2014

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

5 голосов
/ 06 августа 2011

По моему мнению, потоки - не самый эффективный способ выполнения соединений с сокетами, но они обеспечивают большую функциональность с точки зрения запуска потоков.Я говорю это потому, что из-за опыта, длительный запуск потоков приводит к тому, что устройства становятся очень горячими и ресурсоемкими.Даже простой while(true) разогреет телефон за считанные минуты.Если вы говорите, что взаимодействие с пользовательским интерфейсом не важно, возможно, AsyncTask хорошо, потому что они предназначены для долгосрочных процессов.Это просто мое мнение по этому поводу.

ОБНОВЛЕНИЕ

Пожалуйста, не обращайте внимания на мой ответ выше! Я ответил на этот вопрос еще в 2011 году, когда я был далекоменее опытен в Android, чем я сейчас.Мой ответ выше вводит в заблуждение и считается неправильным.Я оставляю это там, потому что многие люди прокомментировали это ниже, исправляя меня, и я усвоил мой урок.

В этой теме есть гораздо лучшие другие ответы, но я, по крайней мере, дам мне более правильный ответ,Нет ничего плохого в использовании обычной Java Thread;тем не менее, вы должны быть очень осторожны с реализацией этого, потому что неправильное выполнение может быть очень ресурсоемким (наиболее заметным симптомом может быть нагрев вашего устройства).AsyncTask довольно идеально подходят для большинства задач, которые вы хотите запускать в фоновом режиме (типичными примерами являются дисковый ввод / вывод, сетевые вызовы и вызовы базы данных).Однако AsyncTask s не следует использовать для особенно длительных процессов, которые могут потребоваться для продолжения после того, как пользователь закрыл ваше приложение или перевел свое устройство в режим ожидания.Я бы сказал, что в большинстве случаев обо всем, что не принадлежит потоку пользовательского интерфейса, можно позаботиться в AsyncTask.

2 голосов
/ 10 июля 2017
public class RequestHandler {

    public String sendPostRequest(String requestURL,
                                  HashMap<String, String> postDataParams) {

        URL url;

        StringBuilder sb = new StringBuilder();
        try {
            url = new URL(requestURL);

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(15000);
            conn.setConnectTimeout(15000);
            conn.setRequestMethod("POST");
            conn.setDoInput(true);
            conn.setDoOutput(true);


            OutputStream os = conn.getOutputStream();
            BufferedWriter writer = new BufferedWriter(
                    new OutputStreamWriter(os, "UTF-8"));
            writer.write(getPostDataString(postDataParams));

            writer.flush();
            writer.close();
            os.close();
            int responseCode = conn.getResponseCode();

            if (responseCode == HttpsURLConnection.HTTP_OK) {
                BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                sb = new StringBuilder();
                String response;
                while ((response = br.readLine()) != null){
                    sb.append(response);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    private String getPostDataString(HashMap<String, String> params) throws UnsupportedEncodingException {
        StringBuilder result = new StringBuilder();
        boolean first = true;
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (first)
                first = false;
            else
                result.append("&");

            result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
            result.append("=");
            result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
        }

        return result.toString();
    }

}
1 голос
/ 24 августа 2014

Позвольте мне попытаться ответить на вопрос здесь с примером :) - MyImageSearch [Пожалуйста, посмотрите изображение здесь на главном экране активности - содержащее текст редактирования / кнопку поиска / вид сетки]

MyImageSearch

Описание MyImageSearch - Как только пользователь введет данные в текстовое поле редактирования и нажмет на кнопку поиска, мы будем искать изображения в Интернете черезвеб-сервисы, предоставляемые flickr (вам нужно всего лишь зарегистрироваться там, чтобы получить ключ / секретный токен) - для поиска мы отправляем HTTP-запрос и возвращаем данные JSON в ответ, содержащие URL-адреса отдельных изображений, которые мы затем будем использовать для загрузкипредставление сетки.

Моя реализация - В основном задании я определю внутренний класс, который расширяет AsyncTask для отправки HTTP-запроса в метод doInBackGround и получения JSONОтветьте и обновите мой локальный ArrayList FlickrItems, который я собираюсь использовать для обновления моего GridView через FlickrAdapter (exteнаходит BaseAdapter) и вызывает adapter.notifyDataSetChanged () в onPostExecute () AsyncTask, чтобы перезагрузить представление сетки.Обратите внимание, что здесь HTTP-запрос является блокирующим вызовом, из-за которого я сделал это через AsyncTask.И я могу кэшировать элементы в адаптере, чтобы увеличить производительность или сохранить их на SDCard.Сетка, которую я буду раздувать в FlickrAdapter, содержит в моей реализации индикатор выполнения и представление изображения.Ниже вы можете найти код mainActivity, который я использовал.

Ответ на вопрос сейчас - Таким образом, когда у нас есть данные JSON для выборки отдельных изображений, мы можем реализовать логикуполучение изображений в фоновом режиме с помощью обработчиков или потоков или AsyncTask.Здесь следует отметить, что, поскольку мои загруженные изображения должны отображаться в пользовательском интерфейсе / главном потоке, мы не можем просто использовать потоки в том виде, как они есть, поскольку они не имеют доступа к контексту.В FlickrAdapter я могу придумать варианты:

  • Вариант 1: Создать LooperThread [расширяет поток] - и продолжать загружать изображения последовательно в одном потоке, сохраняя этот поток открытым [looper.loop ()]
  • Вариант 2: Используйте пул потоков и опубликуйте исполняемый файл через myHandler, который содержит ссылку на мой ImageView, но поскольку представления в сеточном представлении перерабатываются, снова может возникнуть проблема, когда изображение с индексом 4отображается с индексом 9 [загрузка может занять больше времени]
  • Вариант 3 [Я использовал это]: использовать пул потоков и отправить сообщение myHandler, который содержит данные, относящиеся к индексу ImageView иСам ImageView, поэтому при выполнении handleMessage () мы будем обновлять ImageView только в том случае, если currentIndex совпадает с индексом изображения, которое мы пытались загрузить.
  • Выбор 4: Используйте AsyncTask для загрузки изображений в фоновом режиме, но здесь у меня не будет доступа к количеству потоков, которые я хочу в пуле потоков, и оно зависит отдругая версия для Android, но в Варианте 3 я могу сознательно принять решение о размере пула потоков в зависимости от используемой конфигурации устройства.

Вот исходный код:

public class MainActivity extends ActionBarActivity {

    GridView imageGridView;
    ArrayList<FlickrItem> items = new ArrayList<FlickrItem>();
    FlickrAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageGridView = (GridView) findViewById(R.id.gridView1);
        adapter = new FlickrAdapter(this, items);
        imageGridView.setAdapter(adapter);
    }

    // To avoid a memory leak on configuration change making it a inner class
    class FlickrDownloader extends AsyncTask<Void, Void, Void> {



        @Override
        protected Void doInBackground(Void... params) {
            FlickrGetter getter = new FlickrGetter();

            ArrayList<FlickrItem> newItems = getter.fetchItems();

            // clear the existing array
            items.clear();

            // add the new items to the array
            items.addAll(newItems);

            // is this correct ? - Wrong rebuilding the list view and should not be done in background
            //adapter.notifyDataSetChanged();

            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            super.onPostExecute(result);

            adapter.notifyDataSetChanged();
        }

    }

    public void search(View view) {
        // get the flickr data
        FlickrDownloader downloader = new FlickrDownloader();
        downloader.execute();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...