page.title=Процессы и потоки page.tags=жизненный цикл,фон @jd:body
Когда компонент приложения запускается при отсутствии других работающих компонентов , система Android запускает новый процесс Linux для приложения с одним потоком выполнения. По умолчанию все компоненты одного приложения работают в одном процессе и потоке (называется «главным потоком»). Если компонент приложения запускается при наличии процесса для этого приложения (так как существует другой компонент из приложения), тогда компонент запускается в этом процессе и использует тот же поток выполнения. Однако можно организовать выполнение других компонентов приложения в отдельных процессах и создавать дополнительный поток для любого процесса.
В этом документе обсуждается работа процессов и потоков в приложении Android.
По умолчанию все компоненты одного приложения работают в одном процессе, и большинство приложений не должно менять это поведение. Однако, если необходимо контролировать, к какому процессу принадлежат определенный компонент, можно сделать это в файле манифеста.
Запись манифеста для каждого типа элементов компонента —{@code <activity>}, {@code <service>}, {@code <receiver>} и {@code <provider>} —поддерживает атрибут {@code android:process}, позволяющий задавать процесс, в котором следует выполнять этот компонент. Можно установить этот атрибут так, чтобы каждый компонент выполнялся в собственном процессе, или так, чтобы только некоторые компоненты совместно использовали один процесс. Можно также настроить процесс {@code android:process} так, чтобы компоненты разных приложений выполнялись в одном процессе, при условии что приложения совместно используют один идентификатор пользователя Linux и выполняют вход с одним сертификатом.
Элемент {@code <application>} также поддерживает атрибут {@code android:process}, позволяющий задать значение по умолчанию, которое применяется ко всем компонентам.
Android может остановить процесс в некоторой точке, когда не хватает памяти и она необходима другим процессам, которые обслуживают пользователя в данный момент. Работа компонентов приложения, работающих в этом процессе, последовательно останавливается. Процесс для этих компонентов запускается повторно, когда для них появляется работа.
Принимая решение о прерывании процессов, система Android взвешивает их относительную важность для пользователя. Например, более вероятно выключение процессов, содержащих действия, которые не отображаются на экране, по сравнению с процессом, содержащим видимые действия. Следовательно, решение о прерывании процесса зависит от состояния компонентов, работающих в этом процессе. Ниже обсуждаются правила, на основании которых принимается решение о выборе прерываемых процессов.
Система Android пытается сохранять процесс приложения как можно дольше, но в конечном счете вынуждена удалять старые процессы, чтобы восстановить память для новых или более важных процессов. Чтобы определить, какие процессы сохранить, а какие удалить, система помещает каждый процесс в «иерархию важности» на основе компонентов, выполняющихся в процессе, и состояния этих компонентов. Процессы с самым низким уровнем важности исключаются в первую очередь, затем исключаются процессы следующего уровня важности и т. д., насколько это необходимо для восстановления ресурсов системы.
В иерархии важности предусмотрено пять уровней. В следующем списке представлены различные типы процессов в порядке важности (первый процесс является наиболее важным и удаляется в последнюю очередь):
Процесс, необходимый для текущей деятельности пользователя. Процесс считается процессом переднего плана, если выполняется любое из следующих условий:
Обычно одновременно работает лишь несколько процессов переднего плана. Они уничтожаются только в крайнем случае, если памяти остается так мало, что они не могут продолжать совместную работу. Обычно в этот момент устройство достигло состояния разбиения памяти на страницы, поэтому для того, чтобы пользовательский интерфейс откликался на действия пользователя, необходимо удаление некоторых процессов на переднем плане.
Процессы, которые не содержат компонентов переднего плана, но могут влиять на отображение на экране. Процесс считается видимым, если выполняется любое из следующих условий:
Видимый процесс считается исключительно важным, его следует удалять только в случае, если требуется сохранить работу всех процессов переднего плана.
Процесс, который выполняет службу, запущенную с помощью метода {@link android.content.Context#startService startService()}, и не попадает ни в одну из двух категорий более высокого уровня. Хотя служебные процессы не связаны непосредственно с тем, что видит пользователь, они обычно выполняют важные для пользователя действия (например, воспроизводят музыку в фоновом режиме или загружают данные в сеть), поэтому система сохраняет их выполнение, если памяти достаточно для их работы наряду со всеми видимыми процессами и процессами переднего плана.
Процесс, содержащий действия, которые не видны пользователю в настоящее время (вызван метод {@link android.app.Activity#onStop onStop()} действия). Эти процессы не оказывают непосредственного воздействия на работу пользователя, и система может удалить их в любой момент, чтобы освободить память для процессов переднего плана, видимых или служебных процессов. Обычно выполняется множество фоновых процессов, поэтому они хранятся в списке LRU (недавно использованные), чтобы процессы, содержащие самые недавние действия, которые видел пользователь, удалялись в последнюю очередь. Если для действия правильно реализованы методы жизненного цикла, и действие сохраняет текущее состояние, удаление процесса этого действия не оказывает видимого воздействия на работу пользователя, так как когда пользователь возвращается к этому действию, оно восстанавливает все элементы своего видимого состояния. Информацию о сохранении и восстановлении состояния см. в документе Действия .
Процесс, не содержащий никаких компонентов активного приложения. Единственная причина сохранять процесс такого типа — это кэширование, которое улучшает время следующего запуска компонента в этом процессе. Система часто удаляет эти процессы для равномерного распределения всех системных ресурсов между кэшем процесса и кэшем базового ядра.
Система Android относит процесс к максимально высокому уровню на основе важности компонентов, активных в процессе в текущее время. Например, если процесс содержит служебное и видимое действие, процесс считается видимым, а не служебным процессом.
Кроме того, уровень процесса может быть повышен, поскольку имеются другие процессы, зависимые от него. Например, процесс, обслуживающий другой процесс, не может иметь уровень ниже уровня обслуживаемого процесса. Например, если поставщик контента в процессе A обслуживает клиента в процессе B или служебный процесс A связан с компонентом в процессе B, процесс A всегда считается не менее важным, чем процесс B.
Так как процесс, выполняющий службу, оценивается выше процесса с фоновыми действиям, действие, запускающее долговременную операцию, может запустить службу для этой операции, а не просто создать рабочий поток, особенно в случае, если операция продлится дольше действия. Например, действие, которое загружает изображение на веб-сайт, должно запустить службу для выполнения загрузки, так что загрузка может продолжаться в фоновом режиме даже после выхода пользователя из действия. Использование службы гарантирует, что операция будет иметь приоритет не ниже «служебного процесса», независимо от того, что происходит с действием. По этой же причине ресиверы должны использовать службы, а не просто ставить в поток операции, требующие много времени для выполнения.
При запуске приложения система создает поток выполнения для приложения, который называется «главным». Этот поток очень важен, так как он отвечает за диспетчеризацию событий на виджеты соответствующего интерфейса пользователя, включая события графического представления. Он также является потоком, в котором приложение взаимодействует с компонентами из набора инструментов пользовательского интерфейса Android (компонентами из пакетов {@link android.widget} и {@link android.view}). По существу, главный поток — это то, что иногда называют потоком пользовательского интерфейса.
Система не создает отдельного потока для каждого экземпляра компонента. Все компоненты, которые выполняются в одном процессе, создают экземпляры в потоке пользовательского интерфейса, и системные вызовы каждого компонента отправляются из этого потока. Поэтому методы, которые отвечают на системные обратные вызовы (такие как метод {@link android.view.View#onKeyDown onKeyDown()} для сообщения о действиях пользователя или метод обратного вызова жизненного цикла), всегда выполняются в потоке пользовательского интерфейса процесса.
Например, когда пользователь нажимает кнопку на экране, поток пользовательского интерфейса вашего приложения отправляет событие нажатия в виджет, который, в свою очередь, устанавливает кнопку в нажатое состояние и отправляет запрос на аннулирование в очередь событий. Поток пользовательского интерфейса исключает запрос из очереди и уведомляет виджет, что он должен отобразиться повторно.
Когда приложение выполняет интенсивную работу в ответ на действия пользователя, эта одиночная модель потока может показывать плохую производительность, если приложение реализовано неправильно. То есть, если все происходит в потоке пользовательского интерфейса, выполнение долговременных операций, таких как сетевой доступ или запросы к базе данных, будет блокировать весь пользовательский интерфейс. Когда поток заблокирован, не могут обрабатываться никакие события, включая события изменения отображения. С точки зрения пользователя приложение выглядит зависшим. Хуже того, если поток пользовательского интерфейса заблокирован более нескольких секунд (в настоящее время около 5 секунд), отображается печально известное диалоговое окно «приложение не отвечает». После этого недовольный пользователь может выйти из вашего приложения и удалить его.
Кроме того, набор инструментов пользовательского интерфейса Andoid не является потокобезопасным. Поэтому, вы не должны работать с пользовательским интерфейсом из рабочего потока. Манипуляции с пользовательским интерфейсом необходимо выполнять из потока пользовательского интерфейса. Таким образом, существует только два правила однопоточной модели Android:
Вследствие описанной выше однопоточной модели для динамичности пользовательского интерфейса ваших приложений очень важно не блокировать поток пользовательского интерфейса. Если требуется выполнять операции, занимающие некоторое время, обязательно выполняйте их в отдельных потоках (»фоновых» или «рабочих» потоках).
Например, ниже приведен код контроля нажатий, который загружает изображение из отдельного потока и отображает их в виджете {@link android.widget.ImageView}:
public void onClick(View v) { new Thread(new Runnable() { public void run() { Bitmap b = loadImageFromNetwork("http://example.com/image.png"); mImageView.setImageBitmap(b); } }).start(); }
На первый взгляд, он должен работать хорошо, так как он создает новый поток для обработки сетевой операции. Однако, он нарушает второе правило однопоточной модели: не обращайтесь к набору инструментов пользовательского интерфейса Android снаружи потока пользовательского интерфейса — этот пример изменяет {@link android.widget.ImageView} из рабочего потока, а не из потока пользовательского интерфейса. Это может привести к неопределенному и непредвиденному поведению, отследить которое будет трудно.
Для устранения этой проблемы Android предлагает несколько путей доступа к потоку пользовательского интерфейса из других потоков. Ниже приведен список полезных методов:
Например, можно исправить приведенный выше код с помощью метода {@link android.view.View#post(java.lang.Runnable) View.post(Runnable)}:
public void onClick(View v) { new Thread(new Runnable() { public void run() { final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png"); mImageView.post(new Runnable() { public void run() { mImageView.setImageBitmap(bitmap); } }); } }).start(); }
Теперь реализация является потокобезопасной: сетевая операция выполняется из отдельного потока, тогда как {@link android.widget.ImageView} работает из потока пользовательского интерфейса.
Однако по мере роста сложности, код такого типа может становиться запутанным и сложным для поддержания. Чтобы обрабатывать более сложные взаимодействия с рабочим потоком, можно использовать метод {@link android.os.Handler} в рабочем потоке для обработки сообщений, поступающих из потока пользовательского интерфейса. Вероятно, самым лучшим решением является расширение класса {@link android.os.AsyncTask}, которое упрощает выполнение заданий рабочего потока, которые должны взаимодействовать с пользовательским интерфейсом.
Метод {@link android.os.AsyncTask} позволяет выполнять асинхронную работу в пользовательском интерфейсе. Он выполняет операции блокирования в рабочем потоке и затем публикует результаты в потоке пользовательского интерфейса без необходимости самостоятельно обрабатывать потоки и/или обработчики.
Для использования этого метода необходимо создать подкласс {@link android.os.AsyncTask} и реализовать метод обратного вызова {@link android.os.AsyncTask#doInBackground doInBackground()}, который работает в пуле фоновых потоков. Чтобы обновить пользовательский интерфейс, следует реализовать метод {@link android.os.AsyncTask#onPostExecute onPostExecute()}, который доставляет результат из {@link android.os.AsyncTask#doInBackground doInBackground()} и работает в потоке пользовательского интерфейса, так что вы можете безопасно обновлять пользовательский интерфейс. Задача выполняется через вызов метода {@link android.os.AsyncTask#execute execute()} из потока пользовательского интерфейса.
Например, можно реализовать предыдущий пример с помощью метода {@link android.os.AsyncTask} следующим образом:
public void onClick(View v) { new DownloadImageTask().execute("http://example.com/image.png"); } private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> { /** The system calls this to perform work in a worker thread and * delivers it the parameters given to AsyncTask.execute() */ protected Bitmap doInBackground(String... urls) { return loadImageFromNetwork(urls[0]); } /** The system calls this to perform work in the UI thread and delivers * the result from doInBackground() */ protected void onPostExecute(Bitmap result) { mImageView.setImageBitmap(result); } }
Теперь пользовательский интерфейс защищен и код стал проще, так как работа разделена на часть, которая должна выполняться в рабочем потоке, и часть, которая должна выполняться в потоке пользовательского интерфейса.
Прочитайте статью {@link android.os.AsyncTask}, чтобы полностью понять использование этого класса. Здесь приведен краткий обзор его работы:
Предупреждение! Другая проблема, с которой вы можете столкнуться при использовании рабочего потока, состоит в непредсказуемом перезапуске действия вследствие изменения конфигурации в режиме выполнения, (например, когда пользователь изменяет ориентацию экрана), что может разрушить рабочий поток. Чтобы увидеть, как можно сохранить задание во время одного из подобных перезапусков и как правильно отменить задание при разрушении действия, изучите исходный код примера приложения Shelves.
В некоторых ситуациях реализованные методы могут вызываться из нескольких потоков и, следовательно, должны быть написаны с сохранением потокобезопасности.
В первую очередь это относится к методам, которые можно вызывать удаленно, например, к методам в связанной службе. Когда вызов метода реализуется в классе {@link android.os.IBinder}, происходящем из того же процесса, в котором выполняется {@link android.os.IBinder IBinder}, метод выполняется в потоке вызывающего метода. Однако, когда вызов происходит из другого процесса, метод выполняется в потоке, выбранном из пула потоков, которые система поддерживает в том же процессе, что и {@link android.os.IBinder IBinder} (он не выполняется в потоке пользовательского интерфейса процесса). Например, поскольку метод {@link android.app.Service#onBind onBind()} службы будет вызываться из потока пользовательского интерфейса процесса службы, методы, реализованные в объекте, который возвращает {@link android.app.Service#onBind onBind()} (например, подкласс, который реализует методы RPC), будут вызываться из потоков в пуле. Так как служба может иметь несколько клиентов, несколько потоков из пула могут одновременно использовать один и тот же метод {@link android.os.IBinder IBinder}. Поэтому методы {@link android.os.IBinder IBinder} должны быть реализованы с сохранением потокобезопасности.
Аналогичным образом поставщик контента может получать запросы данных, которые происходят из другого процесса. Хотя классы {@link android.content.ContentResolver} и {@link android.content.ContentProvider} скрывают подробности управления взаимодействием процессов, методы {@link android.content.ContentProvider}, которые отвечают на эти запросы, —методы {@link android.content.ContentProvider#query query()}, {@link android.content.ContentProvider#insert insert()}, {@link android.content.ContentProvider#delete delete()}, {@link android.content.ContentProvider#update update()} и {@link android.content.ContentProvider#getType getType()} —вызываются из пула потоков в процессе поставщика контента, а не в процессе потока пользовательского интерфейса. Поскольку эти методы могут вызываться из любого числа потоков одновременно, они также должны быть реализованы с сохранением потокобезопасности.
Система Android предлагает механизм взаимодействия процессов (IPC) с помощью удаленного вызова процедуры (RPC), при котором метод вызывается действием или другим компонентом приложения, но выполняется удаленно (в другом процессе) с возвратом всех результатов вызывающему компоненту. Это влечет разложение вызова метода и его данных до уровня, понятного операционной системе, передачу его из локального процесса и адресного пространства удаленному процессу и адресному пространству, а затем повторную сборку и восстановление вызова. После этого возвращенные значения передаются в обратном направлении. Система Android содержит все коды для выполнения этих механизмов IPC, так что вы можете сосредоточиться на определении и реализации программного интерфейса RPC.
Для выполнения IPC приложение должно быть привязано к службе с помощью метода {@link android.content.Context#bindService bindService()}. Дополнительные сведения представлены в разделе Службы руководства для разработчиков.