Android какие способы работы с многопоточностью знаешь

Содержание
  1. Потоки
  2. Использование фоновых потоков
  3. Плохое приложение
  4. Запуск потока
  5. Усыпить поток
  6. Приоритет потоков
  7. Отмена выполнения потока
  8. Многопоточность и асинхронность
  9. Создание потоков и визуальный интерфейс
  10. Многопоточность в Android. Все что вам нужно знать. Часть 1 — Введение
  11. Многозадачность в Android
  12. Компоненты многопоточности, которые присоединяются к активности / фрагменту
  13. AsyncTask
  14. Загрузчики
  15. Компоненты многопоточности, которые не присоединяются к активности / фрагменту
  16. Service
  17. IntentService
  18. Многозадачность в Android. Часть 2 — 7 шаблонов использования многопоточности в Android
  19. Случай 1. Выполнение запроса по сети, без необходимости ответа от сервера
  20. Вариант 1. AsyncTask или загрузчики
  21. Вариант 2. Service
  22. Вариант 3. IntentService
  23. Случай 2. Выполнение сетевого вызова и получение ответа от сервера
  24. Вариант 1. Service или IntentService
  25. Вариант 2. AsyncTask или загрузчики
  26. Вариант 3. RxJava
  27. Случай 3. Цепочка сетевых вызовов
  28. Вариант 1. AsyncTask или загрузчики
  29. Вариант 2. RxJava с использованием flatMap
  30. Случай 4. Общение c UI потоком из другого потока
  31. Вариант 1. RxJava внутри сервиса
  32. Вариант 2. BroadcastReceiver
  33. Вариант 3. Использование Handler
  34. Вариант 3. Использование EventBus
  35. Случай 5. Двусторонняя связь между потоками на основе действий пользователя
  36. Вариант 1. Использование EventBus
  37. Вариант 2. Использование BoundService
  38. Случай 6. Выполнение действий параллельно и получение результатов
  39. Случай 7. Запрос к локальной SQLite базе данных
  40. Заключение

Потоки

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

Использование фоновых потоков

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

Применение фоновых потоков — необходимое условие, если вы хотите избежать появления диалогового окна для принудительного закрытия приложения. Когда активность в Android на протяжении 5 секунд не отвечает на события пользовательского ввода (например, нажатие кнопки) или приёмник широковещательных намерений не завершает работу обработчика onReceive() в течение 10 секунд, считается, что приложение зависло. Подобные ситуации следует избегать любой ценой. Используйте фоновые потоки для всех трудоёмких операций, включая работу с файлами, сетевые запросы, транзакции в базах данных и сложные вычисления.

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

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)
  • Handlers
  • AsyncTask

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

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

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

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

Если вам нужно обновлять интерфейс программы, то нужно использовать AsyncTask, о котором говорилось выше, или вы можете реализовать ваш собственный класс, наследованный от Thread, используя объект Handler из пакета android.os для синхронизации с потоком GUI перед обновлением пользовательского интерфейса.

Вы можете создавать дочерние потоки и управлять ими с помощью класса Handler, а также классов, доступных в пространстве имён java.lang.Thread. Ниже показан простой каркас для переноса операций в дочерний поток.

Плохое приложение

Напишем «плохое» приложение, неправильно использующее основной поток. Однажды мы писали программу для подсчёта ворон. На этот раз будем считать чёрных котов, которые перебегают нам дорогу. Зачем они это делают — молчит наука. Может быть собранная статистика поможет разгадать тайну. Добавим на экран активности кнопки и текстовую метку. Код для щелчка кнопки.

Для имитации тяжёлой работы программа делает паузу на двадцать секунд, а потом выводит текст с подсчётом котов. Если нажать на кнопку один раз и подождать двадцать секунд, то программа отработает как положено. Но представьте себе, что вы нажали на кнопку один раз. Программа запустила паузу. Вы, не дожидаясь окончания паузы, снова нажали на кнопку. Программа должна выполнить вашу команду, но предыдущая команда ещё не отработала и наступает конфликт. Попробуйте нажать на кнопку несколько раз с небольшими перерывами. В какой-то момент приложение зависнет и выведет системное диалоговое окно:

В реальных приложениях такое окно может разозлить пользователя и он поставит низкую оценку вашему приложению.

