Обработчик против AsyncTask против потока


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

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

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

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

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

Тем временем документация была значительно улучшена.
12 358

12 ответов:

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

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

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

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

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

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

И что касается AsyncTask, Как говорится в ссылке разработчика Android :

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

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

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

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

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

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

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

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

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

Таким образом, нет никакой магии в AsyncTask или Handler. Они просто облегчают вашу работу в качестве разработчика.

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

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

Почему вы должны использовать новую нить? Вы можете поищите в гугле. Много-много причин.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

После того, как вы посмотрите в глубину, это прямо вперед.

AsyncTask:

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

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

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

Handler:

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

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

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

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

Thread:

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

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

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

Вы можете создать фон Thread и передать сообщения обратно в основной поток с помощью метода Handler post.

Поток

Android поддерживает стандартные Javaпотоки . Вы можете использовать стандартные потоки и инструменты из пакета " 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

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

Обновить

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

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

Thread:

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

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

Вы можете найти ответы на свои запросы на странице документацииThreading performance .

Обработчик :

A Handler позволяет отправлять и обрабатывать Сообщение и 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: тост в потоке

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

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

MyImageSearch

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

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

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

  • выбор 1: Создать LooperThread [расширяет поток] - и продолжайте загрузка изображений последовательно в одном потоке, сохраняя этот поток открой [петлитель.loop ()]
  • выбор 2: Используйте пул потоков и разместите runnable через myHandler, который содержит ссылку на мой ImageView, но так как представления в виде сетки повторно используются, снова может возникнуть проблема, где изображение в индексе 4 является отображается в индексе 9 [загрузка может занять больше времени]
  • Выбор 3 [я использовал это]: используйте пул потоков и отправьте сообщение myHandler, содержащее данные, относящиеся к индексу ImageView и ImageView сам, так что при выполнении handleMessage () мы будем обновлять ImageView только в том случае, если currentIndex соответствует индексу изображения, которое мы попробовал скачать.
  • выбор 4: Используйте AsyncTask для загрузки изображения в фоновом режиме, но здесь у меня не будет доступа к количеству потоков, которые я хочу в пул потоков и он варьируется в зависимости от версии android, но в Choice 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);
    }
}
Я надеюсь, что мой ответ, хотя и длинный, поможет понять некоторые из более тонких деталей.
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();
    }

}

Handler - является средством связи между потоками. В android он в основном используется для связи с основным потоком путем создания и отправки сообщений через обработчик

AsyncTask - используется для выполнения длительно работающих приложений в фоновом потоке. С помощью n AsyncTask можно выполнить операцию в фоновом потоке и получить результат в основном потоке приложения.

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

Поток

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

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

Чтобы предотвратить это, можно создавать рабочие потоки и выполнять фоновые или длительные задачи.

Обработчик

Поскольку android использует однопоточную модель, компоненты пользовательского интерфейса создаются не потокобезопасными, то есть только созданный поток должен получить к ним доступ, что означает, что компонент пользовательского интерфейса должен обновляться только в главном потоке. Поскольку компонент пользовательского интерфейса выполняется в основном потоке, задачи, выполняемые в рабочих потоках, не могут изменять компоненты пользовательского интерфейса. Это где Хэндлер входит в картину. Обработчик с помощью Looper может подключиться к новому потоку или существующему потоку и запустить код, который он содержит на подключенном потоке.

Обработчик

Делает возможным межпотоковое взаимодействие. Используя обработчик, фоновый поток может отправлять ему результаты, а обработчик, подключенный к основному потоку, может обновлять компоненты пользовательского интерфейса в основном потоке.

AsyncTask

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

См. ветке андроид, обработчик, asynctask и потоки для примера.