blob: c9b7dbe62a360bbb3bd5372c7806be7362f29aa7 [file] [log] [blame]
page.title=Процессы и потоки
page.tags=жизненный цикл,фон
@jd:body
<div id="qv-wrapper">
<div id="qv">
<h2>Содержание документа</h2>
<ol>
<li><a href="#Processes">Процессы</a>
<ol>
<li><a href="#Lifecycle">Жизненный цикл процесса</a></li>
</ol>
</li>
<li><a href="#Threads">Потоки</a>
<ol>
<li><a href="#WorkerThreads">Рабочие потоки</a></li>
<li><a href="#ThreadSafe">Потокобезопасные методы</a></li>
</ol>
</li>
<li><a href="#IPC">Взаимодействие процессов</a></li>
</ol>
</div>
</div>
<p>Когда компонент приложения запускается при отсутствии других работающих компонентов
, система Android запускает новый процесс Linux для приложения с одним потоком
выполнения. По умолчанию все компоненты одного приложения работают в одном процессе и потоке
(называется «главным потоком»). Если компонент приложения запускается при наличии процесса
для этого приложения (так как существует другой компонент из приложения), тогда компонент
запускается в этом процессе и использует тот же поток выполнения. Однако можно организовать выполнение
других компонентов приложения в отдельных процессах и создавать дополнительный
поток для любого процесса.</p>
<p>В этом документе обсуждается работа процессов и потоков в приложении Android.</p>
<h2 id="Processes">Процессы</h2>
<p>По умолчанию все компоненты одного приложения работают в одном процессе, и большинство приложений
не должно менять это поведение. Однако, если необходимо контролировать, к какому процессу принадлежат определенный
компонент, можно сделать это в файле манифеста.</p>
<p>Запись манифеста для каждого типа элементов компонента &mdash;<a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code
&lt;activity&gt;}</a>, <a href="{@docRoot}guide/topics/manifest/service-element.html">{@code
&lt;service&gt;}</a>, <a href="{@docRoot}guide/topics/manifest/receiver-element.html">{@code
&lt;receiver&gt;}</a> и <a href="{@docRoot}guide/topics/manifest/provider-element.html">{@code
&lt;provider&gt;}</a> &mdash;поддерживает атрибут {@code android:process}, позволяющий задавать
процесс, в котором следует выполнять этот компонент. Можно установить этот атрибут так, чтобы каждый компонент выполнялся
в собственном процессе, или так, чтобы только некоторые компоненты совместно использовали один процесс. Можно также настроить процесс
{@code android:process} так, чтобы компоненты разных приложений выполнялись в одном
процессе, при условии что приложения совместно используют один идентификатор пользователя Linux и выполняют вход с
одним сертификатом.</p>
<p>Элемент <a href="{@docRoot}guide/topics/manifest/application-element.html">{@code
&lt;application&gt;}</a> также поддерживает атрибут {@code android:process}, позволяющий задать
значение по умолчанию, которое применяется ко всем компонентам.</p>
<p>Android может остановить процесс в некоторой точке, когда не хватает памяти и она необходима другим
процессам, которые обслуживают пользователя в данный момент. Работа компонентов
приложения, работающих в этом процессе, последовательно останавливается. Процесс для этих компонентов запускается
повторно, когда для них появляется работа.</p>
<p>Принимая решение о прерывании процессов, система Android взвешивает их относительную важность
для пользователя. Например, более вероятно выключение процессов, содержащих действия, которые
не отображаются на экране, по сравнению с процессом, содержащим видимые действия. Следовательно, решение
о прерывании процесса зависит от состояния компонентов, работающих в этом процессе. Ниже обсуждаются правила,
на основании которых принимается решение о выборе прерываемых процессов. </p>
<h3 id="Lifecycle">Жизненный цикл процесса</h3>
<p>Система Android пытается сохранять процесс приложения как можно дольше, но
в конечном счете вынуждена удалять старые процессы, чтобы восстановить память для новых или более важных процессов. Чтобы
определить, какие процессы сохранить,
а какие удалить, система помещает каждый процесс в «иерархию важности» на основе
компонентов, выполняющихся в процессе, и состояния этих компонентов. Процессы с самым низким уровнем
важности исключаются в первую очередь, затем исключаются процессы следующего уровня важности и т. д., насколько это необходимо
для восстановления ресурсов системы.</p>
<p>В иерархии важности предусмотрено пять уровней. В следующем списке представлены различные
типы процессов в порядке важности (первый процесс является <em>наиболее важным</em> и
<em>удаляется в последнюю очередь</em>):</p>
<ol>
<li><b>Процесс переднего плана</b>
<p>Процесс, необходимый для текущей деятельности пользователя. Процесс
считается процессом переднего плана, если выполняется любое из следующих условий:</p>
<ul>
<li>Он содержит действие {@link android.app.Activity}, с которым взаимодействует пользователь (вызван метод {@link
android.app.Activity}
{@link android.app.Activity#onResume onResume()}).</li>
<li>Он содержит службу {@link android.app.Service}, связанную с действием, с которым
взаимодействует пользователь.</li>
<li>Он содержит службу {@link android.app.Service}, которая выполняется "на переднем плане", &mdash; службу,
которая называется {@link android.app.Service#startForeground startForeground()}.
<li>Он содержит службу{@link android.app.Service}, которая выполняет один из обратных вызовов
жизненного цикла ({@link android.app.Service#onCreate onCreate()}, {@link android.app.Service#onStart
onStart()} или {@link android.app.Service#onDestroy onDestroy()}).</li>
<li>Он содержит ресивер {@link android.content.BroadcastReceiver}, который выполняет метод {@link
android.content.BroadcastReceiver#onReceive onReceive()}.</li>
</ul>
<p>Обычно одновременно работает лишь несколько процессов переднего плана. Они уничтожаются только
в крайнем случае, если памяти остается так мало, что они не могут продолжать совместную работу. Обычно в этот момент
устройство достигло состояния разбиения памяти на страницы, поэтому для того, чтобы пользовательский интерфейс откликался на действия пользователя, необходимо
удаление некоторых процессов на переднем плане.</p></li>
<li><b>Видимые процессы</b>
<p>Процессы, которые не содержат компонентов переднего плана, но могут
влиять на отображение на экране. Процесс считается видимым,
если выполняется любое из следующих условий:</p>
<ul>
<li>Он содержит действие {@link android.app.Activity}, которое не находится на переднем плане, но
видно пользователю (вызван метод {@link android.app.Activity#onPause onPause()}).
Например, это может происходить, если действие на переднем плане запустило диалоговое окно, которое позволяет видеть
предыдущее действие позади него.</li>
<li>Он содержит службу {@link android.app.Service}, связанную с видимым
действием или действием переднего плана.</li>
</ul>
<p>Видимый процесс считается исключительно важным, его следует удалять
только в случае, если требуется сохранить работу всех процессов переднего плана. </p>
</li>
<li><b>Служебный процесс</b>
<p>Процесс, который выполняет службу, запущенную с помощью метода {@link
android.content.Context#startService startService()}, и не попадает ни в одну из двух
категорий более высокого уровня. Хотя служебные процессы не связаны непосредственно с тем, что видит пользователь,
они обычно выполняют важные для пользователя действия (например, воспроизводят музыку в фоновом режиме или
загружают данные в сеть), поэтому система сохраняет их выполнение, если памяти достаточно для
их работы наряду со всеми видимыми процессами и процессами переднего плана. </p>
</li>
<li><b>Фоновый процесс</b>
<p>Процесс, содержащий действия, которые не видны пользователю в настоящее время (вызван метод
{@link android.app.Activity#onStop onStop()} действия). Эти процессы не оказывают непосредственного
воздействия на работу пользователя, и система может удалить их в любой момент, чтобы освободить память для
процессов переднего плана,
видимых или служебных процессов. Обычно выполняется множество фоновых процессов, поэтому они хранятся в списке
LRU (недавно использованные), чтобы процессы, содержащие самые
недавние действия, которые видел пользователь, удалялись в последнюю очередь. Если для действия правильно реализованы методы жизненного цикла,
и действие сохраняет текущее состояние, удаление процесса этого действия не оказывает видимого воздействия на
работу пользователя, так как когда пользователь возвращается к этому действию, оно восстанавливает
все элементы своего видимого состояния. Информацию о сохранении и восстановлении состояния см. в документе <a href="{@docRoot}guide/components/activities.html#SavingActivityState">Действия</a>
.</p>
</li>
<li><b>Пустой процесс</b>
<p>Процесс, не содержащий никаких компонентов активного приложения. Единственная причина сохранять процесс
такого типа — это кэширование, которое улучшает время следующего запуска
компонента в этом процессе. Система часто удаляет эти процессы для равномерного распределения всех системных
ресурсов между кэшем процесса и кэшем базового ядра.</p>
</li>
</ol>
<p>Система Android относит процесс к максимально высокому уровню на основе важности
компонентов, активных в процессе в текущее время. Например, если процесс содержит служебное и видимое действие,
процесс считается видимым, а не служебным процессом.</p>
<p>Кроме того, уровень процесса может быть повышен, поскольку имеются другие процессы, зависимые от него.
Например, процесс, обслуживающий другой процесс, не может иметь уровень ниже уровня обслуживаемого
процесса. Например, если поставщик контента в процессе A обслуживает клиента в процессе B или
служебный процесс A связан с компонентом в процессе B, процесс A всегда считается не менее
важным, чем процесс B.</p>
<p>Так как процесс, выполняющий службу, оценивается выше процесса с фоновыми действиям,
действие, запускающее долговременную операцию, может запустить <a href="{@docRoot}guide/components/services.html">службу</a> для этой операции, а не просто
создать рабочий поток, особенно в случае, если операция продлится дольше действия.
Например, действие, которое загружает изображение на веб-сайт, должно запустить службу для выполнения
загрузки, так что загрузка может продолжаться в фоновом режиме даже после выхода пользователя из действия.
Использование службы гарантирует, что операция будет иметь приоритет не ниже «служебного процесса»,
независимо от того, что происходит с действием. По этой же причине ресиверы должны
использовать службы, а не просто ставить в поток операции, требующие много времени для выполнения.</p>
<h2 id="Threads">Потоки</h2>
<p>При запуске приложения система создает поток выполнения для приложения,
который называется «главным». Этот поток очень важен, так как он отвечает за диспетчеризацию событий
на виджеты соответствующего интерфейса пользователя, включая события графического представления. Он также является потоком, в котором
приложение взаимодействует с компонентами из набора инструментов пользовательского интерфейса Android (компонентами из пакетов {@link
android.widget} и {@link android.view}). По существу, главный поток — это то, что иногда называют
потоком пользовательского интерфейса.</p>
<p>Система <em>не</em> создает отдельного потока для каждого экземпляра компонента. Все
компоненты, которые выполняются в одном процессе, создают экземпляры в потоке пользовательского интерфейса, и системные вызовы
каждого компонента отправляются из этого потока. Поэтому методы, которые отвечают на системные
обратные вызовы (такие как метод {@link android.view.View#onKeyDown onKeyDown()} для сообщения о действиях пользователя
или метод обратного вызова жизненного цикла), всегда выполняются в потоке пользовательского интерфейса процесса.</p>
<p>Например, когда пользователь нажимает кнопку на экране, поток пользовательского интерфейса вашего приложения отправляет
событие нажатия в виджет, который, в свою очередь, устанавливает кнопку в нажатое состояние и отправляет запрос на аннулирование
в очередь событий. Поток пользовательского интерфейса исключает запрос из очереди и уведомляет виджет, что он должен
отобразиться повторно.</p>
<p>Когда приложение выполняет интенсивную работу в ответ на действия пользователя, эта одиночная модель потока
может показывать плохую производительность, если приложение реализовано неправильно. То есть, если
все происходит в потоке пользовательского интерфейса, выполнение долговременных операций, таких как сетевой доступ или
запросы к базе данных, будет блокировать весь пользовательский интерфейс. Когда поток заблокирован, не могут обрабатываться никакие события,
включая события изменения отображения. С точки зрения пользователя
приложение выглядит зависшим. Хуже того, если поток пользовательского интерфейса заблокирован более нескольких секунд
(в настоящее время около 5 секунд), отображается печально известное диалоговое окно <a href="http://developer.android.com/guide/practices/responsiveness.html">«приложение не
отвечает</a>». После этого недовольный пользователь может выйти из вашего приложения
и удалить его.</p>
<p>Кроме того, набор инструментов пользовательского интерфейса Andoid <em>не</em> является потокобезопасным. Поэтому, вы не должны работать
с пользовательским интерфейсом из рабочего потока. Манипуляции с пользовательским интерфейсом необходимо выполнять из
потока пользовательского интерфейса. Таким образом, существует только два правила однопоточной модели Android:</p>
<ol>
<li>Не блокируйте поток пользовательского интерфейса
<li>Не обращайтесь к набору инструментов пользовательского интерфейса Android снаружи потока пользовательского интерфейса
</ol>
<h3 id="WorkerThreads">Рабочие потоки</h3>
<p>Вследствие описанной выше однопоточной модели для динамичности пользовательского интерфейса ваших приложений
очень важно не блокировать поток пользовательского интерфейса. Если требуется выполнять операции,
занимающие некоторое время, обязательно выполняйте их в отдельных потоках (»фоновых» или
«рабочих» потоках).</p>
<p>Например, ниже приведен код контроля нажатий, который загружает изображение из отдельного
потока и отображает их в виджете {@link android.widget.ImageView}:</p>
<pre>
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
Bitmap b = loadImageFromNetwork("http://example.com/image.png");
mImageView.setImageBitmap(b);
}
}).start();
}
</pre>
<p>На первый взгляд, он должен работать хорошо, так как он создает новый поток для обработки сетевой
операции. Однако, он нарушает второе правило однопоточной модели: <em>не обращайтесь к набору инструментов пользовательского интерфейса
Android снаружи потока пользовательского интерфейса</em> &mdash; этот пример изменяет {@link
android.widget.ImageView} из рабочего потока, а не из потока пользовательского интерфейса. Это может привести
к неопределенному и непредвиденному поведению, отследить которое будет трудно.</p>
<p>Для устранения этой проблемы Android предлагает несколько путей доступа к потоку пользовательского интерфейса из других
потоков. Ниже приведен список полезных методов:</p>
<ul>
<li>{@link android.app.Activity#runOnUiThread(java.lang.Runnable)
Activity.runOnUiThread(Runnable)}</li>
<li>{@link android.view.View#post(java.lang.Runnable) View.post(Runnable)}</li>
<li>{@link android.view.View#postDelayed(java.lang.Runnable, long) View.postDelayed(Runnable,
long)}</li>
</ul>
<p>Например, можно исправить приведенный выше код с помощью метода {@link
android.view.View#post(java.lang.Runnable) View.post(Runnable)}:</p>
<pre>
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();
}
</pre>
<p>Теперь реализация является потокобезопасной: сетевая операция выполняется из отдельного потока,
тогда как {@link android.widget.ImageView} работает из потока пользовательского интерфейса.</p>
<p>Однако по мере роста сложности, код такого типа может становиться запутанным и сложным
для поддержания. Чтобы обрабатывать более сложные взаимодействия с рабочим потоком, можно
использовать метод {@link android.os.Handler} в рабочем потоке для обработки сообщений, поступающих из потока
пользовательского интерфейса. Вероятно, самым лучшим решением является расширение класса {@link android.os.AsyncTask},
которое упрощает выполнение заданий рабочего потока, которые должны взаимодействовать с пользовательским интерфейсом.</p>
<h4 id="AsyncTask">Использование AsyncTask</h4>
<p>Метод {@link android.os.AsyncTask} позволяет выполнять асинхронную работу в пользовательском
интерфейсе. Он выполняет операции блокирования в рабочем потоке и затем публикует результаты в потоке
пользовательского интерфейса без необходимости самостоятельно обрабатывать потоки и/или обработчики.</p>
<p>Для использования этого метода необходимо создать подкласс {@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()}
из потока пользовательского интерфейса.</p>
<p>Например, можно реализовать предыдущий пример с помощью метода {@link android.os.AsyncTask} следующим
образом:</p>
<pre>
public void onClick(View v) {
new DownloadImageTask().execute("http://example.com/image.png");
}
private class DownloadImageTask extends AsyncTask&lt;String, Void, Bitmap&gt; {
/** 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);
}
}
</pre>
<p>Теперь пользовательский интерфейс защищен и код стал проще, так как работа разделена на
часть, которая должна выполняться в рабочем потоке, и часть, которая должна выполняться в потоке пользовательского интерфейса.</p>
<p>Прочитайте статью {@link android.os.AsyncTask}, чтобы полностью понять
использование этого класса. Здесь приведен краткий обзор его работы:</p>
<ul>
<li>Можно указывать тип параметров, значения хода выполнения и конечное
значение задания с помощью универсальных компонентов</li>
<li>Метод {@link android.os.AsyncTask#doInBackground doInBackground()} выполняется автоматически
в рабочем потоке</li>
<li>Методы {@link android.os.AsyncTask#onPreExecute onPreExecute()}, {@link
android.os.AsyncTask#onPostExecute onPostExecute()} и {@link
android.os.AsyncTask#onProgressUpdate onProgressUpdate()} запускаются в потоке пользовательского интерфейса</li>
<li>Значение, возвращенное методом {@link android.os.AsyncTask#doInBackground doInBackground()}, отправляется в метод
{@link android.os.AsyncTask#onPostExecute onPostExecute()}</li>
<li>Можно вызвать {@link android.os.AsyncTask#publishProgress publishProgress()} в любой момент в {@link
android.os.AsyncTask#doInBackground doInBackground()} для выполнения {@link
android.os.AsyncTask#onProgressUpdate onProgressUpdate()} в потоке пользовательского интерфейса</li>
<li>Задание можно отменить в любой момент из любого потока</li>
</ul>
<p class="caution"><strong>Предупреждение!</strong> Другая проблема, с которой вы можете столкнуться при использовании рабочего
потока, состоит в непредсказуемом перезапуске действия вследствие <a href="{@docRoot}guide/topics/resources/runtime-changes.html">изменения конфигурации в режиме выполнения</a>,
(например, когда пользователь изменяет ориентацию экрана), что может разрушить рабочий поток. Чтобы
увидеть, как можно сохранить задание во время одного из подобных перезапусков и как правильно отменить задание
при разрушении действия, изучите исходный код примера приложения <a href="http://code.google.com/p/shelves/">Shelves</a>.</p>
<h3 id="ThreadSafe">Потокобезопасные методы</h3>
<p> В некоторых ситуациях реализованные методы могут вызываться из нескольких потоков и, следовательно,
должны быть написаны с сохранением потокобезопасности. </p>
<p>В первую очередь это относится к методам, которые можно вызывать удаленно, например, к методам в <a href="{@docRoot}guide/components/bound-services.html">связанной службе</a>. Когда вызов
метода реализуется в классе {@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} должны быть реализованы с сохранением потокобезопасности.</p>
<p> Аналогичным образом поставщик контента может получать запросы данных, которые происходят из другого процесса.
Хотя классы {@link android.content.ContentResolver} и {@link android.content.ContentProvider}
скрывают подробности управления взаимодействием процессов, методы {@link
android.content.ContentProvider}, которые отвечают на эти запросы, &mdash;методы {@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()} &mdash;вызываются из пула потоков в процессе поставщика контента, а не в процессе
потока пользовательского интерфейса. Поскольку эти методы могут вызываться из любого числа потоков одновременно,
они также должны быть реализованы с сохранением потокобезопасности. </p>
<h2 id="IPC">Взаимодействие процессов</h2>
<p>Система Android предлагает механизм взаимодействия процессов (IPC) с помощью удаленного вызова процедуры
(RPC), при котором метод вызывается действием или другим компонентом приложения, но выполняется
удаленно (в другом процессе) с возвратом всех результатов
вызывающему компоненту. Это влечет разложение вызова метода и его данных до уровня, понятного
операционной системе, передачу его из локального процесса и адресного пространства удаленному процессу и
адресному пространству, а затем повторную сборку и восстановление вызова. После этого возвращенные значения
передаются в обратном направлении. Система Android содержит все коды для выполнения этих механизмов IPC,
так что вы можете сосредоточиться на определении и реализации программного интерфейса RPC. </p>
<p>Для выполнения IPC приложение должно быть привязано к службе с помощью метода {@link
android.content.Context#bindService bindService()}. Дополнительные сведения представлены в разделе <a href="{@docRoot}guide/components/services.html">Службы</a> руководства для разработчиков.</p>
<!--
<h2>Beginner's Path</h2>
<p>For information about how to perform work in the background for an indefinite period of time
(without a user interface), continue with the <b><a
href="{@docRoot}guide/components/services.html">Services</a></b> document.</p>
-->