В данном случае ошибку вызывает не сам вывод текста в текстовой метке, который, к слову, тоже выполняется в основном потоке, а сам щелчок кнопки. Если вы закомментируете последние две строчки кода, связанные с TextView, то ошибка сохранится.

Вам необходимо перенести трудоёмкую задачу в отдельный поток. Для этого создаётся экземпляр класса Runnable, у которого есть метод run(). Далее создаётся объект Thread, в конструкторе у которого указывается созданный Runnable. После этого можно запускать новый поток с помощью метода start(). Перепишем пример.

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

Основной поток также называют UI-потоком. Имено в главном потоке можно обновить текст у текстовой метки. В создаваемых нами потоках это делать нельзя. Если вы уберёте комментарии с последнего примера и запустите проект, то получите сообщение об ошибке.

Нужен некий посредник между создаваемыми потоками и основным UI-потоком. В роли такого посредника служит класс Handler (полное название класса android.os.Handler, не перепутайте). Вам нужно создать экземпляр класса и указать код, который нужно выполнить.

После строчки кода с Log.i() добавьте вызов метода посредника.

Поток вызывает посредника, который в свою очередь обновляет интерфейс. В нашем случае посредник посылает пустое сообщение от потока.

Но бывает так, что от потока требуется получить информацию для обработки. Ниже упрощённый пример.

Запуск потока

Предположим, мы разрабатываем собственный проигрыватель. У нас есть кнопка Play, которая вызывает метод play() для воспроизведения музыки:

Теперь запустим метод в другом потоке. Сначала создаётся новый поток. Далее описывается объект Runnable в конструкторе потока. А внутри созданного потока вызываем наш метод play(). И, наконец, запускаем поток.

Читайте также:  Телефонный секретарь маша для андроид

Усыпить поток

Иногда требуется временно приостановить поток («усыпить»):

Приоритет потоков

Для установки приоритета процесса используется метод setPriority(), который вызывается до запуска потока. Значение приоритета может варьироваться от Thread.MIN_PRIORITY (1) до Thread.MAX_PRIORITY (10):

Отмена выполнения потока

У потока есть метод stop(), но использовать его не рекомендуется, поскольку он оставляет приложение в неопределённом состоянии. Обычно используют такой подход:

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

Источник

Многопоточность и асинхронность

Создание потоков и визуальный интерфейс

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

Для создания новых потоков нам доcтупен стандартный функционал класса Thread из базовой библиотеки Java из пакета java.util.concurrent , которые особой трудности не представляют. Тем не менее трудности могут возникнуть при обновлении визуального интерфейса из потока.

Например, создадим простейшее приложение с использованием потоков. Определим следующую разметку интерфейса в файле activity_main.xml :

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

Далее определим в классе MainActivity следующий код:

Итак, здесь к кнопке прикреплен обработчик нажатия, который запускает новый поток. Создавать и запускать поток в Java можно различными способами. В данном случае сами действия, которые выполняются в потоке, определяются в методе run() объекта Runnable :

Для примера получаем текущее время и пытаемся отобразить его в элементе TextView.

Далее определяем объект потока — объект Thread , который принимает объект Runnable. И с помощью метода start() запускаем поток:

Вроде ничего сложного. Но если мы запустим приложение и нажмем на кнопку, то мы столкнемся с ошибкой:

Поскольку изменять состояние визуальных элементов, обращаться к ним мы можем только в основном потоке приложения или UI-потоке.

Для решения этой проблемы — взаимодействия во вторичных потоках с элементами графического интерфейса класс View() определяет метод post() :

В качестве параметра он принимает задачу, которую надо выполнить, и возвращает логическое значение — true , если задача Runnable успешно помещена в очередь сообщение, или false , если не удалось разместить в очереди

Также у класса View есть аналогичный метод:

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

Так, изменим код MainActivity следующим образом

Теперь для обновления TextView применяется метод post:

То есть здесь в методе run() передаемого в метод post() объекта Runnable мы можем обращаться к элементам визуального интерфейса и взаимодействовать с ними.

Подобным образом можно работать и с другими виджетами, которые наследуются от класса View .

Источник

Многопоточность в Android. Все что вам нужно знать. Часть 1 — Введение

13.08.2017 в 11:40

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

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

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

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

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

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

