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

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

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

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

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

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

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

12 ответов


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

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

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

и a 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();

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

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

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

что может Handler и AsyncTask действительно помочь?

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

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

разницу между Handler и AsyncTask is: Use AsyncTask, когда вызывающий поток это UI Thread. Это то, что Android документ говорит:

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

я хочу подчеркнуть на двух очки:

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

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

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

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

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

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


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

AsyncTask:

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

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

  1. получение некоторых данных из веб-служб и отображение поверх макета.

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

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


нить

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

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

проводник

класс "Handler " можно обновить пользовательский интерфейс. Дескриптор предоставляет методы для получения сообщений и для runnables. Чтобы использовать обработчик, вы должны создать его подкласс и переопределить 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) нагреет телефон в минутах. Если вы говорите, что взаимодействие UI не важно, возможно,AsyncTask - это хорошо, потому что они предназначены для длительных процессов. Это просто мое мнение о он.

обновление

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

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


Thread:

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

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

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

проводник:

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

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

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

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

AsyncTask:

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

недостатки:

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

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

HandlerThread:

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

ThreadPoolExecutor:

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

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

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

поскольку взаимодействие с UI не требуется, вы не можете пойти на 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 предназначен для выполнения не более нескольких секунд операции, которые должны быть сделаны в фоновом режиме (не рекомендуется для мегабайт загрузки файлов с сервера или вычислить cpu интенсивной задачи, такие как операции ввода-вывода файлов ). Если вам нужно выполнить длительную операцию, настоятельно рекомендуется использовать собственные потоки java. Java дает вам различные классы, связанные с потоками, чтобы делать то, что вам нужно. Использовать Handler обновить UI-потоке.


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

MyImageSearch

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

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

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

  • выбор 1: Создайте LooperThread [расширяет поток] - и продолжайте загрузка изображений последовательно в одном потоке, сохраняя этот поток открыть [looper.loop()]
  • выбор 2: Используйте пул потоков и опубликуйте runnable через 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);
    }
}

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


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 он в основном используется для связи с основным потоком путем создания и отправки сообщений через handler

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

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


нить

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

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

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

проводник

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

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

AsyncTask

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

см. Android поток, обработчик, asynctask и пулы потоков для примера.