- Многопоточность в Android. Все что вам нужно знать. Часть 1 — Введение
- Многозадачность в Android
- Компоненты многопоточности, которые присоединяются к активности / фрагменту
- AsyncTask
- Загрузчики
- Компоненты многопоточности, которые не присоединяются к активности / фрагменту
- Service
- IntentService
- Difference between Main thread and UI thread in android
- A journey on the Android Main Thread — Lifecycle bits
- Activities love orientation changes
- Why it matters
- A refresher on orientation changes
- Orientation changes and the main thread
- Tying it all together
- What could you do?
- The real fix
- If you have a good reason to post
- If you really need that activity reference
- If you want to get fired
- A word on runOnUiThread()
- Services
- IntentService
- Conclusion
- Android UI thread
- Рассмотрим взаимодействие системы Android с компонентами приложения.
Многопоточность в 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 обычно используется для коротких задач, которые не обязательно должны быть привязаны к какому-либо пользовательскому интерфейсу.
Источник
Difference between Main thread and UI thread in android
If you finding this question on google, document’s android or somewhere, you always view this quote:
Ordinarily, an app’s main thread is also the UI thread. However, under special circumstances, an app’s main thread might not be its UI thread;
The question here is when special circumstances take place? So I’d search and this is a short answer:
@MainThread is the first thread that starts running when you start your application
@UiThread starts from Main Thread for Rendering user Interface
Note: The @MainThread and the @UiThread annotations are interchangeable so methods calls from either thread type are allowed for these annotations.
Still not clear, so I find more and more and have the clearest answer. Turns out, UI and Main threads are not necessarily the same.
Whenever a new application started, public static void main(String[]) method of ActivityThread is being executed. The «main» thread is being initialized there, and all calls to Activity lifecycle methods are being made from that exact thread. In Activity#attach() method (its source was shown above) the system initializes «ui» thread to «this» thread, which is also happens to be the «main» thread. Therefore, for all practical cases «main» thread and «ui» thread are the same.
This is true for all applications, with one exception.
When Android framework is being started for the first time, it too runs as an application, but this application is special (for example: has privileged access). Part of this “specialty” is that it needs a specially configured “main” thread. Since it has already ran through public static void main(String[]) method (just like any other app), its «main» and «ui» threads are being set to the same thread. In order to get «main» thread with special characteristics, system app performs a static call to public static ActivityThread systemMain() and stores the obtained reference. But its «ui» thread is not overridden, therefore «main» and «ui» threads end up being not the same.
However, as stated in the documentation, the distinction is important only in context of some system applications (applications that run as part of OS). Therefore, as long as you don’t build a custom ROM or work on customizing Android for phone manufacturers, I wouldn’t bother to make any distinction at all.
Источник
A journey on the Android Main Thread — Lifecycle bits
Heads up, we’ve moved! If you’d like to continue keeping up with the latest technical content from Square please visit us at our new home https://developer.squareup.com/blog
In the previous part we took a dive into loopers and handlers and how they relate to the Android main thread.
Today, we will take a closer look at how the main thread interacts with the Android components lifecycle.
Activities love orientation changes
Let’s start with the activity lifecycle and the magic behind the handling of configuration changes.
Why it matters
This article was inspired by a real crash that occurred in Square Register.
A simplified version of the code is:
As we will see, doSomething() can be called after the activity onDestroy()method has been called due to a configuration change. At that point, you should not use the activity instance anymore.
A refresher on orientation changes
The device orientation can change at any time. We will simulate an orientation change while the activity is being created using Activity#setRequestedOrientation(int).
Can you predict the log output when starting this activity in portrait?
If you know the Android lifecycle, you probably predicted this:
The Android Lifecycle goes on normally, the activity is created, resumed, and then the orientation change is taken into account and the activity is paused, destroyed, and a new activity is created and resumed.
Orientation changes and the main thread
Here is an important detail to remember: an orientation change leads to recreating the activity via a simple post of a message to the main thread looper queue.
Let’s look at that by writing a spy that will read the content of the looper queue via reflection:
As you can see, the message queue is merely a linked list where each message has a reference to the next message.
We log the content of the queue right after the orientation change:
Here is the output:
A quick look at the ActivityThread class tells us what those 118 and 126 messages are:
Requesting an orientation change added CONFIGURATION_CHANGED and a RELAUNCH_ACTIVITY message to the main thread looper queue.
Let’s take a step back and think about what’s going on:
When the activity starts for the first time, the queue is empty. The message currently being executed is LAUNCH_ACTIVITY, which creates the activity instance, calls onCreate() and then onResume() in a row. Then only the main looper processes the next message in the queue.
When a device orientation change is detected, a RELAUNCH_ACTIVITY is posted to the queue.
When that message is processed, it:
- calls onSaveInstanceState(), onPause(), onDestroy() on the old activity instance,
- creates a new activity instance,
- calls onCreate() and onResume() on that new activity instance.
All that in one message handling. Any message you post in the meantime will be handled after onResume() has been called.
Tying it all together
What could happen if you post to a handler in onCreate() during an orientation change? Let’s look at the two cases, right before and right after the orientation change:
Here is the output:
To sum things up: at the end on onCreate(), the queue contained four messages. The first was the post before the orientation change, then the two messages related to the orientation change, and then only the post after the orientation change. The logs show that these were executed in order.
Therefore, any message posted before the orientation change will be handled before onPause() of the leaving activity, and any message posted after the orientation change will be handled after onResume() of the incoming activity.
The practical implication is that when you post a message, you have no guarantee that the activity instance that existed at the time it was sent will still be running when the message is handled (even if you post from onCreate() or onResume()). If your message holds a reference to a view or an activity, the activity won’t be garbage collected until the message is handled.
What could you do?
The real fix
Stop calling handler.post() when you are already on the main thread. In most cases, handler.post() is used as a quick fix to ordering problems. Fix your architecture instead of messing it up with random handler.post() calls.
If you have a good reason to post
Make sure your message does not hold a reference to an activity, as you would do for a background operation.
If you really need that activity reference
Remove the message from the queue with handler.removeCallbacks() in the activity onPause().
If you want to get fired
Use handler.postAtFrontOfQueue() to make sure a message posted before onPause() is always handled before onPause(). Your code will become really hard to read and understand. Seriously, don’t.
A word on runOnUiThread()
Did you notice that we created a handler and used handler.post() instead of directly calling Activity.runOnUiThread()?
Unlike handler.post(), runOnUiThread() does not post the runnable if the current thread is already the main thread. Instead, it calls run() synchronously.
Services
There is a common misconception that needs to die: a service does not run on a background thread.
All service lifecycle methods (onCreate(), onStartCommand(), etc) run on the main thread (the very same thread that’s used to play funky animations in your activities).
Whether you are in a service or an activity, long tasks must be executed in a dedicated background thread. This background thread can live as long as the process of your app lives, even when your activities are long gone.
However, at any time the Android system can decide to kill the app process. A service is a way to ask the system to let us live if possible and be polite by letting the service know before killing the process.
Side note: When an IBinder returned from onBind() receives a call from another process, the method will be executed in a background thread.
Take the time to read the Service documentation — it’s pretty good.
IntentService
IntentService provides a simple way to serially process a queue of intents on a background thread.
Internally, it uses a Looper to handle the intents on a dedicated HandlerThread. When the service is destroyed, the looper lets you finish handling the current intent, and then the background thread terminates.
Conclusion
Most Android lifecycle methods are called on the main thread. Think of these callbacks as simple messages sent to a looper queue.
This article wouldn’t be complete without the reminder that goes into almost every Android dev article: Do not block the main thread.
Have you ever wondered what blocking the main thread actually means? That’s the subject of the next part!
Источник
Android UI thread
Большая часть кода Android приложения работает в контексте компонент, таких как Activity, Service, ContentProvider или BroadcastReceiver. Рассмотрим, как в системе Android организованно взаимодействие этих компонент с потоками.
При запуске приложения система выполняет ряд операций: создаёт процесс ОС с именем, совпадающим с наименованием пакета приложения, присваивает созданному процессу уникальный идентификатор пользователя, который по сути является именем пользователя в ОС Linux. Затем система запускает Dalvik VM где создаётся главный поток приложения, называемый также «поток пользовательского интерфейса (UI thread)». В этом потоке выполняются все четыре компонента Android приложения: Activity, Service, ContentProvider, BroadcastReceiver. Выполнение кода в потоке пользовательского интерфейса организованно посредством «цикла обработки событий» и очереди сообщений.
Рассмотрим взаимодействие системы Android с компонентами приложения.
Activity. Когда пользователь выбирает пункт меню или нажимает на экранную кнопку, система оформит это действие как сообщение (Message) и поместит его в очередь потока пользовательского интерфейса (UI thread).
Service. Исходя из наименования, многие ошибочно полагают, что служба (Service) работает в отдельном потоке (Thread). На самом деле, служба работает так же, как Activity в потоке пользовательского интерфейса. При запуске локальной службы командой startService, новое сообщение помещается в очередь основного потока, который выпонит код сервиса.
BroadcastReceiver. При создании широковещательного сообщения система помещает его в очередь главного потока приложения. Главный поток позднее загрузит код BroadcastReceiver который зарегистрирован для данного типа сообщения, и начнёт его выполнение.
ContentProvider. Вызов локального ContentProvider немного отличается. ContentProvider также выполняется в основном потоке но его вызов является синхронным и для запуска кода ContentProvider не использует очередь сообщений.
Исходя из вышесказанного можно заметить, что если главный поток в данный момент обрабатывает пользовательский ввод или выполняет иное действие, выполнение кода, полученного в новом сообщении, начнётся только после завершения текущей операции. Если какая либо операция в одном из компонентов потребует значительного времени выполнения, пользователь столкнётся или с анимацией с рывками, или с неотзывчивыми элементами интерфейса или с сообщением системы «Приложение не отвечает» (ANR).
Для решения данной проблемы используется парадигма параллельного программирования. В Java для её реализации используется понятие потока выполнения (Thread).
Thread: поток, поток выполнения, иногда ещё упоминается как нить, можно рассматривать как отдельную задачу, в которой выполняется независимый набор инструкций. Если в вашей системе только один процессор то потоки выполняются поочередно (но быстрое переключение системы между ними создает впечатление параллельной или одновременной работы). На диаграмме показано приложение, которое имеет три потока выполнения:
Но, к сожалению, для взаимодействия с пользователем, от потока мало пользы. На самом деле, если вы внимательно посмотрите на диаграмму выше, вы поймёте, что как только поток выполнить все входящие в него инструкции он останавливается и перестаёт отслеживать действия пользователя. Чтобы избежать этого, нужно в наборе инструкций реализовать бесконечный цикл. Но возникает проблема как выполнить некое действие, например отобразить что-то на экране из другого потока, иными словами как вклиниться в бесконечный цикл. Для этого в Android можно использовать Android Message System. Она состоит из следующих частей:
Looper: который ещё иногда ещё называют «цикл обработки событий» используется для реализации бесконечного цикла который может получать задания используется. Класс Looper позволяет подготовить Thread для обработки повторяющихся действий. Такой Thread, как показано на рисунке ниже, часто называют Looper Thread. Главный поток Android на самом деле Looper Thread. Looper уникальны для каждого потока, это реализованно в виде шаблона проектирования TLS или Thread Local Storage (любопытные могут посмотреть на класс ThreadLocal в Java документации или Android).
Message: сообщение представляет собой контейнер для набора инструкций которые будут выполнены в другом потоке.
Handler: данный класс обеспечивает взаимодействие с Looper Thread. Именно с помощью Handler можно будет отправить Message с реализованным Runnable в Looper, которая будет выполнена (сразу или в заданное время) потоком с которым связан Handler. Код ниже иллюстрирует использование Handler. Этот код создаёт Activity которая завершиться через определённый период времени.
HandlerThread: написание кода потока реализующего Looper может оказаться не простой задачей, чтобы не повторять одни и те же ошибки система Android включает в себя класс HandlerThread. Вопреки названию этот класс не занимается связью Handler и Looper.
Практическую реализацию данного подхода можно изучить на примере кода класса IntentService, данный класс хорошо подходит для выполнения асинхронных сетевых или иных запросов, так как он может принимать задания одно за другим, не дожидаясь полной обработки текущего, и завершает свою работу самостоятельно.
Выполнение операций в отдельном потоке, не означает, что вы можете делать все что угодно, не влияя на производительность системы. Никогда не забывайте, что написанный вами код работает на машинах, как правило, не очень мощных. Поэтому всегда стоит использовать возможности предоставляемые системой для оптимизации.
Подготовлено на основе материалов AndroidDevBlog
Источник