Многозадачность в Android

В Android вы можете классифицировать все компоненты потоков на две основные категории:

Потоки связанные с активностью / фрагментом. Эти потоки привязаны к жизненному циклу активности / фрагмента и завершаются сразу после их уничтожения.

Потоки не связанные с активностью / фрагментом. Эти потоки могут продолжать работать за пределами жизни активности / фрагмента (если есть), из которых они были созданы.

Компоненты многопоточности, которые присоединяются к активности / фрагменту

AsyncTask

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

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

Загрузчики

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

В основном есть два типа загрузчиков: AsyncTaskLoader и CursorLoader . О загрузчике CursorLoader вы узнаете далее в этой статье.

AsyncTaskLoader похож на AsyncTask , но немного сложнее.

Компоненты многопоточности, которые не присоединяются к активности / фрагменту

Service

Service это компонент, который полезен для выполнения длинных (или потенциально длительных) операций без какого-либо пользовательского интерфейса.

Service работает в основном потоке своего процесса; не создает свой собственный поток и не запускается в отдельном процессе, если вы это не указали.

Используя Service вы обязаны остановить его, когда его работа будет завершена, вызвав методы stopSelf() или stopService() .

IntentService

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

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

Источник

Многозадачность в Android. Часть 2 — 7 шаблонов использования многопоточности в Android

31.08.2017 в 21:20

Случай 1. Выполнение запроса по сети, без необходимости ответа от сервера

Иногда вы можете отправить запрос API на сервер, не беспокоясь о его ответе. Например, вы можете отправлять токен устройства для пуш-уведомлений на ваш сервер.

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

Вариант 1. AsyncTask или загрузчики

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

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

Читайте также:  Android button press any key

Вариант 2. Service

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

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

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

Вариант 3. IntentService

Это, на мой взгляд, было бы лучшим вариантом.

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

Случай 2. Выполнение сетевого вызова и получение ответа от сервера

Этот вариант использования, вероятно, более распространен. Например, вы можете вызвать API в фоновом режиме и использовать его ответ для заполнения полей на экране.

Вариант 1. Service или IntentService

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

Вариант 2. AsyncTask или загрузчики

AsyncTask или загрузчики выглядели бы здесь очевидным решением. Они просты в использовании — просты и понятны.

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

Это довольно много работы для каждого вызова. К счастью, теперь доступно гораздо лучшее и простое решение: RxJava.

Вариант 3. RxJava

Возможно вы слышали о библиотеке RxJava, разработанной Netflix. Это почти волшебство на Java.

RxAndroid позволяет использовать RxJava в Android и позволяет работать с асинхронными задачами. Вы можете узнать больше о RxJava на Android здесь. RxJava предоставляет два компонента: Observer и Subscriber .

Наблюдатель — это компонент, который содержит какое-то действие. Он выполняет это действие и возвращает результат, если он удался, ошибка, если он не работает.

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

В RxJava вы сначала создаете наблюдателя:

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

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

Вы связываетесь с наблюдателем этими двумя функциями:

Планировщики — это компоненты, которые выполняют действие в определенном потоке. AndroidSchedulers.mainThread() — это планировщик, связанный с основным потоком.

Учитывая, что наш вызов API — это mRestApi.getData() , и он возвращает объект Data, базовый вызов может выглядеть так:

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

Случай 3. Цепочка сетевых вызовов

Для сетевых вызовов, которые необходимо выполнить последовательно (т.е. когда каждая операция зависит от ответа / результата предыдущей операции), вам нужно быть особенно осторожным при создании спагетти кода.

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

Вариант 1. AsyncTask или загрузчики

Использование AsyncTask или загрузчиков почти наверняка приведет к спагетти коду. Общую функциональность будет трудно реализовать правильно, и для вашего проекта потребуется огромное количество избыточного шаблонного кода.

Вариант 2. RxJava с использованием flatMap

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

Шаг 1. Создайте наблюдателя, который извлекает токен:

Шаг 2. Создайте наблюдателя, который получает данные с помощью токена:

Шаг 3. Цепочка двух наблюдателей вместе и подписка:

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

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

Случай 4. Общение c UI потоком из другого потока

Рассмотрим сценарий, в котором вы хотите загрузить файл и обновить пользовательский интерфейс после его завершения.

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

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

