Потоки приложения в Android

Каждое запускаемое приложение ОС Android содержит несколько потоков, объединённых в Linux-процессе и в виртуальной машине Dalvik или ART с целью управления внутренним выполнением данного приложения. В приложении всегда существуют системные потоки, например, поток пользовательского интерфейса (UI-поток) и связующие потоки, но, кроме того, приложение создаёт свои фоновые потоки.

Все потоки приложений базируются на низкоуровневых потоках pthreads в ядре Linux и их представлении Thread в Java, при этом платформа Android назначает потокам дополнительные, отличительные свойства. С точки зрения приложения существует три типа потоков: UI-поток (поток пользовательского интерфейса), связующие потоки и фоновые потоки.

В приложении конкретные случаи использования UI-потока и прочих рабочих потоков совершенно различны, но с точки зрения ядра Linux это обычные системные потоки, которые обрабатываются одинаково. Все ограничения, присущие UI-потоку, такие как, например, исключительная обязанность выполнять все обновления пользовательского интерфейса, жёстко устанавливаются менеджером окон (Window Manager) в прикладной среде, но не ядром Linux.

Процесс Linux

В Linux каждому пользователю присваивается уникальный идентификатор (user ID) - номер, с помощью которого операционная система отличает пользователей друг от друга. Каждому пользователю разрешён доступ только к собственным ресурсам, и ни один из пользователей (за исключением суперпользователя root) не сможет получить доступ к личным ресурсам другого пользователя. Для изоляции пользователей друг от друга создаются так называемые песочницы (sandboxes). В ОС Android пакет каждого приложения имеет неповторяющийся в системе идентификатор пользователя, то есть приложение Android соответствует отдельному пользователю в Linux и не имеет доступа к ресурсам других приложений.

Посмотрим процессы

phone:/$ ps -A USER PID PPID VSZ RSS WCHAN ADDR S NAME root 1 0 35712 4380 0 0 S init ... root 570 1 4378492 39020 0 0 S zygote64 root 571 1 1690588 17300 0 0 S zygote ... u0_a141 14414 570 4508948 87436 0 0 S ru.linux_doc.app.tasktracker

Здесь виден начальный процесс init, от которого рождаются процессы zygote64 и zygote. Эти процессы работают под пользователем root. А уже от zygote64 был рождён процесс приложения ru.linux_doc.app.tasktracker, который уже работает под своим пользователем u0_a141.

Теперь посмотрим потоки

phone:/$ ps -AT USER PID TID PPID VSZ RSS WCHAN ADDR S CMD root 1 1 0 35712 4380 0 0 S init ... root 570 570 1 4378492 39020 0 0 S main root 570 14418 1 4378492 39020 0 0 S ReferenceQueueD root 570 14419 1 4378492 39020 0 0 S FinalizerDaemon root 570 14420 1 4378492 39020 0 0 S FinalizerWatchd root 570 14421 1 4378492 39020 0 0 S HeapTaskDaemon root 571 571 1 1690588 17300 0 0 S main root 571 13878 1 1690588 17300 0 0 S ReferenceQueueD root 571 13879 1 1690588 17300 0 0 S FinalizerDaemon root 571 13880 1 1690588 17300 0 0 S FinalizerWatchd root 571 13881 1 1690588 17300 0 0 S HeapTaskDaemon ... u0_a141 14414 14414 570 4506756 86504 0 0 S app.tasktracker u0_a141 14414 14422 570 4506756 86504 0 0 S Jit thread pool u0_a141 14414 14423 570 4506756 86504 0 0 S Signal Catcher u0_a141 14414 14424 570 4506756 86504 0 0 S ADB-JDWP Connec u0_a141 14414 14425 570 4506756 86504 0 0 S ReferenceQueueD u0_a141 14414 14426 570 4506756 86504 0 0 S FinalizerDaemon u0_a141 14414 14427 570 4506756 86504 0 0 S FinalizerWatchd u0_a141 14414 14428 570 4506756 86504 0 0 S HeapTaskDaemon u0_a141 14414 14429 570 4506756 86504 0 0 S Binder:14414_1 u0_a141 14414 14430 570 4506756 86504 0 0 S Binder:14414_2 u0_a141 14414 14431 570 4506756 86504 0 0 S Binder:14414_3 u0_a141 14414 14445 570 4506756 86504 0 0 S Profile Saver u0_a141 14414 14447 570 4506756 86504 0 0 S Binder:14414_4 u0_a141 14414 14448 570 4506756 86504 0 0 S Thread-3 u0_a141 14414 14450 570 4506756 86504 0 0 S Thread-4 u0_a141 14414 14453 570 4506756 86504 0 0 S HwBinder:14414_ u0_a141 14414 14454 570 4506756 86504 0 0 S RenderThread u0_a141 14414 14458 570 4506756 86504 0 0 S mali-mem-purge u0_a141 14414 14459 570 4506756 86504 0 0 S mali-utility-wo u0_a141 14414 14460 570 4506756 86504 0 0 S mali-utility-wo u0_a141 14414 14461 570 4506756 86504 0 0 S mali-utility-wo u0_a141 14414 14462 570 4506756 86504 0 0 S mali-utility-wo u0_a141 14414 14463 570 4506756 86504 0 0 S mali-utility-wo u0_a141 14414 14464 570 4506756 86504 0 0 S mali-utility-wo u0_a141 14414 14465 570 4506756 86504 0 0 S mali-utility-wo u0_a141 14414 14466 570 4506756 86504 0 0 S mali-utility-wo u0_a141 14414 14467 570 4506756 86504 0 0 S mali-cmar-backe u0_a141 14414 14468 570 4506756 86504 0 0 S mali-hist-dump u0_a141 14414 14481 570 4506756 86504 0 0 S queued-work-loo

Как тут видно в таблицу добавилось ещё одно поле с идентификатором потока TID. Кроме того у приложения оказалось аж 29 потоков. Причём первый поток приложения, у которого идентификатор потока равен идентификатору процесса, является UI-потоком, от которого потом рождаются все остальные потоки.

Запуск приложения

Приложение запускается, когда один из его компонентов инициализируется для выполнения. Любой компонент может быть точкой входа в приложение, и как только первый компонент активируется, создаётся процесс Linux - если он уже не находился в рабочем состоянии, - и выполняется следующая процедура запуска:

  1. Запускается процесс Linux
  2. Создаётся среда выполнения
  3. Создаётся экземпляр класса Application
  4. Создаётся компонент точки входа в приложение

Настройка нового процесса Linux и среды выполнения являются достаточно длительными операциями. Они могут ухудшить производительность и принести немало отрицательных эмоций пользователю. Поэтому система пытается сократить время запуска приложений Android, активируя особый процесс с именем Zygote во время загрузки самой системы. Zygote содержит полный набор заранее загруженных системных библиотек ядра. Новые процессы создаются (fork) как клоны процесса Zygote, без копирования системных библиотек, которые совместно используются всеми приложениями.

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


java.lang.Thread - класс потока
java.lang.Runnable - интерфейс задачи

Далее

Главный программный поток
Синхронизация потоков
Передача сообщений (Handler, Looper, MessageQueue, Message)
Архитектура Android