Вариант 1. RxJava внутри сервиса

RxJava, как самостоятельно, так и внутри IntentService , может быть не идеальным решением. Вам нужно будет использовать механизм обратного вызова при подписке на Observable , а IntentService построен для выполнения простых синхронных, а не обратных вызовов.

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

Вариант 2. BroadcastReceiver

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

Для этого вам нужно создать собственный класс, который расширяет BroadcastReceiver , регистрирует его в манифесте и использует Intent и IntentFilter для создания настраиваемого события. Чтобы инициировать событие, вам понадобится метод sendBroadcast .

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

Вариант 3. Использование Handler

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

Читайте также:  Drops android полная версия

Когда Handler создается, он может получить объект Looper в конструкторе, который указывает, к какому потоку он прикреплен. Если вы хотите использовать Handler, прикрепленный к основному потоку, вам нужно использовать looper, связанный с основным потоком, вызывая Looper.getMainLooper() .

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

Этот подход намного лучше, чем первый, но есть еще более простой способ сделать это…

Вариант 3. Использование EventBus

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

Шаг 1. Создайте класс событий. Например, UIEvent .

Шаг 2. Подпишитесь на событие.

Шаг 3. Отправьте событие: EventBus.getDefault (). Post (новый UIEvent ());

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

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

В активности / фрагменте:

Используя библиотеку EventBus , связь между потоками становится намного проще.

Случай 5. Двусторонняя связь между потоками на основе действий пользователя

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

Полный пример медиаплеера выходит за рамки этой статьи. Однако вы можете найти хорошие материалы здесь и здесь.

Вариант 1. Использование EventBus

Здесь вы можете использовать EventBus . Однако, как правило, не безопасно отправлять событие из потока пользовательского интерфейса и получать его в сервисе. Это связано с тем, что вы не можете узнать, работает ли сервис, когда вы отправили сообщение.

Вариант 2. Использование BoundService

BoundService — это служба, связанная с активностью / фрагментом. Это означает, что активность / фрагмент всегда знает, работает служба или нет и получает доступ к публичным методам службы.

Чтобы реализовать его, вам необходимо создать настраиваемый Binder внутри службы и создать метод, который возвращает службу.

Чтобы связать активность со службой, вам необходимо реализовать ServiceConnection , который является классом, контролирующим статус службы, и использовать метод bindService для привязки:

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

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

Когда есть мультимедийное событие, и вы хотите сообщить об этом активности / фрагменту, вы можете использовать один из более ранних подходов (например, BroadcastReceiver, Handler или EventBus).

Случай 6. Выполнение действий параллельно и получение результатов

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

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

Вариант 1: Использование RxJava

В RxJava вы можете комбинировать несколько наблюдателей в одном с помощью операторов merge() или concat() . Затем вы можете подписаться на «merged» наблюдатели и ждать всех результатов.

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

Вариант 2. Использование Java-компонентов

ExecutorService в Java создает фиксированное (настраиваемое) количество потоков и одновременно выполняет задачи на них. Служба возвращает объект Future , который в конечном итоге возвращает все результаты с помощью метода invokeAll() .

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

Получив результаты от invokeAll() , вы можете проверить каждый результат и действовать соответствующим образом.

Скажем, например, что у вас есть три типа данных, поступающих из трех разных конечных точек, и вы хотите сделать три параллельных вызова:

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

Этот подход проще, чем использование RxJava. Это проще, короче и не отменяет все действия из-за одного исключения.

Случай 7. Запрос к локальной SQLite базе данных

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

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

Вариант 1: Использование RxJava

Вы можете использовать RxJava и получать данные из базы данных, так же, как мы получаем данные из нашего back-end:

Вы можете использовать наблюдателя, возвращаемого getLocalDataObservable() следующим образом:

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

Вариант 2: Использование CursorLoader + ContentProvider

Android предоставляет CursorLoader , собственный компонент для загрузки данных из SQLite и управления соответствующим потоком. Это Loader , который возвращает Cursor , который мы можем использовать для получения данных, вызывая простые методы, такие как getString() , getLong() и т.д.

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

Заключение

Android предоставляет множество способов обработки и управления потоками, но ни один из них не является серебрянной пулей.

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

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

Источник

Оцените статью