diff options
Diffstat (limited to 'docs/html-intl/intl/ru/guide/components')
-rw-r--r-- | docs/html-intl/intl/ru/guide/components/activities.jd | 756 | ||||
-rw-r--r-- | docs/html-intl/intl/ru/guide/components/bound-services.jd | 658 | ||||
-rw-r--r-- | docs/html-intl/intl/ru/guide/components/fragments.jd | 812 | ||||
-rw-r--r-- | docs/html-intl/intl/ru/guide/components/fundamentals.jd | 480 | ||||
-rw-r--r-- | docs/html-intl/intl/ru/guide/components/index.jd | 57 | ||||
-rw-r--r-- | docs/html-intl/intl/ru/guide/components/intents-filters.jd | 899 | ||||
-rw-r--r-- | docs/html-intl/intl/ru/guide/components/loaders.jd | 494 | ||||
-rw-r--r-- | docs/html-intl/intl/ru/guide/components/processes-and-threads.jd | 411 | ||||
-rw-r--r-- | docs/html-intl/intl/ru/guide/components/recents.jd | 256 | ||||
-rw-r--r-- | docs/html-intl/intl/ru/guide/components/services.jd | 813 | ||||
-rw-r--r-- | docs/html-intl/intl/ru/guide/components/tasks-and-back-stack.jd | 578 |
11 files changed, 6214 insertions, 0 deletions
diff --git a/docs/html-intl/intl/ru/guide/components/activities.jd b/docs/html-intl/intl/ru/guide/components/activities.jd new file mode 100644 index 000000000000..5f55a3521350 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/activities.jd @@ -0,0 +1,756 @@ +page.title=Операции +page.tags=операция,намерение +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> +<h2>Содержание документа</h2> +<ol> + <li><a href="#Creating">Создание операции</a> + <ol> + <li><a href="#UI">Реализация пользовательского интерфейса</a></li> + <li><a href="#Declaring">Объявление операции в манифесте</a></li> + </ol> + </li> + <li><a href="#StartingAnActivity">Запуск операции</a> + <ol> + <li><a href="#StartingAnActivityForResult">Запуск операции для получения результата</a></li> + </ol> + </li> + <li><a href="#ShuttingDown">Завершение операции</a></li> + <li><a href="#Lifecycle">Управление жизненным циклом операций</a> + <ol> + <li><a href="#ImplementingLifecycleCallbacks">Реализация обратных вызовов жизненного цикла</a></li> + <li><a href="#SavingActivityState">Сохранение состояния операции</a></li> + <li><a href="#ConfigurationChanges">Обработка изменений в конфигурации</a></li> + <li><a href="#CoordinatingActivities">Согласование операций</a></li> + </ol> + </li> +</ol> + +<h2>Ключевые классы</h2> +<ol> + <li>{@link android.app.Activity}</li> +</ol> + +<h2>См. также:</h2> +<ol> + <li><a href="{@docRoot}guide/components/tasks-and-back-stack.html">Задачи и стек +переходов назад</a></li> +</ol> + +</div> +</div> + + + +<p>{@link android.app.Activity} — это компонент приложения, который выдает экран, + и с которым пользователи могут взаимодействовать для выполнения каких-либо действий, например набрать номер телефона, сделать фото, отправить письмо или +просмотреть карту. Каждой операции присваивается окно для прорисовки соответствующего пользовательского интерфейса. Обычно окно +отображается во весь экран, однако его размер может быть меньше, и оно может размещаться поверх других +окон.</p> + +<p> Как правило, приложение состоит из нескольких операций, которые слабо +связаны друг с другом. Обычно одна из операций в приложении обозначается как «основная», +предлагаемая пользователю при первом запуске приложения. В свою очередь, каждая +операция может запустить другую операцию для выполнения различных действий. Каждый раз, когда +запускается новая операция, предыдущая операция останавливается, однако система сохраняет ее +в стеке («стек переходов назад»). При запуске новой операции она помещается в стек переходов назад и +отображается для пользователя. Стек переходов назад работает по принципу «последним вошёл — первым вышел», +поэтому после того как пользователь завершил текущую операцию и нажал кнопку <em>Назад</em>, текущая операция +удаляется из стека (и уничтожается), и возобновляется предыдущая операция. (Подробные сведения о стеке +переходов назад представлены в статье <a href="{@docRoot}guide/components/tasks-and-back-stack.html">Задачи и стек +переходов назад</a>.)</p> + +<p>Когда операция останавливается по причине запуска новой операции, для уведомления об изменении ее состояния +используются методы обратного вызова жизненного цикла операции. +Существует несколько таких методов, которые может принимать операция вследствие изменения своего состояния +— создание операции, ее остановка, возобновление или уничтожение системой; также +каждый обратный вызов представляет возможность выполнить определенное действие, +подходящее для соответствующего изменения состояния. Например, в случае остановки операция должна освободить любые +крупные объекты, например, подключение к сети или базе данных. При возобновлении операции вы можете +повторно получить необходимые ресурсы и возобновить выполнение прерванных действий. Такие изменения состояния +являются частью жизненного цикла операции.</p> + +<p>Далее в этом документе рассматриваются основы создания и использования операций, +включая полное описание жизненного цикла операции, чтобы вы могли лучше понять, как следует управлять +переходами между различными состояниями операции.</p> + + + +<h2 id="Creating">Создание операции</h2> + +<p>Чтобы создать операцию, сначала необходимо создать подкласс класса {@link android.app.Activity} (или +воспользоваться существующим его подклассом). В таком подклассе необходимо реализовать методы обратного вызова, которые +вызывает система при переходе операции из одного состояния своего жизненного цикла в другое, например при +создании, остановке, возобновлении или уничтожении операции. Вот два наиболее важных метода +обратного вызова:</p> + +<dl> + <dt>{@link android.app.Activity#onCreate onCreate()}</dt> + <dd>Этот метод необходимо обязательно реализовать, поскольку система вызывает его при создании вашей +операции. В своей реализации вам необходимо инициализировать ключевые компоненты +операции. + Наиболее важно именно здесь вызвать {@link android.app.Activity#setContentView +setContentView()} для определения макета пользовательского интерфейса операции.</dd> + <dt>{@link android.app.Activity#onPause onPause()}</dt> + <dd>Система вызывает этот метод в качестве первого признака выхода пользователя из +операции (однако это не всегда означает, что операция будет уничтожена). Обычно именно здесь +необходимо применять любые изменения, которые должны быть сохранены помимо текущего сеанса работы пользователя (поскольку +пользователь может не вернуться назад).</dd> +</dl> + +<p>Существуют также и некоторые другие методы обратного вызова жизненного цикла, которые необходимо использовать для того, чтобы обеспечить +плавный переход между операциями, а также для обработки непредвиденных нарушений в работе операции, в результате которых она может быть +остановлена или даже уничтожена. Более подробное описание всех методов обратного вызова жизненного цикла представлены в разделе, +посвященном <a href="#Lifecycle">управлению жизненным циклом операций</a>.</p> + + + +<h3 id="UI">Реализация пользовательского интерфейса</h3> + +<p> Для реализации пользовательского интерфейса операции используется иерархия представлений —объектов, полученных из +класса {@link android.view.View}. Каждое представление отвечает за определенную прямоугольную область +окна операции и может реагировать на действия пользователей. Например, представлением может быть +кнопка, нажатие на которую приводит к выполнению определенного действия.</p> + +<p>В Android предусмотрен набор уже готовых представлений, которые можно использовать для создания дизайна макета и его +организации. Виджеты — это представления с визуальными (и интерактивными) элементами, например, +кнопками, текстовыми полями, чекбоксами или просто изображениями. Макеты — это представления, полученные из класса {@link +android.view.ViewGroup}, обеспечивающие уникальную модель компоновки для своих дочерних представлений, таких как линейный +макет, сетка или относительный макет. Также можно создать подкласс для классов {@link android.view.View} и +{@link android.view.ViewGroup} (или воспользоваться существующими подклассами), чтобы создать собственные виджеты и +макеты, и затем применить их к макету своей операции.</p> + +<p>Чаще всего для задания макета с помощью представлений используется XML-файл макета, сохраненный в +ресурсах приложения. Таким образом вы можете хранить дизайн пользовательского интерфейса отдельно от +исходного кода, который служит для задания поведения операции. Чтобы задать макет в качестве пользовательского интерфейса +операции, можно использовать метод {@link android.app.Activity#setContentView(int) setContentView()}, передав в него +идентификатор ресурса для макета. Однако вы также можете создать новые {@link android.view.View} в коде вашей +операции и создать иерархию представлений. Для этого вставьте {@link +android.view.View} в {@link android.view.ViewGroup}, а затем используйте этот макет, передав корневой объект +{@link android.view.ViewGroup} в метод {@link android.app.Activity#setContentView(View) +setContentView()}.</p> + +<p>Подробные сведения о создании пользовательского интерфейса см. в статье <a href="{@docRoot}guide/topics/ui/index.html">Пользовательский интерфейс</a>.</p> + + + +<h3 id="Declaring">Объявление операции в манифесте</h3> + +<p>Чтобы операция стала доступна системе, ее необходимо объявить в файле +манифеста. Для этого откройте файл манифеста и добавьте элемент <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> в +качестве дочернего для элемента +<a href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a>. Например:</p> + +<pre> +<manifest ... > + <application ... > + <activity android:name=".ExampleActivity" /> + ... + </application ... > + ... +</manifest > +</pre> + +<p>Существует несколько других атрибутов, которые можно включить в этот элемент, чтобы определить такие +свойства, как метка операции, значок операции или тема оформления для пользовательского интерфейса операции. +Единственным обязательным атрибутом является <a href="{@docRoot}guide/topics/manifest/activity-element.html#nm">{@code android:name}</a> +— он определяет имя класса операции. После +публикации вашего приложения вам не следует переименовывать его, поскольку это может нарушить некоторые +функциональные возможности приложения, например, ярлыки приложения (ознакомьтесь с публикацией <a href="http://android-developers.blogspot.com/2011/06/things-that-cannot-change.html">Вещи +, которые нельзя менять</a> в блоге разработчиков).</p> + +<p>Дополнительные сведения об объявлении операции +в манифесте см. в справке по элементу <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a>.</p> + + +<h4>Использование фильтров намерений</h4> + +<p>Элемент <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code +<activity>}</a> также может задавать различные фильтры намерений — с помощью элемента <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a> — для объявления того, как другие компоненты приложения +могут активировать операцию.</p> + +<p>При создании нового приложения с помощью инструментов Android SDK в заготовке операции, +создаваемой автоматически, имеется фильтр намерений, который объявляет операцию. +Эта операция реагирует на выполнение «основного» действия, и ее следует поместить в категорию переходсредства запуска. Фильтр намерений +выглядит следующим образом.</p> + +<pre> +<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> +</activity> +</pre> + +<p>Элемент <a href="{@docRoot}guide/topics/manifest/action-element.html">{@code +<action>}</a> указывает, что это «основная» точка входа в приложение. Элемент <a href="{@docRoot}guide/topics/manifest/category-element.html">{@code +<category>}</a>указывает, что эту операцию следует указать в +средстве запуска приложений системы (чтобы пользователи могли запускать эту операцию).</p> + +<p>Если приложение планируется создать самодостаточным и запретить другим приложениям активировать +его операции, то других фильтров намерений создавать не нужно. В этом случае только в одной операции +должно иметься «основное» действие, и ее следует поместить в категорию средства запуска, как в примере выше. В операциях, +которые не должны быть доступны для других приложений, не следует включать фильтры намерений. +Вы можете самостоятельно запустить такие операции с помощью явных намерений (как описывается в следующем разделе).</p> + +<p>Однако, если вам необходимо, чтобы операция реагировала на неявные намерения, +получаемые от других приложений (а также из вашего приложения), для операции необходимо определить дополнительные фильтры +намерений. Для каждого типа намерения, на который необходимо реагировать, необходимо указать объект <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a>, включающий элемент +<a href="{@docRoot}guide/topics/manifest/action-element.html">{@code +<action>}</a> и необязательный элемент <a href="{@docRoot}guide/topics/manifest/category-element.html">{@code +<category>}</a> или <a href="{@docRoot}guide/topics/manifest/data-element.html">{@code +<data>}</a> (или оба этих элемента). Эти элементы определяют тип намерения, на который может реагировать +ваша операция.</p> + +<p>Дополнительные сведения о том, как операции могут реагировать на намерения, приведены в статье <a href="{@docRoot}guide/components/intents-filters.html">Намерения и фильтры намерений</a>. +</p> + + + +<h2 id="StartingAnActivity">Запуск операции</h2> + +<p>Для запуска другой операции достаточно вызвать метод {@link android.app.Activity#startActivity +startActivity()}, передав в него объект {@link android.content.Intent}, который описывает запускаемую +операцию. В намерении указывается либо точная операция для запуска, либо описывается +тип операции, которую вы хотите выполнить (после чего система выбирает для вас подходящую +операцию, которая +может даже находиться в другом приложении). Намерение также может содержать небольшой объем данных, +которые будут использоваться запущенной операцией.</p> + +<p>При работе с собственным приложением зачастую требуется лишь запустить нужную операцию. + Для этого необходимо создать намерение, которое явно определяет требуемую операцию +с помощью имени класса. Ниже представлен пример запуска одной операцией другой операции с именем {@code +SignInActivity}.</p> + +<pre> +Intent intent = new Intent(this, SignInActivity.class); +startActivity(intent); +</pre> + +<p>Однако в вашем приложении также может потребоваться выполнить некоторое действие, например, отправить письмо, текстовое +сообщение или обновить статус, используя данные из вашей операции. В этом случае в вашем приложении могут +отсутствовать такие действия, поэтому вы можете воспользоваться операциями +из других приложений, имеющихся на устройстве, которые могут выполнять требуемые действия. Как раз в этом случае +намерения особенно полезны — можно создать намерение, которое описывает необходимое действие, +после чего система +запускает его из другого приложения. При наличии +нескольких операций, которые могут обработать намерение, пользователь может выбирать, какое из них следует использовать. Например, +если пользователю требуется предоставить возможность отправить электронное письмо, можно создать +следующее намерение:</p> + +<pre> +Intent intent = new Intent(Intent.ACTION_SEND); +intent.putExtra(Intent.EXTRA_EMAIL, recipientArray); +startActivity(intent); +</pre> + +<p>Дополнительный компонент {@link android.content.Intent#EXTRA_EMAIL}, добавленный в намерение, представляет собой строковый массив +адресов электронной почты для отправки письма. Когда почтовая программа реагирует на это +намерение, она считывает дополнительно добавленный строковый массив и помещает имеющиеся в нем адреса в поле получателя +в окне создания письма. При этом запускается операция почтовой программы, а после того, как +пользователь завершит требуемые действия, возобновляется ваша операция.</p> + + + + +<h3 id="StartingAnActivityForResult">Запуск операции для получения результата</h3> + +<p>В некоторых случаях после запуска операции может потребоваться получить результат. Для этого +вызовите метод {@link android.app.Activity#startActivityForResult +startActivityForResult()} (вместо {@link android.app.Activity#startActivity +startActivity()}). Чтобы получить результат после выполнения последующей +операции, реализуйте метод обратного +вызова {@link android.app.Activity#onActivityResult onActivityResult()}. По завершении последующей операции она возвращает результат в объекте {@link +android.content.Intent} +в вызванный метод {@link android.app.Activity#onActivityResult onActivityResult()}.</p> + +<p>К примеру, пользователю потребуется выбрать один из контактов, чтобы ваша операция могла +выполнить некоторые действия с информацией об этом контакте. Ниже представлен пример создания такого намерения +и обработки результата.</p> + +<pre> +private void pickContact() { + // Create an intent to "pick" a contact, as defined by the content provider URI + Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI); + startActivityForResult(intent, PICK_CONTACT_REQUEST); +} + +@Override +protected void onActivityResult(int requestCode, int resultCode, Intent data) { + // If the request went well (OK) and the request was PICK_CONTACT_REQUEST + if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) { + // Perform a query to the contact's content provider for the contact's name + Cursor cursor = getContentResolver().query(data.getData(), + new String[] {Contacts.DISPLAY_NAME}, null, null, null); + if (cursor.moveToFirst()) { // True if the cursor is not empty + int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME); + String name = cursor.getString(columnIndex); + // Do something with the selected contact's name... + } + } +} +</pre> + +<p>В этом примере демонстрируется базовая логика, которой следует руководствоваться при использовании метода {@link +android.app.Activity#onActivityResult onActivityResult()} для обработки результата +выполнения операции. Первое условие проверяет, успешен ли запрос, и если он успешен, то результат для +{@code resultCode} будет {@link android.app.Activity#RESULT_OK}; также проверяется, известен ли запрос, +для которого получен этот результат, и в этом случае {@code requestCode} соответствует +второму параметру, отправленному в метод {@link android.app.Activity#startActivityForResult +startActivityForResult()}. Здесь код обрабатывает результат выполнения операции путем запроса данных, +возвращенных в {@link android.content.Intent} (параметр {@code data}).</p> + +<p>При этом {@link +android.content.ContentResolver} выполняет запрос к поставщику контента, который возвращает объект +{@link android.database.Cursor}, обеспечивающий считывание запрошенных данных. Дополнительные сведения представлены в статье +<a href="{@docRoot}guide/topics/providers/content-providers.html">Поставщики контента</a>.</p> + +<p>Дополнительные сведения об использовании намерений см. в статье <a href="{@docRoot}guide/components/intents-filters.html">Намерения и фильтры +намерений</a>.</p> + + +<h2 id="ShuttingDown">Завершение операции</h2> + +<p>Для завершения операции достаточно вызвать ее метод {@link android.app.Activity#finish +finish()}. Также для завершения отдельной операции, запущенной ранее, можно вызвать метод +{@link android.app.Activity#finishActivity finishActivity()}.</p> + +<p class="note"><strong>Примечание.</strong> В большинстве случаев вам не следует явно завершать операцию +с помощью этих методов. Как указано в следующем разделе, посвященном управлению жизненным циклом операции, система +Android выполняет такое управление за вас, поэтому вам не нужно завершать ваши +собственные операции. Вызов этих методов может отрицательно сказаться на ожидаемом +поведении приложения. Их следует использовать исключительно тогда, когда вы абсолютно не хотите, чтобы пользователь возвращался к этому +экземпляру операции.</p> + + +<h2 id="Lifecycle">Управление жизненным циклом операций</h2> + +<p>Управление жизненным циклом операций путем реализации методов обратного вызова +имеет важное значение при разработке надежных +и гибких приложений. На жизненный цикл операции напрямую влияют его связи +с другими операциями, его задачами и стеком переходов назад.</p> + +<p>Существует всего три состояния операции:</p> + +<dl> + <dt><i>Возобновлена</i></dt> + <dd>Операция выполняется на переднем плане экрана и отображается для пользователя. (Это состояние +также иногда называется «Выполняется».)</dd> + + <dt><i>Приостановлена</i></dt> + <dd>На переднем фоне выполняется другая операция, которая отображается для пользователя, однако эта операция по-прежнему не скрыта. То есть +поверх текущей операции отображается другая операция, частично прозрачная или не занимающая +полностью весь экран. Приостановленная операция полностью активна (объект {@link android.app.Activity} +по-прежнему находится в памяти, в нем сохраняются все сведения о состоянии и информация об элементах, и он также остается связанным с +диспетчером окон), однако в случае острой нехватки памяти система может завершить ее.</dd> + + <dt><i>Остановлена</i></dt> + <dd>Операция полностью перекрывается другой операцией (теперь она выполняется в +фоновом режиме). Остановленная операция по-прежнему активна (объект {@link android.app.Activity} +по-прежнему находится в памяти, в нем сохраняются все сведения о состоянии и информация об элементах, но объект больше <em>не</em> +связан с диспетчером окон). Однако операция больше не видна пользователю, и в случае нехватки памяти система +может завершить ее.</dd> +</dl> + +<p>Если операция приостановлена или полностью остановлена, система может очистить ее из памяти путем +завершения самой операции (с помощью метода {@link android.app.Activity#finish finish()}) или просто завершить ее +процесс. В случае повторного открытия операции (после ее завершения) ее потребуется создать +полностью.</p> + + + +<h3 id="ImplementingLifecycleCallbacks">Реализация обратных вызовов жизненного цикла</h3> + +<p>При переходе операции из одного вышеописанного состояния в другое, уведомления об этом +реализуются через различные методы обратного вызова. Все методы обратного вызова представляют собой привязки, которые +можно переопределить для выполнения подходящего действия при изменении состояния операции. Указанная ниже базовая +операция включает каждый из основных методов жизненного цикла.</p> + + +<pre> +public class ExampleActivity extends Activity { + @Override + public void {@link android.app.Activity#onCreate onCreate}(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // The activity is being created. + } + @Override + protected void {@link android.app.Activity#onStart onStart()} { + super.onStart(); + // The activity is about to become visible. + } + @Override + protected void {@link android.app.Activity#onResume onResume()} { + super.onResume(); + // The activity has become visible (it is now "resumed"). + } + @Override + protected void {@link android.app.Activity#onPause onPause()} { + super.onPause(); + // Another activity is taking focus (this activity is about to be "paused"). + } + @Override + protected void {@link android.app.Activity#onStop onStop()} { + super.onStop(); + // The activity is no longer visible (it is now "stopped") + } + @Override + protected void {@link android.app.Activity#onDestroy onDestroy()} { + super.onDestroy(); + // The activity is about to be destroyed. + } +} +</pre> + +<p class="note"><strong>Примечание.</strong> При реализации этих методов жизненного цикла +всегда вызывайте реализацию суперкласса, прежде чем выполнять какие-либо действия, как показано в примерах выше.</p> + +<p>Вместе все эти методы определяют весь жизненный цикл операции. С помощью реализации этих +методов можно отслеживать три вложенных цикла в жизненном цикле операции: </p> + +<ul> +<li><b>Весь жизненный цикл</b> операции происходит между вызовом метода {@link +android.app.Activity#onCreate onCreate()} и вызовом метода {@link +android.app.Activity#onDestroy}. Ваша операция должна выполнить настройку +«глобального» состояния (например, определение макета) в методе {@link android.app.Activity#onCreate onCreate()}, а затем +освободить все оставшиеся в {@link android.app.Activity#onDestroy} ресурсы. Например, если в вашей операции +имеется поток, выполняющийся в фоновом режиме, для загрузки данных по сети, операция может создать +такой поток в методе {@link android.app.Activity#onCreate onCreate()}, а затем остановить его в методе {@link +android.app.Activity#onDestroy}.</li> + +<li><p><b>Видимый жизненный цикл</b> операции происходит между вызовами методов {@link +android.app.Activity#onStart onStart()} и {@link +android.app.Activity#onStop onStop()}. В течение этого времени операция +отображается на экране, где пользователь может взаимодействовать с ней. Например, метод {@link android.app.Activity#onStop onStop()} вызывается в +случае, когда запускается новая операция, а текущая больше не отображается. В промежутке между вызовами этих двух методов можно +сохранить ресурсы, необходимые для отображения операции для пользователя. Например, можно зарегистрировать объект +{@link android.content.BroadcastReceiver} в методе {@link +android.app.Activity#onStart onStart()} для отслеживания изменений, влияющих на пользовательский интерфейс, а затем отменить его регистрацию +в методе {@link android.app.Activity#onStop onStop()}, когда пользователь больше не видит +отображаемого. В течение всего жизненного цикла операции система может несколько раз вызывать методы {@link android.app.Activity#onStart onStart()} и {@link +android.app.Activity#onStop onStop()}, поскольку +операция то отображается для пользователя, то скрывается от него.</p></li> + +<li><p><b>Жизненный цикл операции, выполняемый на переднем плане</b>, происходит между вызовами методов {@link +android.app.Activity#onResume onResume()} и {@link android.app.Activity#onPause +onPause()}. В течение этого времени операция выполняется на фоне всех прочих операций и +отображается для пользователя. Операция может часто уходить в фоновый режим и выходить из него — например, +метод {@link android.app.Activity#onPause onPause()} вызывается при переходе устройства в спящий режим или +при появлении диалогового окна. Поскольку переход в это состояние может выполняться довольно часто, код в этих двух методах +должен быть легким, чтобы не допустить медленных переходов и не заставлять пользователя ждать.</p></li> +</ul> + +<p>На рисунке 1 иллюстрируются проходы и пути, которые операция может пройти между состояниями. +Прямоугольниками обозначены методы обратного вызова, которые можно реализовать для выполнения действий между переходами операции из одного +состояния в другое. <p> + +<img src="{@docRoot}images/activity_lifecycle.png" alt="" /> +<p class="img-caption"><strong>Рисунок 1.</strong> Жизненный цикл операции.</p> + +<p>Эти же методы жизненного цикла перечислены в таблице 1, в которой подробно описан каждый метод +обратного вызова и указано его место в +жизненном цикле операции в целом, включая сведения о том, может ли система завершить операцию по завершении +метода обратного вызова.</p> + +<p class="table-caption"><strong>Таблица 1.</strong> Сводные сведения о методах обратного вызова +жизненного цикла операции.</p> + +<table border="2" width="85%" frame="hsides" rules="rows"> +<colgroup align="left" span="3"></colgroup> +<colgroup align="left"></colgroup> +<colgroup align="center"></colgroup> +<colgroup align="center"></colgroup> + +<thead> +<tr><th colspan="3">Метод</th> <th>Описание</th> <th>Завершаемый?</th> <th>Следующий</th></tr> +</thead> + +<tbody> +<tr> + <td colspan="3" align="left"><code>{@link android.app.Activity#onCreate onCreate()}</code></td> + <td>Вызывается при первом создании операции. + Здесь необходимо настроить все обычные статические элементы — +создать представления, привязать данные и т. д. Этот метод передает +объект Bundle, содержащий предыдущее состояние операции (если +такое состояние было зафиксировано ранее; см. раздел <a href="#actstate">Сохранение состояния +операции</a>). + <p>За ним всегда следует метод {@code onStart()}.</p></td> + <td align="center">Нет</td> + <td align="center">{@code onStart()}</td> +</tr> + +<tr> + <td rowspan="5" style="border-left: none; border-right: none;"> </td> + <td colspan="2" align="left"><code>{@link android.app.Activity#onRestart +onRestart()}</code></td> + <td>Вызывается после остановки операции непосредственно перед ее +повторным запуском. + <p>За ним всегда следует метод {@code onStart()}.</p></td> + <td align="center">Нет</td> + <td align="center">{@code onStart()}</td> +</tr> + +<tr> + <td colspan="2" align="left"><code>{@link android.app.Activity#onStart onStart()}</code></td> + <td>Вызывается непосредственно перед тем, как операция становится видимой для пользователя. + <p>За ним следует метод {@code onResume()}, если операция +переходит на передний план, или метод {@code onStop()}, если она становится скрытой.</p></td> + <td align="center">Нет</td> + <td align="center">{@code onResume()} <br/>или<br/> {@code onStop()}</td> +</tr> + +<tr> + <td rowspan="2" style="border-left: none;"> </td> + <td align="left"><code>{@link android.app.Activity#onResume onResume()}</code></td> + <td>Вызывается непосредственно перед тем, как операция +начинает взаимодействие с пользователем. На этом этапе операция находится в самом +верху стека операций, и в нее поступают данные, вводимые пользователем. + <p>За ним всегда следует метод {@code onPause()}.</p></td> + <td align="center">Нет</td> + <td align="center">{@code onPause()}</td> +</tr> + +<tr> + <td align="left"><code>{@link android.app.Activity#onPause onPause()}</code></td> + <td>Вызывается, когда система собирается возобновить +другую операцию. Этот метод обычно используется для записи несохраненных изменений +в постоянное место хранения данных, остановки анимаций и других элементов, которые могут +использовать ресурсы ЦП и т. д. Здесь крайне важна оперативность, поскольку +следующая операция не будет возобновлена до тех пор, пока она не будет возвращена на передний план. + <p>За ним следует либо метод {@code onResume()}, если операция +возвращается на передний план, либо метод {@code onStop()}, если операция +становится скрытой для пользователя.</td> + <td align="center"><strong style="color:#800000">Да</strong></td> + <td align="center">{@code onResume()} <br/>или<br/> {@code onStop()}</td> +</tr> + +<tr> + <td colspan="2" align="left"><code>{@link android.app.Activity#onStop onStop()}</code></td> + <td>Вызывается в случае, когда операция больше не отображается для пользователя. Это может +произойти по причине того, что операция уничтожена, или ввиду возобновления поверх нее другой операции +(существующей или новой). + <p>За ним следует либо метод {@code onRestart()}, если +операция возобновляет взаимодействие с пользователем, либо метод +{@code onDestroy()}, если операция переходит в фоновый режим.</p></td> + <td align="center"><strong style="color:#800000">Да</strong></td> + <td align="center">{@code onRestart()} <br/>или<br/> {@code onDestroy()}</td> +</tr> + +<tr> + <td colspan="3" align="left"><code>{@link android.app.Activity#onDestroy +onDestroy()}</code></td> + <td>Вызывается перед тем, как операция будет уничтожена. Это финальный вызов, +который получает операция. Его можно вызвать либо по причине +завершения операции (вызов метода <code>{@link android.app.Activity#finish + finish()}</code>), либо ввиду временного уничтожения системой этого +экземпляра операции с целью освободить место. Чтобы различить эти два +сценария, используется метод <code>{@link + android.app.Activity#isFinishing isFinishing()}</code>.</td> + <td align="center"><strong style="color:#800000">Да</strong></td> + <td align="center"><em>Ничего</em></td> +</tr> +</tbody> +</table> + +<p>В столбце «Завершаемый?» указывается, может ли система +в любое время завершить процесс, содержащий операцию, <em>после возвращения метода</em> без +выполнения другой строки кода операции. Для трех методов в этом столбце указано «Да»: ({@link +android.app.Activity#onPause +onPause()}, {@link android.app.Activity#onStop onStop()} и {@link android.app.Activity#onDestroy +onDestroy()}). Поскольку метод {@link android.app.Activity#onPause onPause()} является первым из +этих трех после создания операции, метод {@link android.app.Activity#onPause onPause()} является +последним, который гарантированно будет вызван перед тем, как процесс <em>можно будет</em> завершить; если +системе потребуется срочно восстановить память в случае аварийной ситуации, то методы {@link +android.app.Activity#onStop onStop()} и {@link android.app.Activity#onDestroy onDestroy()} вызвать +не удастся. Поэтому следует воспользоваться {@link android.app.Activity#onPause onPause()}, чтобы записать +критически важные данные (такие как правки пользователя) в хранилище постоянных данных. Однако следует внимательно подходить к выбору информации, +которую необходимо сохранить во время выполнения метода {@link android.app.Activity#onPause onPause()}, поскольку любая блокировка +процедур в этом методе может вызвать блокирование перехода к следующей операции и тормозить работу +пользователя.</p> + +<p> Методы, для которых в столбце <b>Завершаемый?</b> указано «Нет», защищают процесс, содержащий операцию +, от завершения сразу с момента их вызова. Поэтому завершить операцию +можно в период между возвратом {@link android.app.Activity#onPause onPause()} и вызовом +{@link android.app.Activity#onResume onResume()}. Его снова не удастся завершить, пока снова не будет вызван и возвращен +{@link android.app.Activity#onPause onPause()}. </p> + +<p class="note"><strong>Примечание.</strong> Операцию, которую технически невозможно завершить в соответствии с определением +в таблице 1, по-прежнему может завершить система, однако это может произойти только в +чрезвычайных ситуациях, когда нет другой возможности. Случаи, когда возможно завершение операции, +более подробно рассматриваются в статье<a href="{@docRoot}guide/components/processes-and-threads.html">Процессы и +потоки</a>.</p> + + +<h3 id="SavingActivityState">Сохранение состояния операции</h3> + +<p>В обзорных сведениях об <a href="#Lifecycle">управлении жизненным циклом операции</a> кратко упоминается, +что +в случае приостановки или полной остановки операции ее состояние сохраняется. Это действительно так, поскольку +объект {@link android.app.Activity} при этом по-прежнему находится в памяти +, и вся информация о ее элементах и текущем состоянии по-прежнему активна. Поэтому любые +вносимые пользователем в операции изменения сохраняются, и когда операция возвращается на передний план +(когда она «возобновляется»), эти изменения остаются в этом объекте.</p> + +<p>Однако когда система уничтожает операцию в целях восстановления памяти, объект {@link +android.app.Activity} уничтожается, в результате чего системе не удается просто восстановить состояние операции для взаимодействия +с ней. Вместо этого системе необходимо повторно создать объект {@link android.app.Activity}, если пользователь +возвращается к нему. Но пользователю неизвестно, +что система уже уничтожила операцию и создала ее повторно, поэтому, возможно, +он ожидает, что операция осталась прежней. В этой ситуации можно обеспечить +сохранение важной информации о состоянии операции путем реализации дополнительного +метода обратного вызова, который позволяет сохранить информацию о вашей операции: {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}.</p> + +<p>Прежде чем сделать операцию доступной для уничтожения, система вызывает метод +{@link android.app.Activity#onSaveInstanceState onSaveInstanceState()}. Система передает в этот метод объект +{@link android.os.Bundle}, в котором можно сохранить +информацию о состоянии операции в виде пар «имя-значение», используя для этого такие методы, как {@link +android.os.Bundle#putString putString()} и {@link +android.os.Bundle#putInt putInt()}. Затем, если система завершает процесс +вашего приложения и пользователь возвращается к вашей операции, система повторно создает операцию и передает объект +{@link android.os.Bundle} в оба метода: {@link android.app.Activity#onCreate onCreate()} и {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()}. С помощью любого из этих +методов можно извлечь из объекта {@link android.os.Bundle} сохраненную информацию о состоянии операции и восстановить +ее. Если такая информация отсутствует, то объект {@link +android.os.Bundle} передается с нулевым значением (это происходит в случае, когда операция +создается в первый раз).</p> + +<img src="{@docRoot}images/fundamentals/restore_instance.png" alt="" /> +<p class="img-caption"><strong>Рисунок 2.</strong> Два способа возврата операции к отображению для пользователя +в неизмененном состоянии: уничтожение операции с последующим ее повторным созданием, когда операция должна восстановить свое +ранее сохраненное состояние, или остановка операции и ее последующее восстановление в неизмененном +состоянии.</p> + +<p class="note"><strong>Примечание.</strong> Нет никаких гарантий, что метод {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} будет вызван до того, как ваша +операция будет уничтожена, поскольку существуют случаи, когда нет необходимости сохранять состояние +(например, когда пользователь покидает вашу операцию нажатием кнопки <em>Назад</em>, +явным образом +закрывая ее). Если система вызывает метод {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()}, она делает это до вызова метода {@link +android.app.Activity#onStop onStop()} и, возможно, перед вызовом метода {@link android.app.Activity#onPause +onPause()}.</p> + +<p>Однако, даже если вы ничего не предпринимаете и не реализуете метод {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}, часть состояния операции восстанавливается +реализацией по умолчанию метода {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} класса {@link android.app.Activity}. В частности, реализация +по умолчанию вызывает соответствующий метод {@link +android.view.View#onSaveInstanceState onSaveInstanceState()} для каждого объекта {@link +android.view.View} в макете, благодаря чему каждое представление может предоставлять ту информацию о себе, +которую следует сохранить. Почти каждый виджет в платформе Android реализует этот метод необходимым +для себя способом так, что любые видимые изменения в пользовательском интерфейсе автоматически сохраняются и восстанавливаются при повторном +создании операции. Например, виджет {@link android.widget.EditText} сохраняет любой текст, +введенный пользователем, а виджет {@link android.widget.CheckBox} сохраняет информацию о том, был +ли установлен флажок. От вас требуется лишь указать уникальный идентификатор (с атрибутом <a href="{@docRoot}guide/topics/resources/layout-resource.html#idvalue">{@code android:id}</a>) +для каждого виджета, состояние которого необходимо сохранить. Если виджету не присвоен идентификатор, то системе +не удастся сохранить его состояние.</p> + +<div class="sidebox-wrapper"> +<div class="sidebox"> +<p>Вы также можете явно отключить сохранение информации о состоянии представления в макете. Для этого задайте для атрибута +{@link android.R.attr#saveEnabled android:saveEnabled} значение {@code "false"} или вызовите метод +{@link android.view.View#setSaveEnabled setSaveEnabled()}. Обычно отключать сохранение такой информации не требуется +, однако это может потребоваться в случаях, когда восстановить состояние пользовательского интерфейса операции необходимо другим образом.</p> +</div> +</div> + +<p>Несмотря на то что реализация метода {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} по умолчанию позволяет сохранить полезную информацию о +пользовательском интерфейсе вашей операции, вам по-прежнему может потребоваться переопределить ее для сохранения дополнительной информации. +Например, может потребоваться сохранить значения элементов, которые изменялись в течение жизненного цикла операции (которые могут +коррелировать со значениями, восстановленными в пользовательском интерфейсе, однако элементы, содержащие эти значения пользовательского интерфейса, по умолчанию не +были восстановлены).</p> + +<p>Поскольку реализация метода {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} по умолчанию позволяет сохранить состояние пользовательского интерфейса, в случае +, если вы переопределите метод с целью сохранить дополнительную информацию о состоянии, перед выполнением каких-либо действий вы всегда можете вызвать реализацию +суперкласса для метода +{@link android.app.Activity#onSaveInstanceState onSaveInstanceState()}. Точно так же реализацию суперкласса {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} следует вызывать в случае ее переопределения, чтобы реализация по умолчанию могла сохранить +состояния представлений.</p> + +<p class="note"><strong>Примечание.</strong> Поскольку вызов метода {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()} не гарантируется, +вам следует использовать его только для записи переходного состояния операции (состояние +пользовательского интерфейса) — никогда не используйте его для хранения постоянных данных. Вместо этого используйте метод {@link +android.app.Activity#onPause onPause()} для сохранения постоянных данных (например, тех, которые следует сохранить в +базу данных), когда пользователь покидает операцию.</p> + +<p>Отличный способ проверить возможность вашего приложения восстанавливать свое состояние — это просто повернуть +устройство для изменения ориентации экрана. При изменении ориентации экрана система +уничтожает и повторно создает операцию, чтобы применить альтернативные ресурсы, которые могут быть +доступны для новой конфигурации экрана. Только по одной этой причине крайне важно, чтобы ваша операция +могла полностью восстанавливать свое состояние при ее повторном создании, поскольку пользователи постоянно работают с +приложениями в разных ориентациях экрана.</p> + + +<h3 id="ConfigurationChanges">Обработка изменений в конфигурации</h3> + +<p>Некоторые конфигурации устройств могут изменяться в режиме выполнения +(например, ориентация экрана, доступность клавиатуры и язык). В таких случаях Android повторно создает выполняющуюся операцию +(система сначала вызывает метод {@link android.app.Activity#onDestroy}, а затем сразу же вызывает метод {@link +android.app.Activity#onCreate onCreate()}). Такое поведение позволяет +приложению учитывать новые конфигурации путем автоматической перезагрузки в приложение +альтернативных ресурсов, которые вы предоставили (например, различные макеты для +разных ориентаций и экранов разных размеров).</p> + +<p>Если операция разработана должным образом и должным образом поддерживает перезапуск после изменения ориентации экрана и +восстановление своего состояния, как описано выше, ваше приложение можно считать более устойчивым к другим +непредвиденным событиям в жизненном цикле операции.</p> + +<p>Лучший способ обработки такого перезапуска +— сохранить и восстановить состояние операции с помощью методов {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} и {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} (или {@link +android.app.Activity#onCreate onCreate()}), как описано в предыдущем разделе.</p> + +<p>Дополнительные сведения об изменениях конфигурации, происходящих в режиме выполнения, и о способах их обработки +представлены в руководстве<a href="{@docRoot}guide/topics/resources/runtime-changes.html">Обработка изменений в режиме +выполнения</a>.</p> + + + +<h3 id="CoordinatingActivities">Согласование операций</h3> + + <p>Когда одна операция запускает другую, в жизненных циклах обеих из них происходит переход из одного состояния в другое. Первая операция +приостанавливается и заврешается (однако она не будет остановлена, если она по-прежнему видима на фоне), а вторая +операция создается. В случае, если эти операции обмениваются данным, сохраненными на диске или в другом месте, важно понимать, +что первая операция не останавливается полностью до тех пор, пока не будет создана вторая операция. +Наоборот, процесс запуска второй операции накладывается на процесс остановки первой +операции.</p> + +<p>Порядок обратных вызовов жизненного цикла четко определен, в частности, когда в одном и том же процессе находятся две операции +, и одна из них запускает другую. Ниже представлен порядок выполнения действий в случае, когда операция +А запускает операцию Б. </p> + +<ol> +<li>Выполняется метод {@link android.app.Activity#onPause onPause()} операции А.</li> + +<li>Последовательно выполняются методы {@link android.app.Activity#onCreate onCreate()}, {@link +android.app.Activity#onStart onStart()} и {@link android.app.Activity#onResume onResume()} +операции Б. (Теперь для пользователя отображается операция Б.)</li> + +<li>Затем, если операция A больше не отображается на экране, выполняется ее метод {@link +android.app.Activity#onStop onStop()}.</li> +</ol> + + <p>Такая предсказуемая последовательность выполнения обратных вызовов жизненного цикла позволяет управлять переходом +информации из одной операции в другую. Например, если после остановки первой операции требуется выполнить запись в базу данных, +чтобы следующая операция могла считать их, то запись в базу данных следует выполнить +во время выполнения метода {@link android.app.Activity#onPause onPause()}, а не во время выполнения метода {@link +android.app.Activity#onStop onStop()}.</p> + +<!-- +<h2>Beginner's Path</h2> + +<p>For more information about how Android maintains a history of activities and +enables user multitasking, continue with the <b><a +href="{@docRoot}guide/components/tasks-and-back-stack.html">Tasks and Back +Stack</a></b> document.</p> +--> diff --git a/docs/html-intl/intl/ru/guide/components/bound-services.jd b/docs/html-intl/intl/ru/guide/components/bound-services.jd new file mode 100644 index 000000000000..ad690b776ef0 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/bound-services.jd @@ -0,0 +1,658 @@ +page.title=Привязанные службы +parent.title=Службы +parent.link=services.html +@jd:body + + +<div id="qv-wrapper"> +<ol id="qv"> +<h2>Содержание документа</h2> +<ol> + <li><a href="#Basics">Основы</a></li> + <li><a href="#Creating">Создание привязанной службы</a> + <ol> + <li><a href="#Binder">Расширение класса Binder</a></li> + <li><a href="#Messenger">Использование объекта Messenger</a></li> + </ol> + </li> + <li><a href="#Binding">Привязка к службе</a></li> + <li><a href="#Lifecycle">Управление жизненным циклом привязанной службы</a></li> +</ol> + +<h2>Ключевые классы</h2> +<ol> + <li>{@link android.app.Service}</li> + <li>{@link android.content.ServiceConnection}</li> + <li>{@link android.os.IBinder}</li> +</ol> + +<h2>Примеры</h2> +<ol> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html">{@code + RemoteService}</a></li> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html">{@code + LocalService}</a></li> +</ol> + +<h2>См. также:</h2> +<ol> + <li><a href="{@docRoot}guide/components/services.html">Службы</a></li> +</ol> +</div> + + +<p>Привязанная служба предоставляет интерфейс типа клиент-сервер. Привязанная служба позволяет компонентам приложения +(например, операциям) взаимодействовать со службой, отправлять запросы, получать результаты и даже делать то же самое с другими процессами через +IPC. Привязанная служба обычно работает, пока другой компонент приложения +привязан к ней. Она не работает постоянно в фоновом режиме.</p> + +<p>В этом документе рассказывается, как создать привязанную службу, включая привязку +службы к другим компонентам приложения. Также рекомендуем обратиться к статье <a href="{@docRoot}guide/components/services.html">Службы</a>, чтобы узнать подробнее +о службах, например, об организации отправки уведомлений от службы, настройке службы +на работу на переднем плане и т. д.</p> + + +<h2 id="Basics">Основы</h2> + +<p>Привязанная служба представляет собой реализацию класса {@link android.app.Service}, которая позволяет +другим приложениям привязываться к нему и взаимодействовать с ним. Чтобы обеспечить привязку службы +, сначала необходимо реализовать метод обратного вызова {@link android.app.Service#onBind onBind()}. Этот +метод возвращает объект {@link android.os.IBinder}. Он определяет программный интерфейс, +с помощью которого клиенты могут взаимодействовать со службой.</p> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h3>Привязка к запущенной службе</h3> + +<p>Как указано в статье <a href="{@docRoot}guide/components/services.html">Службы</a>, +можно создать службу, которая одновременно и запущена, и привязана. Это означает, что службу можно запустить +путем вызова метода {@link android.content.Context#startService startService()}, который позволяет службе +работать неограниченное время, а также позволяет клиентам привязываться к ней с помощью вызова метода {@link +android.content.Context#bindService bindService()}. + <p>Если разрешить запуск и привязку службы, то после ее запуска +система <em>не</em> уничтожает ее после отмены всех привязок клиентов. Вместо этого необходимо +явным образом остановить службу, вызвав метод {@link android.app.Service#stopSelf stopSelf()} или {@link +android.content.Context#stopService stopService()}.</p> + +<p>Несмотря на то, что обычно необходимо реализовывать либо метод {@link android.app.Service#onBind onBind()}, +<em>либо метод</em> {@link android.app.Service#onStartCommand onStartCommand()}, в некоторых случаях требуется +реализовать оба этих метода. Например, в музыкальном проигрывателе может оказаться полезным разрешить выполнение службы в течение +неограниченного времени, а также обеспечить ее привязку. Таким образом, операция может запустить службу для воспроизведения музыки +, которая будет воспроизводиться даже после выхода пользователя из приложения. После того возвращения +пользователя к приложению операция может отменить привязку к службе, чтобы вернуть управление воспроизведением.</p> + +<p>Обязательно ознакомьтесь с разделом, посвященным <a href="#Lifecycle">управлению жизненным циклом привязанной +службы</a>. В нем представлены подробные сведения о жизненном цикле службы, привязанной к уже +запущенной службе.</p> +</div> +</div> + +<p>Для привязки к службе клиент может вызвать метод {@link android.content.Context#bindService +bindService()}. После привязки он должен предоставить реализацию метода {@link +android.content.ServiceConnection}, который служит для отслеживания подключения к службе. Метод {@link +android.content.Context#bindService bindService()} возвращается незамедлительно без значения, однако +, когда система Android устанавливает подключение +клиент-служба, она вызывает метод {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} для {@link +android.content.ServiceConnection}, чтобы выдать объект {@link android.os.IBinder}, который +клиент может использовать для взаимодействия со службой.</p> + +<p>Одновременно к службе могут подключиться сразу несколько клиентов. Однако система вызывает метод +{@link android.app.Service#onBind onBind()} вашей службы для получения объекта {@link android.os.IBinder} +только при первой привязке клиента. После чего система выдает такой же объект {@link android.os.IBinder} для любых +дополнительных клиентов, которые выполняют привязку, без повторного вызова метода {@link android.app.Service#onBind onBind()}.</p> + +<p>Когда отменяется привязка последнего клиента от службы, система уничтожает службу (если только +служба не была так же запущена методом {@link android.content.Context#startService startService()}).</p> + +<p>Самую важную роль в реализации привязанной службы играет определение интерфейса, +который возвращает ваш метод обратного вызова {@link android.app.Service#onBind onBind()}. Существует несколько +различных способов определения интерфейса {@link android.os.IBinder} службы. Каждый из них рассматривается в следующем +разделе.</p> + + + +<h2 id="Creating">Создание привязанной службы</h2> + +<p>При создании службы, обеспечивающей привязку, требуется объект {@link android.os.IBinder}, +который обеспечивает программный интерфейс, с помощью которого клиенты могут взаимодействовать со службой. Существует +три способа определения такого интерфейса:</p> + +<dl> + <dt><a href="#Binder">Расширение класса Binder</a></dt> + <dd>Если служба является частной и предоставляется в рамках вашего собственного приложения, а также выполняется в том же процессе, что и клиент +(общий процесс), создавать интерфейс следует путем расширения класса {@link android.os.Binder} +и возврата его экземпляра из метода +{@link android.app.Service#onBind onBind()}. Клиент получает объект {@link android.os.Binder}, +после чего он может использовать его для получения прямого доступа к общедоступным методам, имеющимся либо в реализации {@link android.os.Binder}, +либо даже в {@link android.app.Service}. + <p>Этот способ является предпочтительным, когда служба просто выполняется в фоновом режиме для +вашего приложения. Этот способ не подходит для создания интерфейса только тогда, +когда ваша служба используется другими приложениями или в отдельных процессах.</dd> + + <dt><a href="#Messenger">Использование объекта Messenger</a></dt> + <dd>Если необходимо, чтобы интерфейс службы был доступен для разных процессов, его можно создать +с помощью объекта {@link android.os.Messenger}. Таким образом, служба +определяет объект {@link android.os.Handler}, соответствующий различным типам объектов {@link +android.os.Message}. Этот объект {@link android.os.Handler} +является основой для объекта {@link android.os.Messenger}, который, в свою очередь, предоставляет клиенту объект {@link android.os.IBinder}, +благодаря чему последний может отправлять команды в службу с помощью объектов {@link +android.os.Message}. Кроме того, клиент может определить объект {@link android.os.Messenger} для самого +себя, что позволяет службе возвращать сообщения клиенту. + <p>Это самый простой способ организовать взаимодействие процессов, поскольку {@link +android.os.Messenger} организует очередь всех запросов в рамках одного потока, поэтому вам не нужно делать +свою службу потокобезопасной.</p> + </dd> + + <dt>Использование языка AIDL</dt> + <dd>AIDL (Android Interface Definition Language) выполняет всю работу по разделению объектов на +примитивы, которые операционная система может распознать и распределить по процессам для организации +взаимодействия между ними (IPC). Предыдущий способ с использованием объекта {@link android.os.Messenger} фактически основан на AIDL, поскольку это его +базовая структура. Как уже упоминалось выше, объект {@link android.os.Messenger} создает очередь из всех +запросов клиентов в рамках одного потока, поэтому служба одновременно получает только один запрос. Однако, +если необходимо, чтобы служба обрабатывала одновременно сразу несколько запросов, можно использовать AIDL +напрямую. В таком случае ваша служба должна поддерживать многопоточность и должна быть потокобезопасной. + <p>Чтобы использовать AIDL напрямую, необходимо +создать файл {@code .aidl}, который определяет программный интерфейс. Этот файл используется инструментами SDK Android для +создания абстрактного класса, который реализует интерфейс и обеспечивает взаимодействие процессов, и который +в дальнейшем можно расширить в службе.</p> + </dd> +</dl> + + <p class="note"><strong>Примечание.</strong> В большинстве приложений <strong>не следует</strong> использовать AIDL для +создания и привязки службы, поскольку для этого может потребоваться поддержка многопоточности, что, в свою очередь, может привести +к более сложной реализации. Поэтому AIDL не подходит для большинства приложений, +и в этой статье мы не будем рассматривать использование этого способа для вашей службы. Если же вы точно уверены, что +вам необходимо использовать AIDL напрямую, обратитесь к статье +<a href="{@docRoot}guide/components/aidl.html">AIDL</a>.</p> + + + + +<h3 id="Binder">Расширение класса Binder</h3> + +<p>Если ваша служба используется только локальным приложением и не взаимодействует с разными процессами, +можно реализовать собственный класс {@link android.os.Binder}, с помощью которого клиент получает прямой +доступ к общедоступным методам в службе.</p> + +<p class="note"><strong>Примечание.</strong> Этот вариант подходит только в том случае, если клиент и служба выполняются внутри +одного приложения и процесса, что является наиболее распространенной ситуацией. Например, расширение класса отлично подойдет для +музыкального приложения, в котором необходимо привязать операцию к собственной службе приложения, которая +воспроизводит музыку в фоновом режиме.</p> + +<p>Вот как это сделать:</p> +<ol> + <li>Создайте в вашей службе экземпляр класса {@link android.os.Binder} со следующими характеристиками: + <ul> + <li>экземпляр содержит общедоступные методы, которые может вызывать клиент; либо</li> + <li>экземпляр возвращает текущий экземпляр класса {@link android.app.Service}, содержащий +общедоступные методы, которые может вызывать клиент; или</li> + <li>экземпляр возвращает экземпляр другого класса, размещенного в службе, содержащий общедоступные методы, +которые может вызывать клиент.</li> + </ul> + <li>Верните этот экземпляр класса {@link android.os.Binder} из метода обратного вызова {@link +android.app.Service#onBind onBind()}.</li> + <li>В клиенте получите класс {@link android.os.Binder} от метода обратного вызова {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} и +выполните вызовы к привязанной службе с помощью предоставленных методов.</li> +</ol> + +<p class="note"><strong>Примечание.</strong> Служба и клиент должны выполняться в одном и том же приложении +, поскольку в этом случае клиент может транслировать возвращенный объект и надлежащим образом вызывать его API-интерфейсы. Кроме того, служба +и клиент должны выполняться в рамках одного и того же процесса, поскольку этот способ не подразумевает какого-либо +распределения по процессам.</p> + +<p>Ниже представлен пример службы, которая предоставляет клиентам доступ к методам посредством реализации класса +{@link android.os.Binder}:</p> + +<pre> +public class LocalService extends Service { + // Binder given to clients + private final IBinder mBinder = new LocalBinder(); + // Random number generator + private final Random mGenerator = new Random(); + + /** + * Class used for the client Binder. Because we know this service always + * runs in the same process as its clients, we don't need to deal with IPC. + */ + public class LocalBinder extends Binder { + LocalService getService() { + // Return this instance of LocalService so clients can call public methods + return LocalService.this; + } + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + /** method for clients */ + public int getRandomNumber() { + return mGenerator.nextInt(100); + } +} +</pre> + +<p>Объект {@code LocalBinder} предоставляет для клиентов метод {@code getService()}, чтобы они могли получить +текущий экземпляр класса {@code LocalService}. Благодаря этому клиенты могут вызывать общедоступные методы в +службе. Например, клиенты могут вызвать метод {@code getRandomNumber()} из службы.</p> + +<p>Ниже представлен пример операции, которая выполняет привязку к классу {@code LocalService} и вызывает метод {@code getRandomNumber()} +при нажатии кнопки:</p> + +<pre> +public class BindingActivity extends Activity { + LocalService mService; + boolean mBound = false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + } + + @Override + protected void onStart() { + super.onStart(); + // Bind to LocalService + Intent intent = new Intent(this, LocalService.class); + bindService(intent, mConnection, Context.BIND_AUTO_CREATE); + } + + @Override + protected void onStop() { + super.onStop(); + // Unbind from the service + if (mBound) { + unbindService(mConnection); + mBound = false; + } + } + + /** Called when a button is clicked (the button in the layout file attaches to + * this method with the android:onClick attribute) */ + public void onButtonClick(View v) { + if (mBound) { + // Call a method from the LocalService. + // However, if this call were something that might hang, then this request should + // occur in a separate thread to avoid slowing down the activity performance. + int num = mService.getRandomNumber(); + Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show(); + } + } + + /** Defines callbacks for service binding, passed to bindService() */ + private ServiceConnection mConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName className, + IBinder service) { + // We've bound to LocalService, cast the IBinder and get LocalService instance + LocalBinder binder = (LocalBinder) service; + mService = binder.getService(); + mBound = true; + } + + @Override + public void onServiceDisconnected(ComponentName arg0) { + mBound = false; + } + }; +} +</pre> + +<p>В примере выше показано, как клиент привязывается к службе с помощью реализации +{@link android.content.ServiceConnection} и обратного вызова {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()}. В +следующем разделе представлена более подробная информация об этом процессе привязки к службе.</p> + +<p class="note"><strong>Примечание.</strong> В примере выше не выполняется явная отмена привязки к службе, +однако всем клиентам следует отменять привязку в соответствующие сроки (например, когда операция приостанавливается).</p> + +<p>Примеры кода представлены в статьях, посвященных классам <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html">{@code +LocalService.java}</a> и <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalServiceActivities.html">{@code +LocalServiceActivities.java}</a>, в <a href="{@docRoot}resources/samples/ApiDemos/index.html">ApiDemos</a>.</p> + + + + + +<h3 id="Messenger">Использование объекта Messenger</h3> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h4>Сравнение с AIDL</h4> + <p>Когда необходимо организовать взаимодействие между процессами, использование объекта {@link android.os.Messenger} для интерфейса +намного проще реализации с помощью AIDL, поскольку объект {@link android.os.Messenger} помещает +в очередь все запросы к службе, тогда как интерфейс, основанный исключительно на AIDL, отправляет службе несколько запросов одновременно, +для чего требуется поддержка многопоточности.</p> + <p>В большинстве приложений служба не должна поддерживать многопоточность, поэтому при использовании объекта {@link +android.os.Messenger} служба может одновременно обрабатывать один запрос. Если вам важно, +чтобы служба была многопоточной, для определения интерфейса следует использовать <a href="{@docRoot}guide/components/aidl.html">AIDL</a>.</p> +</div> +</div> + +<p>Если необходимо, чтобы служба взаимодействовала с удаленными процессами, для предоставления интерфейса службы можно воспользоваться объектом +{@link android.os.Messenger}. Такой подход +позволяет организовать взаимодействие между процессами (IPC) без необходимости использовать AIDL.</p> + +<p>Вот краткий обзор того, как следует использовать объект {@link android.os.Messenger}:</p> + +<ul> + <li>Служба реализует объект {@link android.os.Handler}, который получает обратный вызов для каждого +вызова от клиента.</li> + <li>Объект {@link android.os.Handler} используется для создания объекта {@link android.os.Messenger} +(который является ссылкой на объект {@link android.os.Handler}).</li> + <li>Объект {@link android.os.Messenger} создает объект {@link android.os.IBinder}, который служба +возвращает клиентам из метода {@link android.app.Service#onBind onBind()}.</li> + <li>Клиенты используют полученный объект {@link android.os.IBinder} для создания экземпляра объекта {@link android.os.Messenger} +(который ссылается на объект {@link android.os.Handler} службы), используемого клиентом для отправки объектов +{@link android.os.Message} в службу.</li> + <li>Служба получает каждый объект {@link android.os.Message} в своем объекте {@link +android.os.Handler} — в частности, в методе {@link android.os.Handler#handleMessage +handleMessage()}.</li> +</ul> + + +<p>Таким образом, для клиента отсутствуют «методы» для отправки вызова службе. Вместо этого +клиент отправляет «сообщения» (объекты {@link android.os.Message}), которые служба получает в +своем объекте {@link android.os.Handler}.</p> + +<p>Ниже представлен пример службы, которая использует интерфейс {@link android.os.Messenger}:</p> + +<pre> +public class MessengerService extends Service { + /** Command to the service to display a message */ + static final int MSG_SAY_HELLO = 1; + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SAY_HELLO: + Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show(); + break; + default: + super.handleMessage(msg); + } + } + } + + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + final Messenger mMessenger = new Messenger(new IncomingHandler()); + + /** + * When binding to the service, we return an interface to our messenger + * for sending messages to the service. + */ + @Override + public IBinder onBind(Intent intent) { + Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show(); + return mMessenger.getBinder(); + } +} +</pre> + +<p>Обратите внимание, что метод {@link android.os.Handler#handleMessage handleMessage()} в объекте +{@link android.os.Handler} — это место, где служба получает входящие объекты {@link android.os.Message} +и решает, что делать дальше, руководствуясь элементом {@link android.os.Message#what}.</p> + +<p>Клиенту требуется лишь создать объект {@link android.os.Messenger} на основе объекта {@link +android.os.IBinder}, возвращенного службой, и отправить сообщение с помощью метода {@link +android.os.Messenger#send send()}. Ниже представлен пример того, как простая операция выполняет привязку к +службе и отправляет ей сообщение {@code MSG_SAY_HELLO}:</p> + +<pre> +public class ActivityMessenger extends Activity { + /** Messenger for communicating with the service. */ + Messenger mService = null; + + /** Flag indicating whether we have called bind on the service. */ + boolean mBound; + + /** + * Class for interacting with the main interface of the service. + */ + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + // This is called when the connection with the service has been + // established, giving us the object we can use to + // interact with the service. We are communicating with the + // service using a Messenger, so here we get a client-side + // representation of that from the raw IBinder object. + mService = new Messenger(service); + mBound = true; + } + + public void onServiceDisconnected(ComponentName className) { + // This is called when the connection with the service has been + // unexpectedly disconnected -- that is, its process crashed. + mService = null; + mBound = false; + } + }; + + public void sayHello(View v) { + if (!mBound) return; + // Create and send a message to the service, using a supported 'what' value + Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0); + try { + mService.send(msg); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + } + + @Override + protected void onStart() { + super.onStart(); + // Bind to the service + bindService(new Intent(this, MessengerService.class), mConnection, + Context.BIND_AUTO_CREATE); + } + + @Override + protected void onStop() { + super.onStop(); + // Unbind from the service + if (mBound) { + unbindService(mConnection); + mBound = false; + } + } +} +</pre> + +<p>Обратите внимание, что в этом примере не показано, как служба отвечает клиенту. Если требуется, +чтобы служба реагировала, необходимо создать объект {@link android.os.Messenger} и в клиенте. Затем, +когда клиент получает обратный вызов {@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()}, она отправляет в службу объект {@link android.os.Message}, который включает объект +{@link android.os.Messenger} клиента в качестве значения параметра {@link android.os.Message#replyTo} +метода {@link android.os.Messenger#send send()}.</p> + +<p>Пример организации двустороннего обмена сообщениями приведен в примерах кода <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/MessengerService.html">{@code +MessengerService.java}</a> (служба) и <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/MessengerServiceActivities.html">{@code +MessengerServiceActivities.java}</a> (клиент).</p> + + + + + +<h2 id="Binding">Привязка к службе</h2> + +<p>Для привязки к службе компоненты приложения (клиенты) могут использовать метод +{@link android.content.Context#bindService bindService()}. После этого система Android +вызывает метод {@link android.app.Service#onBind +onBind()} службы, который возвращает объект {@link android.os.IBinder} для взаимодействия со службой.</p> + +<p>Привязка выполняется асинхронно. {@link android.content.Context#bindService +bindService()} возвращается сразу же и <em>не</em> возвращает клиенту объект +{@link android.os.IBinder}. Для получения объекта {@link android.os.IBinder} клиенту необходимо создать экземпляр {@link +android.content.ServiceConnection} и передать его в метод {@link android.content.Context#bindService +bindService()}. Интерфейс {@link android.content.ServiceConnection} включает метод обратного вызова, +который система использует для того, чтобы выдать объект {@link android.os.IBinder}.</p> + +<p class="note"><strong>Примечание.</strong> Выполнить привязку к службе могут только операции, другие службы и поставщики контента +— вы <strong>не можете</strong> самостоятельно выполнить привязку к службе из ресивера.</p> + +<p>Поэтому для привязки к службе из клиента необходимо выполнить указанные ниже действия. </p> +<ol> + <li>Реализуйте интерфейс {@link android.content.ServiceConnection}. + <p>Ваша реализация должна переопределять два метода обратного вызова:</p> + <dl> + <dt>{@link android.content.ServiceConnection#onServiceConnected onServiceConnected()}</dt> + <dd>Система вызывает этот метод, чтобы выдать объект {@link android.os.IBinder}, возвращенный методом +{@link android.app.Service#onBind onBind()}службы.</dd> + <dt>{@link android.content.ServiceConnection#onServiceDisconnected +onServiceDisconnected()}</dt> + <dd>Система Android вызывает этот метод в случае непредвиденной потери +подключения к службе, например при сбое в работе службы или в случае ее завершения. Этот метод <em>не</em> вызывается, когда клиент +отменяет привязку.</dd> + </dl> + </li> + <li>Вызовите метод {@link +android.content.Context#bindService bindService()}, передав в него реализацию интерфейса {@link +android.content.ServiceConnection}. </li> + <li>Когда система вызывает ваш метод обратного вызова {@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()}, вы можете приступить к выполнению вызовов к службе с помощью +методов, определенных интерфейсом.</li> + <li>Чтобы отключиться от службы, вызовите метод {@link +android.content.Context#unbindService unbindService()}. + <p>В случае уничтожения клиента выполняется отмена его привязки к службе, однако вам всегда следует отменять +привязку по завершении взаимодействия со службой или в случае приостановки операции, чтобы служба +могла завершить свою работу, когда она не используется. (Более подробно подходящее время для привязки и ее отмены +рассматриваются далее в этом документе).</p> + </li> +</ol> + +<p>Ниже представлен пример фрагмента кода для подключения клиента к созданной выше службе путем +<a href="#Binder">расширения класса Binder</a> — клиенту нужно лишь передать возвращенный объект +{@link android.os.IBinder} в класс {@code LocalService} и запросить экземпляр {@code +LocalService}:</p> + +<pre> +LocalService mService; +private ServiceConnection mConnection = new ServiceConnection() { + // Called when the connection with the service is established + public void onServiceConnected(ComponentName className, IBinder service) { + // Because we have bound to an explicit + // service that is running in our own process, we can + // cast its IBinder to a concrete class and directly access it. + LocalBinder binder = (LocalBinder) service; + mService = binder.getService(); + mBound = true; + } + + // Called when the connection with the service disconnects unexpectedly + public void onServiceDisconnected(ComponentName className) { + Log.e(TAG, "onServiceDisconnected"); + mBound = false; + } +}; +</pre> + +<p>С помощью этого интерфейса {@link android.content.ServiceConnection} клиент может выполнить привязку к службе, передав ее в +метод {@link android.content.Context#bindService bindService()}. Например:</p> + +<pre> +Intent intent = new Intent(this, LocalService.class); +bindService(intent, mConnection, Context.BIND_AUTO_CREATE); +</pre> + +<ul> + <li>Первый параметр в методе {@link android.content.Context#bindService bindService()} представляет собой объект +{@link android.content.Intent}, который явным образом именует службу для привязки (хотя переход может быть +и неявным).</li> +<li>Второй параметр — это объект {@link android.content.ServiceConnection}.</li> +<li>Третий параметр представляет собой флаг, указывающий параметры привязки. Обычно им является {@link +android.content.Context#BIND_AUTO_CREATE}, создающий службу, если она уже не выполняется. +Другие возможные значения: {@link android.content.Context#BIND_DEBUG_UNBIND} +и {@link android.content.Context#BIND_NOT_FOREGROUND} или {@code 0}, если значение отсутствует.</li> +</ul> + + +<h3>Дополнительные примечания</h3> + +<p>Ниже представлены некоторые важные замечания о привязке к службе.</p> +<ul> + <li>Всегда отлавливайте исключения {@link android.os.DeadObjectException}, которые выдаются +при возникновении сбоя подключения. Это единственное исключение, которое выдается удаленными методами.</li> + <li>Для объектов всегда учитываются ссылки на них в процессах. </li> + <li>Обычно необходимо связать привязку и ее отмену во время +сопоставления моментов подключения и отключения в жизненном цикле клиента. Например: + <ul> + <li>Если взаимодействие со службой требуется лишь в то время, когда операция отображается, привязку +необходимо выполнить во время метода {@link android.app.Activity#onStart onStart()}, а отмену привязки — во время выполнения метода {@link +android.app.Activity#onStop onStop()}.</li> + <li>Если необходимо, чтобы операция получала ответы даже в случае ее остановки во время работы в фоновом режиме, +то привязку можно выполнить во время {@link android.app.Activity#onCreate onCreate()}, а отмену привязки + — во время выполнения {@link android.app.Activity#onDestroy onDestroy()}. Однако следует помнить, что такой способ подразумевает, +что вашей операции необходимо использовать службу все время, пока она выполняется (даже если она выполняется в фоновом режиме). Поэтому, +если служба находится в другом процессе, вы тем самым утяжеляете процесс, а это +повышает вероятность того, что система завершит его.</li> + </ul> + <p class="note"><strong>Примечание.</strong> Обычно <strong>не</strong> следует выполнять привязку или отменять ее +во время выполнения методов {@link android.app.Activity#onResume onResume()} и {@link +android.app.Activity#onPause onPause()} вашей операции, поскольку такие обратные вызовы происходят при каждом переходе из одного состояния в другое, + а обработка данных, выполняемая при таких переходах, должна быть минимальной. Кроме того, если к одной и той же +службе привязано несколько операций в вашем приложении, и имеется переход между +двумя этими операциями, служба может быть уничтожена и создана повторно, поскольку текущая операция выполняет отмену привязки +(во время приостановки) до того, как следующая служба выполнит привязку (во время возобновления). (Подробные сведения о согласовании жизненных циклов операций при таких переходах +представлены в статье +<a href="{@docRoot}guide/components/activities.html#CoordinatingActivities">Операции</a>.)</p> +</ul> + +<p>Пример кода, в котором показан порядок привязки к службе, см. в статье, посвященной классу <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html">{@code +RemoteService.java}</a>, в <a href="{@docRoot}resources/samples/ApiDemos/index.html">ApiDemos</a>.</p> + + + + + +<h2 id="Lifecycle">Управление жизненным циклом привязанной службы</h2> + +<p>Когда выполняется отмена привязки службы ко всем клиентам, система Android уничтожает такую службу (если она не была запущена +вместе с {@link android.app.Service#onStartCommand onStartCommand()}). В таком случае вам не нужно +управлять жизненным циклом своей службы, если она исключительно привязанная служба +— система Android управляет ей за вас на основании привязки службы к любым другим клиентам.</p> + +<p>Однако, если вы решите реализовать метод обратного вызова {@link android.app.Service#onStartCommand +onStartCommand()}, вам необходимо явным образом остановить службу, поскольку в +этом случае она считается <em>запущенной</em>. В таком случае служба выполняется до тех пор, пока +сама не остановит свою работу с помощью метода {@link android.app.Service#stopSelf()} или до тех пор, пока другой компонент не вызовет метод {@link +android.content.Context#stopService stopService()}, независимо от привязки службы к каким-либо +клиентам.</p> + +<p>Кроме того, если ваша служба запущена и принимает привязку, то при вызове системой +вашего метода {@link android.app.Service#onUnbind onUnbind()} вы также можете вернуть +{@code true}, если желаете получить вызов к {@link android.app.Service#onRebind +onRebind()} при следующей привязке к службе (вместо получения вызова к методу {@link +android.app.Service#onBind onBind()}). Метод {@link android.app.Service#onRebind +onRebind()} возвращает значение void, однако клиент по-прежнему получает объект {@link android.os.IBinder} в своем методе обратного вызова +{@link android.content.ServiceConnection#onServiceConnected onServiceConnected()}. +На рисунке 1 ниже иллюстрируется логика жизненного цикла такого рода.</p> + + +<img src="{@docRoot}images/fundamentals/service_binding_tree_lifecycle.png" alt="" /> +<p class="img-caption"><strong>Рисунок 1.</strong> Жизненный цикл запущенной службы, +для которой выполняется привязка.</p> + + +<p>Дополнительные сведения о жизненном цикле уже запущенной службы представлены в статье <a href="{@docRoot}guide/components/services.html#Lifecycle">Службы</a>.</p> + + + + diff --git a/docs/html-intl/intl/ru/guide/components/fragments.jd b/docs/html-intl/intl/ru/guide/components/fragments.jd new file mode 100644 index 000000000000..b13fcc3da7ef --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/fragments.jd @@ -0,0 +1,812 @@ +page.title=Фрагменты +parent.title=Операции +parent.link=activities.html +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + <h2>Содержание документа</h2> + <ol> + <li><a href="#Design">Философия проектирования</a></li> + <li><a href="#Creating">Создание фрагмента</a> + <ol> + <li><a href="#UI">Добавление пользовательского интерфейса</a></li> + <li><a href="#Adding">Добавление фрагмента в операцию</a></li> + </ol> + </li> + <li><a href="#Managing">Управление фрагментами</a></li> + <li><a href="#Transactions">Выполнение транзакций с фрагментами</a></li> + <li><a href="#CommunicatingWithActivity">Взаимодействие с операцией</a> + <ol> + <li><a href="#EventCallbacks">Создание обратного вызова события для операции</a></li> + <li><a href="#ActionBar">Добавление элементов в строку действий</a></li> + </ol> + </li> + <li><a href="#Lifecycle">Управление жизненным циклом фрагмента</a> + <ol> + <li><a href="#CoordinatingWithActivity">Согласование с жизненным циклом операции</a></li> + </ol> + </li> + <li><a href="#Example">Пример:</a></li> + </ol> + + <h2>Ключевые классы</h2> + <ol> + <li>{@link android.app.Fragment}</li> + <li>{@link android.app.FragmentManager}</li> + <li>{@link android.app.FragmentTransaction}</li> + </ol> + + <h2>См. также:</h2> + <ol> + <li><a href="{@docRoot}training/basics/fragments/index.html">Создание динамического интерфейса пользователя с использованием фрагментов</a></li> + <li><a href="{@docRoot}guide/practices/tablets-and-handsets.html">Поддержка планшетов +и смартфонов</a></li> + </ol> +</div> +</div> + +<p>Фрагмент (класс {@link android.app.Fragment}) представляет поведение или часть пользовательского интерфейса в операции +(класс {@link android.app.Activity}). Разработчик может объединить несколько фрагментов в одну операцию для построения +многопанельного пользовательского интерфейса и повторного использования фрагмента в нескольких операциях. Фрагмент можно рассматривать как + модульную часть операции. Такая часть имеет свой жизненный цикл и самостоятельно обрабатывает события ввода. Кроме того, +ее можно добавить или удалить непосредственно во время выполнения операции. Это нечто вроде вложенной операции, которую +можно многократно использовать в различных операциях.</p> + +<p>Фрагмент всегда должен быть встроен в операцию, и на его жизненный цикл напрямую +влияет жизненный цикл операции. Например, когда операция приостановлена, в том же состоянии находятся и все +фрагменты внутри нее, а когда операция уничтожается, уничтожаются и все фрагменты. Однако пока +операция выполняется (это соответствует состоянию <em>возобновлена</em> <a href="{@docRoot}guide/components/activities.html#Lifecycle">жизненного цикла</a>), можно +манипулировать каждым фрагментом независимо, например добавлять или удалять их. Когда разработчик выполняет такие +транзакции с фрагментами, он может также добавить их в стек переходов назад, которым управляет +операция. Каждый элемент стека переходов назад в операции является записью + выполненной транзакции с фрагментом. Стек переходов назад позволяет пользователю обратить транзакцию с фрагментом (выполнить навигацию в обратном направлении), +нажимая кнопку <em>Назад</em>.</p> + +<p>Когда фрагмент добавлен как часть макета операции, он находится в объекте {@link +android.view.ViewGroup} внутри иерархии представлений операции и определяет собственный +макет представлений. +Разработчик может вставить фрагмент в макет операции двумя способами. Для этого следует объявить фрагмент в +файле макета операции как элемент {@code <fragment>} или добавить его в +существующий объект{@link android.view.ViewGroup} в коде приложения. Впрочем, фрагмент не обязан быть частью +макета операции. Можно использовать фрагмент без интерфейса в качестве невидимого рабочего потока +операции.</p> + +<p>В этом документе показано, как построить приложение, использующее фрагменты. В частности, обсуждается, +как фрагменты могут поддерживать свое состояние, когда они добавляются в стек переходов назад операции, использовать +события совместно с операцией и другими фрагментами внутри нее, выводить данные в +строку действий операции и т. д.</p> + + +<h2 id="Design">Философия проектирования</h2> + +<p>Фрагменты впервые появились в Android версии 3.0 (API уровня 11), главным образом, для обеспечения +большей динамичности и гибкости пользовательских интерфейсов на больших экранах, например, у планшетов. Поскольку экраны +планшетов гораздо больше, чем у смартфонов, они предоставляют больше возможностей для объединения и +перестановки компонентов пользовательского интерфейса. Фрагменты позволяют делать это, избавляя разработчика от необходимости управлять +сложными изменениями в иерархии представлений. Разбивая макет операции на фрагменты, разработчик получает возможность +модифицировать внешний вид операции в ходе выполнения и сохранять эти изменения в стеке переходов назад, +которым управляет операция.</p> + +<p>Например, новостное приложение может использовать один фрагмент для показа списка статей слева, +а другой—для отображения статьи справа. Оба фрагмента отображаются за +одну операцию рядом друг с другом, и каждый имеет собственный набор методов обратного вызова жизненного цикла и управляет +собственными событиями пользовательского ввода. Таким образом, вместо применения одной операции для выбора статьи, а другой + — для чтения статей, пользователь может выбрать статью и читать ее в рамках одной +операции, как на планшете, изображенном на рисунке 1.</p> + +<p>Следует разрабатывать каждый фрагмент как модульный и повторно используемый компонент операции. Поскольку + каждый фрагмент определяет собственный макет и собственное поведение со своими обратными вызовами жизненного цикла, разработчик может +включить один фрагмент в несколько операций. Поэтому он должен предусмотреть повторное использование фрагмента и не допускать, +чтобы один фрагмент непосредственно манипулировал другим. Это особенно важно, потому что модульность фрагментов +позволяет изменять их сочетания в соответствии с различными размерами экранов. Если +приложение должно работать и на планшетах, и на смартфонах, можно повторно использовать фрагменты в различных +конфигурациях макета, чтобы оптимизировать взаимодействие с пользователем в зависимости от доступного размера экрана. Например, +на смартфоне может возникнуть необходимость в разделении фрагментов для предоставления однопанельного пользовательского интерфейса, если + разработчику не удается поместить более одного фрагмента в одну операцию.</p> + +<img src="{@docRoot}images/fundamentals/fragments.png" alt="" /> +<p class="img-caption"><strong>Рисунок 1.</strong> Пример того, как два модуля пользовательского интерфейса, определенные +фрагментами, могут быть объединены внутри одной операции для работы на планшетах, но разделены на +смартфонах.</p> + +<p>Вернемся к примеру с новостным приложением. Оно может иметь +два фрагмента, встроенных в <em>Операцию А</em>, когда выполняется на устройстве планшетного формата. В то же время на +экране смартфона недостаточно места для обоих фрагментов, и поэтому <em>Операция А</em> включает в себя +только фрагмент со списком статей. Когда пользователь выбирает статью, запускается +<em>Операция В</em>, содержащая второй фрагмент для чтения статьи. Таким образом, приложение +поддерживает как планшеты, так и смартфоны благодаря повторному использованию фрагментов в различных сочетаниях, как показано на +рисунке 1.</p> + +<p>Подробные сведения относительно разработки приложения с различными сочетаниями фрагментов для +различных конфигураций экрана приводятся в руководстве <a href="{@docRoot}guide/practices/tablets-and-handsets.html">Поддержка планшетов и смартфонов</a></p> + + + +<h2 id="Creating">Создание фрагмента</h2> + +<div class="figure" style="width:327px"> +<img src="{@docRoot}images/fragment_lifecycle.png" alt="" /> +<p class="img-caption"><strong>Рисунок 2.</strong> Жизненный цикл фрагмента (во время +выполнения операции)</p> +</div> + +<p>Для создания фрагмента необходимо создать подкласс класса {@link android.app.Fragment} (или его существующего +подкласса). Класс {@link android.app.Fragment} имеет код, во многом схожий с +кодом {@link android.app.Activity}. Он содержит методы обратного вызова, аналогичные методам операции, такие +как {@link android.app.Fragment#onCreate onCreate()}, {@link android.app.Fragment#onStart onStart()}, +{@link android.app.Fragment#onPause onPause()} и {@link android.app.Fragment#onStop onStop()}. На практике, +если требуется преобразовать существующее приложение Android так, чтобы в нем использовались фрагменты, достаточно просто переместить +код из методов обратного вызова операции в соответствующие методы обратного вызова +фрагмента.</p> + +<p>Как правило, необходимо реализовать следующие методы жизненного цикла:</p> + +<dl> + <dt>{@link android.app.Fragment#onCreate onCreate()}</dt> + <dd>Система вызывает этот метод, когда создает фрагмент. В своей реализации разработчик должен +инициализировать ключевые компоненты фрагмента, которые требуется сохранить, когда фрагмент находится в состоянии +паузы или возобновлен после остановки.</dd> + <dt>{@link android.app.Fragment#onCreateView onCreateView()}</dt> + <dd>Система вызывает этот метод при первом отображении пользовательского интерфейса фрагмента +на дисплее. Для прорисовки пользовательского интерфейса фрагмента следует возвратить из этого метода объект {@link android.view.View}, +который является корневым в макете фрагмента. Если фрагмент не имеет пользовательского интерфейса, можно +возвратить null.</dd> + <dt>{@link android.app.Activity#onPause onPause()}</dt> + <dd>Система вызывает этот метод как первое указание того, что пользователь покидает +фрагмент (это не всегда означает уничтожение фрагмента). Обычно именно в этот момент +необходимо фиксировать все изменения, которые должны быть сохранены за рамками текущего сеанса работы пользователя (поскольку +пользователь может не вернуться назад).</dd> +</dl> + +<p>В большинстве приложений для каждого фрагмента должны быть реализованы, как минимум, эти три метода. Однако существуют и +другие методы обратного вызова, которые следует использовать для управления различными этапами жизненного цикла +фрагмента. Все методы обратного вызова жизненного цикла подробно обсуждаются в разделе + <a href="#Lifecycle">Управление жизненным циклом фрагмента</a>.</p> + + +<p>Существует также ряд подклассов, которые, возможно, потребуется расширить вместо использования базового класса {@link +android.app.Fragment}:</p> + +<dl> + <dt>{@link android.app.DialogFragment}</dt> + <dd>Отображение перемещаемого диалогового окна. Использование этого класса для создания диалогового окна является хорошей альтернативой + вспомогательным методам диалогового окна в классе{@link android.app.Activity}. Дело в том, что он дает возможность +вставить диалоговое окно фрагмента в управляемый операцией стек переходов назад для фрагментов, +что позволяет пользователю вернуться к закрытому фрагменту.</dd> + + <dt>{@link android.app.ListFragment}</dt> + <dd>Отображение списка элементов, управляемых адаптером (например, {@link +android.widget.SimpleCursorAdapter}), аналогично классу {@link android.app.ListActivity}. Этот класс предоставляет +несколько методов для управления списком представлений, например, метод обратного вызова {@link +android.app.ListFragment#onListItemClick(ListView,View,int,long) onListItemClick()} для +обработки нажатий.</dd> + + <dt>{@link android.preference.PreferenceFragment}</dt> + <dd>Отображение иерархии объектов {@link android.preference.Preference} в виде списка, аналогично классу +{@link android.preference.PreferenceActivity}. Этот класс полезен, когда в приложении создается +операция «Настройки».</dd> +</dl> + + +<h3 id="UI">Добавление пользовательского интерфейса</h3> + +<p>Фрагмент обычно используется как часть пользовательского интерфейса операции, при этом он добавляет в операцию +свой макет.</p> + +<p>Чтобы создать макет для фрагмента, разработчик должен реализовать метод обратного вызова {@link +android.app.Fragment#onCreateView onCreateView()}, который система Android вызывает, +когда для фрагмента наступает время отобразить свой макет. Реализация этого метода должна возвращать объект +{@link android.view.View}, который является корневым в макете фрагмента. </p> + +<p class="note"><strong>Примечание.</strong> Если фрагмент является подклассом класса {@link +android.app.ListFragment}, реализация по умолчанию возвращает класс {@link android.widget.ListView} из метода + {@link android.app.Fragment#onCreateView onCreateView()}, так что реализовывать его нет необходиомости.</p> + +<p>Чтобы возвратить макет из метода {@link +android.app.Fragment#onCreateView onCreateView()}, можно выполнить его раздувание из <a href="{@docRoot}guide/topics/resources/layout-resource.html">ресурса макета</a>, определенного в XML-файле. Для +этой цели метод {@link android.app.Fragment#onCreateView onCreateView()} предоставляет объект +{@link android.view.LayoutInflater}.</p> + +<p>Например, код подкласса класса {@link android.app.Fragment}, загружающий макет из файла +{@code example_fragment.xml}, может выглядеть так:</p> + +<pre> +public static class ExampleFragment extends Fragment { + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.example_fragment, container, false); + } +} +</pre> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h3>Создание макета</h3> + <p>В приведенном коде конструкция {@code R.layout.example_fragment} является ссылкой на ресурс макета +по имени {@code example_fragment.xml}, хранящийся в ресурсах приложения. Подробные сведения о +создании макета в XML см. в статье <a href="{@docRoot}guide/topics/ui/index.html">Пользовательский интерфейс</a>. +</p> +</div> +</div> + +<p>Параметр {@code container}, передаваемый методу {@link android.app.Fragment#onCreateView +onCreateView()}, является родительским классом {@link android.view.ViewGroup} (из макета операции), в который +будет вставлен макет +фрагмента. Параметр {@code savedInstanceState} является классом {@link android.os.Bundle}, который +предоставляет данные о предыдущем экземпляре фрагмента во время возобновления фрагмента +(восстановление состояния подробно обсуждается в разделе <a href="#Lifecycle">Управление +жизненным циклом фрагмента</a>).</p> + +<p>Метод {@link android.view.LayoutInflater#inflate(int,ViewGroup,boolean) inflate()} принимает +три аргумента:</p> +<ul> + <li>Идентификатор ресурса макета, раздувание которого следует выполнить.</li> + <li>Объект класса {@link android.view.ViewGroup}, который должен стать родительским для макета после раздувания. Передача параметра {@code +container} необходима для того, чтобы система смогла применить параметры макета к корневому представлению +раздутого макета, определяемому родительским представлением, в которое направляется макет.</li> + <li>Логическое значение, показывающее, следует ли прикрепить макет к объекту {@link +android.view.ViewGroup} (второй параметр) во время раздувания. (В данном случае это +false, потому что система уже вставляет раздутый макет в объект {@code +container}, ипередача значения true создала бы лишнюю группу представления в окончательном макете).</li> +</ul> + +<p>Мы увидели, как создавать фрагмент, предоставляющий макет. Теперь необходимо добавить +фрагмент в операцию.</p> + + + +<h3 id="Adding">Добавление фрагмента в операцию</h3> + +<p>Как правило, фрагмент добавляет часть пользовательского интерфейса в операцию, и этот интерфейс встраивается +в общую иерархию представлений операции. Разработчик может добавить фрагмент в макет операции двумя +способами:</p> + +<ul> + <li><b>объявив фрагмент в файле макета операции.</b> +<p>В этом случае можно +указать свойства макета для фрагмента, как будто он является представлением. Например, файл макета операции +с двумя фрагментами может выглядеть следующим образом:</p> +<pre> +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <fragment android:name="com.example.news.ArticleListFragment" + android:id="@+id/list" + android:layout_weight="1" + android:layout_width="0dp" + android:layout_height="match_parent" /> + <fragment android:name="com.example.news.ArticleReaderFragment" + android:id="@+id/viewer" + android:layout_weight="2" + android:layout_width="0dp" + android:layout_height="match_parent" /> +</LinearLayout> +</pre> + <p>Атрибут {@code android:name} в элементе {@code <fragment>} определяет класс {@link +android.app.Fragment}, экземпляр которого создается в макете.</p> + +<p>Когда система создает этот макет операции, она создает экземпляр каждого фрагмента, определенного в макете, +и для каждого вызывает метод {@link android.app.Fragment#onCreateView onCreateView()}, +чтобы получить макет каждого фрагмента. Система вставляет объект {@link android.view.View}, возвращенный + фрагментом, непосредственно вместо элемента {@code <fragment>}.</p> + +<div class="note"> + <p><strong>Примечание.</strong> Каждый фрагмент должен иметь уникальный идентификатор, который +система сможет использовать для восстановления фрагмента в случае перезапуска операции. (Что касается разработчика, он может использовать этот идентификатор для +захвата фрагмента с целью выполнения транзакций с ним, например, чтобы удалить его). Предоставить +идентификатор фрагменту можно тремя способами:</p> + <ul> + <li>указать атрибут {@code android:id} с уникальным идентификатором;</li> + <li>указать атрибут {@code android:tag} с уникальной строкой;</li> + <li>ничего не предпринимать, чтобы система использовала идентификатор контейнерного +представления.</li> + </ul> +</div> + </li> + + <li><b>или программным образом, добавив фрагмент в существующий объект {@link android.view.ViewGroup}.</b> +<p>В любой момент выполнения операции разработчик может добавить фрагменты в ее макет. Для +этого достаточно указать объект {@link +android.view.ViewGroup}, в котором следует разместить фрагмент.</p> + <p>Для выполнения транзакций с фрагментами внутри операции (таких как добавление, удаление или замена +фрагмента) необходимо использовать API-интерфейсы из {@link android.app.FragmentTransaction}. Экземпляр + класса {@link android.app.FragmentTransaction} можно получить от объекта {@link android.app.Activity} следующим образом:</p> + +<pre> +FragmentManager fragmentManager = {@link android.app.Activity#getFragmentManager()} +FragmentTransaction fragmentTransaction = fragmentManager.{@link android.app.FragmentManager#beginTransaction()}; +</pre> + +<p>После этого можно добавить фрагмент методом {@link +android.app.FragmentTransaction#add(int,Fragment) add()}, указав добавляемый фрагмент и +представление, в которое он должен быть добавлен. Например:</p> + +<pre> +ExampleFragment fragment = new ExampleFragment(); +fragmentTransaction.add(R.id.fragment_container, fragment); +fragmentTransaction.commit(); +</pre> + + <p>Первый аргумент, передаваемый методу {@link android.app.FragmentTransaction#add(int,Fragment) add()}, +представляет собой контейнерный объект {@link android.view.ViewGroup} для фрагмента, указанный при помощи +идентификатора ресурса. Второй параметр — это фрагмент, который нужно добавить.</p> + <p>Выполнив изменения с помощью +{@link android.app.FragmentTransaction}, необходимо +вызвать метод {@link android.app.FragmentTransaction#commit}, чтобы они вступили в силу.</p> + </li> +</ul> + + +<h4 id="AddingWithoutUI">Добавление фрагмента, не имеющего пользовательского интерфейса</h4> + +<p>Пример, приведенный выше, демонстрирует, как добавлять в операцию фрагмент с предоставлением пользовательского интерфейса. Однако +можно использовать фрагмент и для реализации фонового поведения операции без какого-либо дополнительного +пользовательского интерфейса.</p> + +<p>Чтобы добавить фрагмент без пользовательского интерфейса, добавьте фрагмент из операции, используя метод {@link +android.app.FragmentTransaction#add(Fragment,String)} (передав ему уникальный строковый «тег» для +фрагмента вместо идентификатора представления). Фрагмент будет добавлен, но, поскольку он не связан +с представлением в макете операции, он не будет принимать вызов метода {@link +android.app.Fragment#onCreateView onCreateView()}. Поэтому в реализации этого метода нет необходимости.</p> + +<p>Передача строкового тега свойственна не только фрагментам без пользовательского интерфейса, поэтому можно +передавать строковые теги и фрагментам, имеющим пользовательский интерфейс. Однако, если у фрагмента нет + пользовательского интерфейса, то строковый тег является единственным способом его идентификации. Если впоследствии потребуется получить фрагмент от +операции, нужно будет вызвать метод {@link android.app.FragmentManager#findFragmentByTag +findFragmentByTag()}.</p> + +<p>Пример операции, использующей фрагмент в качестве фонового потока, без пользовательского интерфейса, приведен в образце кода {@code +FragmentRetainInstance.java}, входящем в число образцов в SDK (и доступном при помощи +Android SDK Manager). Путь к нему в системе — +<code><sdk_root>/APIDemos/app/src/main/java/com/example/android/apis/app/FragmentRetainInstance.java</code>.</p> + + + +<h2 id="Managing">Управление фрагментами</h2> + +<p>Для управления фрагментами в операции нужен класс {@link android.app.FragmentManager}. Чтобы получить его, + следует вызвать метод {@link android.app.Activity#getFragmentManager()} из кода операции.</p> + +<p>Ниже указаны действия, которые позволяет выполнить{@link android.app.FragmentManager}:</p> + +<ul> + <li>получать фрагменты, имеющиеся в операции, с помощью метода {@link +android.app.FragmentManager#findFragmentById findFragmentById()} (для фрагментов, предоставляющих пользовательский интерфейс в + макете операции) или {@link android.app.FragmentManager#findFragmentByTag +findFragmentByTag()} (как для фрагментов, имеющих пользовательский интерфейс, так и для фрагментов без него);</li> + <li>снимать фрагменты со стека переходов назад методом {@link +android.app.FragmentManager#popBackStack()} (имитируя нажатие кнопки <em>Назад</em> пользователем);</li> + <li>регистрировать процесс-слушатель изменений в стеке переходов назад при помощи метода {@link +android.app.FragmentManager#addOnBackStackChangedListener addOnBackStackChangedListener()}.</li> +</ul> + +<p>Дополнительные сведения об этих и других методах приводятся в документации по классу {@link +android.app.FragmentManager}.</p> + +<p>Как было показано в предыдущем разделе, можно использовать класс {@link android.app.FragmentManager} +для открытия {@link android.app.FragmentTransaction}, что позволяет выполнять транзакции с фрагментами, например, +добавление и удаление.</p> + + +<h2 id="Transactions">Выполнение транзакций с фрагментами</h2> + +<p>Большим достоинством использования фрагментов в операции является возможность добавлять, удалять, заменять их и +выполнять другие действия с ними в ответ на действия пользователя. Любой набор изменений, +вносимых в операцию, называется транзакцией. Ее можно выполнить при помощи API-интерфейсов в {@link +android.app.FragmentTransaction}. Каждую транзакцию можно сохранить в стеке переходов назад, +которым управляет операция. Это позволит пользователю перемещаться назад по изменениям во фрагментах (аналогично перемещению +назад по операциям).</p> + +<p>Экземпляр класса {@link android.app.FragmentTransaction} можно получить от {@link +android.app.FragmentManager}, например, так:</p> + +<pre> +FragmentManager fragmentManager = {@link android.app.Activity#getFragmentManager()}; +FragmentTransaction fragmentTransaction = fragmentManager.{@link android.app.FragmentManager#beginTransaction()}; +</pre> + +<p>Каждая транзакция является набором изменений, выполняемых одновременно. Разработчик может указать +все изменения, которые ему нужно выполнить в данной транзакции, вызывая методы {@link +android.app.FragmentTransaction#add add()}, {@link android.app.FragmentTransaction#remove remove()} +и {@link android.app.FragmentTransaction#replace replace()}. Затем, чтобы применить транзакцию +к операции, следует вызвать метод {@link android.app.FragmentTransaction#commit()}.</p> +</dl> + +<p>Впрочем, до вызова метода{@link +android.app.FragmentTransaction#commit()} у разработчика может возникнуть необходимость вызвать метод {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()}, чтобы добавить транзакцию +в стек переходов назад по транзакциям фрагмента. Этим стеком переходов назад управляет операция, что позволяет +пользователю вернуться к предыдущему состоянию фрагмента, нажав кнопку <em>Назад</em>.</p> + +<p>Например, следующий код демонстрирует, как можно заменить один фрагмент другим, сохранив при этом предыдущее +состояние в стеке переходов назад:</p> + +<pre> +// Create new fragment and transaction +Fragment newFragment = new ExampleFragment(); +FragmentTransaction transaction = getFragmentManager().beginTransaction(); + +// Replace whatever is in the fragment_container view with this fragment, +// and add the transaction to the back stack +transaction.replace(R.id.fragment_container, newFragment); +transaction.addToBackStack(null); + +// Commit the transaction +transaction.commit(); +</pre> + +<p>В этом коде объект {@code newFragment} замещает фрагмент (если таковой имеется), находящийся в +контейнере макета, на который указывает идентификатор {@code R.id.fragment_container}. В результате вызова метода {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()} транзакция замены +сохраняется в стеке переходов назад, чтобы пользователь мог обратить транзакцию и вернуть +предыдущий фрагмент, нажав кнопку <em>Назад</em>.</p> + +<p>Если в транзакцию добавить несколько изменений (например, еще раз вызвать {@link +android.app.FragmentTransaction#add add()} или {@link android.app.FragmentTransaction#remove +remove()}), а затем вызвать {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()}, все изменения, примененные до вызова +метода {@link android.app.FragmentTransaction#commit commit()}, будут добавлены в стек + переходов назад как одна транзакция, и кнопка<em>Назад</em> обратит их все вместе.</p> + +<p>Порядок добавления изменений к объекту {@link android.app.FragmentTransaction} не играет роли + за следующими исключениями:</p> +<ul> + <li>метод {@link android.app.FragmentTransaction#commit()} должен быть вызван в последнюю очередь;</li> + <li>если в один контейнер добавляется несколько фрагментов, то порядок их +добавления определяет порядок, в котором они появляются в иерархии видов.</li> +</ul> + +<p>Если при выполнении транзакции, удаляющей фрагмент, не вызвать метод {@link android.app.FragmentTransaction#addToBackStack(String) +addToBackStack()}, при фиксации транзакции фрагмент +уничтожается, и пользователь теряет возможность вернуться к нему. В то же время, если +вызвать {@link android.app.FragmentTransaction#addToBackStack(String) addToBackStack()} при +удалении фрагмента, фрагмент перейдет в состояние <em>остановки</em> и будет возобновлен, если пользователь вернется +к нему.</p> + +<p class="note"><strong>Совет.</strong> К каждой транзакции с фрагментом можно применить анимацию +перехода, вызвав {@link android.app.FragmentTransaction#setTransition setTransition()} до +фиксации.</p> + +<p>Вызов метода {@link android.app.FragmentTransaction#commit()} не приводит к немедленному выполнению + транзакции. Метод запланирует ее выполнение в потоке пользовательского интерфейса операции (в «главном» потоке), как только +у потока появится возможность для этого. Впрочем, при необходимости можно вызвать {@link +android.app.FragmentManager#executePendingTransactions()} из потока пользовательского интерфейса, чтобы +транзакции, запланированные методом {@link android.app.FragmentTransaction#commit()} были выполнены немедленно. Как правило, +в этом нет необходимости, за исключением случаев, когда транзакция является зависимостью для заданий в других потоках.</p> + +<p class="caution"><strong>Внимание!</strong> Фиксировать транзакцию методом {@link +android.app.FragmentTransaction#commit commit()} можно только до того, как операция<a href="{@docRoot}guide/components/activities.html#SavingActivityState">сохранит свое +состояние</a> (после того, как пользователь покинет ее). Попытка зафиксировать транзакцию после этого момента + вызовет исключение. Дело в том, что состояние после фиксации может быть потеряно, если понадобится +восстановить операцию. В ситуациях, в которых потеря фиксации не критична, следует вызывать {@link +android.app.FragmentTransaction#commitAllowingStateLoss()}.</p> + + + + +<h2 id="CommunicatingWithActivity">Взаимодействие с операцией</h2> + +<p>Хотя {@link android.app.Fragment} реализован как объект, независимый от +класса {@link android.app.Activity}, и может быть использован внутри нескольких операций, конкретный экземпляр +фрагмента напрямую связан с содержащей его операцией.</p> + +<p>В частности, фрагмент может обратиться к экземпляру {@link android.app.Activity} с помощью метода {@link +android.app.Fragment#getActivity()} и без труда выполнить такие задачи, как поиск представления в макете +операции:</p> + +<pre> +View listView = {@link android.app.Fragment#getActivity()}.{@link android.app.Activity#findViewById findViewById}(R.id.list); +</pre> + +<p>Аналогичным образом операция может вызывать методы фрагмента, получив ссылку на объект +{@link android.app.Fragment} от {@link android.app.FragmentManager} с помощью метода {@link +android.app.FragmentManager#findFragmentById findFragmentById()} или {@link +android.app.FragmentManager#findFragmentByTag findFragmentByTag()}. Например:</p> + +<pre> +ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment); +</pre> + + +<h3 id="EventCallbacks">Создание обратного вызова события для операции</h3> + +<p>В некоторых случаях необходимо, чтобы фрагмент использовал события совместно с операцией. Хороший способ реализации этого +состоит в том, чтобы определить интерфейс обратного вызова внутри фрагмента и потребовать от контейнерной операции +его реализации. Когда операция примет обратный вызов через этот интерфейс, она сможет обмениваться информацией с +другими фрагментами в макете по мере необходимости.</p> + +<p>Пусть, например, у новостного приложения имеются два фрагмента в одной операции: один для отображения списка +статей (фрагмент A), а другой—для отображения статьи (фрагмент B). Тогда фрагмент A должен сообщать +операции о том, что выбран пункт списка, чтобы она могла сообщить фрагменту B о необходимости отобразить статью. В +этом случае интерфейс {@code OnArticleSelectedListener} объявляется во фрагменте A:</p> + +<pre> +public static class FragmentA extends ListFragment { + ... + // Container Activity must implement this interface + public interface OnArticleSelectedListener { + public void onArticleSelected(Uri articleUri); + } + ... +} +</pre> + +<p>Тогда операция, содержащая этот фрагмент, реализует интерфейс {@code OnArticleSelectedListener} +и переопределит +метод {@code onArticleSelected()}, чтобы извещать фрагмент B о событии, исходящем от фрагмента A. Чтобы +контейнерная операция наверняка реализовала этот интерфейс, метод обратного вызова {@link +android.app.Fragment#onAttach onAttach()} во фрагменте A (который система вызывает при добавлении +фрагмента в операцию) создает экземпляр класса {@code OnArticleSelectedListener}, выполнив +приведение типа объекта {@link android.app.Activity}, который передается методу {@link android.app.Fragment#onAttach +onAttach()}:</p> + +<pre> +public static class FragmentA extends ListFragment { + OnArticleSelectedListener mListener; + ... + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + try { + mListener = (OnArticleSelectedListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener"); + } + } + ... +} +</pre> + +<p>Если операция не реализовала интерфейс, фрагмент генерирует исключение +{@link java.lang.ClassCastException}. +В случае успеха элемент {@code mListener} будет содержать ссылку на реализацию интерфейса +{@code OnArticleSelectedListener} в операции, чтобы фрагмент A мог использовать +события совместно с операцией, вызывая методы, определенные интерфейсом {@code OnArticleSelectedListener}. Например, если фрагмент A является расширением +класса {@link android.app.ListFragment}, то всякий раз, +когда пользователь нажимает элемент списка, система вызывает {@link android.app.ListFragment#onListItemClick +onListItemClick()} во фрагменте. Этот метод, в свою очередь, вызывает метод {@code onArticleSelected()}, чтобы использовать +событие совместно с операцией:</p> + +<pre> +public static class FragmentA extends ListFragment { + OnArticleSelectedListener mListener; + ... + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + // Append the clicked item's row ID with the content provider Uri + Uri noteUri = ContentUris.{@link android.content.ContentUris#withAppendedId withAppendedId}(ArticleColumns.CONTENT_URI, id); + // Send the event and Uri to the host activity + mListener.onArticleSelected(noteUri); + } + ... +} +</pre> + +<p>Параметр {@code id}, передаваемый методу {@link +android.app.ListFragment#onListItemClick onListItemClick()}, — это идентификатор строки с выбранным элементом списка, +который операция (или другой фрагмент) использует для получения статьи от объекта {@link +android.content.ContentProvider} приложения.</p> + +<p><!--To see a complete implementation of this kind of callback interface, see the <a +href="{@docRoot}resources/samples/NotePad/index.html">NotePad sample</a>. -->Дополнительные сведения о +работе с поставщиком контента приводятся в документе <a href="{@docRoot}guide/topics/providers/content-providers.html">Поставщики контента</a>.</p> + + + +<h3 id="ActionBar">Добавление элементов в строку действий</h3> + +<p>Фрагменты могут добавлять пункты меню в <a href="{@docRoot}guide/topics/ui/menus.html#options-menu">Меню вариантов</a> операции (и, следовательно, в <a href="{@docRoot}guide/topics/ui/actionbar.html">Строку действий</a>), реализовав +{@link android.app.Fragment#onCreateOptionsMenu(Menu,MenuInflater) onCreateOptionsMenu()}. Однако, чтобы этот метод мог +принимать вызовы, необходимо вызывать {@link +android.app.Fragment#setHasOptionsMenu(boolean) setHasOptionsMenu()} во время выполнения метода {@link +android.app.Fragment#onCreate(Bundle) onCreate()}, чтобы сообщить, что фрагмент +намеревается добавить пункты в Меню вариантов (в противном случае фрагмент не примет вызов метода +{@link android.app.Fragment#onCreateOptionsMenu onCreateOptionsMenu()}).</p> + +<p>Любые пункты, добавляемые фрагментом в Меню вариантов, присоединяются к +уже существующим. Кроме того, фрагмент принимает обратные вызовы метода {@link +android.app.Fragment#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}, когда пользователь выбирает пункт +меню.</p> + +<p>Разработчик может также зарегистрировать представление в макете своего фрагмента, чтобы предоставить контекстное меню. Для этого следует вызвать метод {@link +android.app.Fragment#registerForContextMenu(View) registerForContextMenu()}. Когда пользователь открывает +контекстное меню, фрагмент принимает вызов метода {@link +android.app.Fragment#onCreateContextMenu(ContextMenu,View,ContextMenu.ContextMenuInfo) +onCreateContextMenu()}. Когда пользователь выбирает пункт меню, фрагмент принимает вызов метода {@link +android.app.Fragment#onContextItemSelected(MenuItem) onContextItemSelected()}.</p> + +<p class="note"><strong>Примечание.</strong> Хотя фрагмент принимает обратный вызов по событию «выбран пункт меню» +для каждого добавленного им пункта, операция первой принимает соответствующий обратный вызов, когда пользователь +выбирает пункт меню. Если имеющаяся в операции реализация обратного вызова по событию «выбран пункт меню» не +обрабатывает выбранный пункт, событие передается методу обратного вызова во фрагменте. Это справедливо для +Меню вариантов и контекстных меню.</p> + +<p>Подробные сведения относительно меню см. в руководствах для разработчиков <a href="{@docRoot}guide/topics/ui/menus.html">Меню</a> и <a href="{@docRoot}guide/topics/ui/actionbar.html">Строка действий</a> </p> + + + + +<h2 id="Lifecycle">Управление жизненным циклом фрагмента</h2> + +<div class="figure" style="width:350px"> +<img src="{@docRoot}images/activity_fragment_lifecycle.png" alt="" /> +<p class="img-caption"><strong>Рисунок 3</strong>. Влияние жизненного цикла операции на жизненный цикл +фрагмента</p> +</div> + +<p>Управление жизненным циклом фрагмента во многом аналогично управлению жизненным циклом операции. Как и +операция, фрагмент может существовать в одном из трех состояний:</p> + +<dl> + <dt><i>Возобновлен</i></dt> + <dd>Фрагмент виден во время выполнения операции.</dd> + + <dt><i>Приостановлен</i></dt> + <dd>На переднем плане выполняется и находится в фокусе другая операция, но операция, содержащая данный +фрагмент, по-прежнему видна (операция переднего плана частично прозрачна или не +занимает весь экран).</dd> + + <dt><i>Остановлен</i></dt> + <dd>Фрагмент не виден. Либо контейнерная операция остановлена, либо +фрагмент удален из нее, но добавлен в стек переходов назад. Остановленный фрагмент +по-прежнему активен (вся информация о состоянии и элементах сохранена в системе). Однако он больше +не виден пользователю и будет уничтожен в случае уничтожения операции.</dd> +</dl> + +<p>Здесь снова просматривается аналогия с операцией: разработчик может сохранить состояние фрагмента с помощью {@link +android.os.Bundle} на случай, если процесс операции будет уничтожен, а разработчику понадобится восстановить +состояние фрагмента при повторном создании операции. Состояние можно сохранить во время выполнения метода обратного вызова {@link +android.app.Fragment#onSaveInstanceState onSaveInstanceState()} во фрагменте и восстановить его во время выполнения +{@link android.app.Fragment#onCreate onCreate()}, {@link +android.app.Fragment#onCreateView onCreateView()} или {@link +android.app.Fragment#onActivityCreated onActivityCreated()}. Дополнительные сведения о сохранении +состояния приводятся в документе <a href="{@docRoot}guide/components/activities.html#SavingActivityState">Операции</a>. +</p> + +<p>Самое значительное различие в ходе жизненного цикла между операцией и фрагментом состоит в принципах +их сохранения в соответствующих стеках переходов назад. По умолчанию операция помещается в управляемый системой стек переходов назад для операций, +когда она останавливается (чтобы пользователь мог вернуться +к ней с помощью кнопки <em>Назад</em>, как описано в статье <a href="{@docRoot}guide/components/tasks-and-back-stack.html">Задачи и стек переходов назад</a>). +В то же время, фрагмент помещается в стек переходов назад, управляемый операцией, только когда разработчик +явно запросит сохранение конкретного экземпляра, вызвав метод {@link +android.app.FragmentTransaction#addToBackStack(String) addToBackStack()} во время транзакции, +удаляющей фрагмент.</p> + +<p>В остальном управление жизненным циклом фрагмента очень похоже на управление жизненным циклом +операции. Поэтому практические рекомендации по <a href="{@docRoot}guide/components/activities.html#Lifecycle">управлению жизненным циклом +операций</a> применимы и к фрагментам. При этом разработчику необходимо понимать, как жизненный цикл +операции влияет на жизненный цикл фрагмента.</p> + +<p class="caution"><strong>Внимание!</strong> Если возникнет необходимость в объекте {@link android.content.Context} +внутри объекта класса {@link android.app.Fragment}, можно вызвать метод{@link android.app.Fragment#getActivity()}. +Однако разработчик должен быть внимательным и вызывать метод {@link android.app.Fragment#getActivity()} только когда фрагмент +прикреплен к операции. Если фрагмент еще не прикреплен или был откреплен в конце +его жизненного цикла, метод {@link android.app.Fragment#getActivity()} возвратит null.</p> + + +<h3 id="CoordinatingWithActivity">Согласование с жизненным циклом операции</h3> + +<p>Жизненый цикл операции, содержащей фрагмент, непосредственным образом влияет на жизненый цикл +фрагмента, так что каждый обратный вызов жизненного цикла операции приводит к аналогичному обратного вызову для каждого +фрагмента. Например, когда операция принимает вызов {@link android.app.Activity#onPause}, каждый +ее фрагмент принимает {@link android.app.Fragment#onPause}.</p> + +<p>Однако у фрагментов есть несколько дополнительных методов обратного вызова жизненого цикла, которые обеспечивают уникальное взаимодействие с операцией +для выполнения таких действий, как создание и уничтожение пользовательского интерфейса фрагмента. Вот эти +методы:</p> + +<dl> + <dt>{@link android.app.Fragment#onAttach onAttach()}</dt> + <dd>Вызывается, когда фрагмент связывается с операцией (ему передается объект {@link +android.app.Activity}).</dd> + <dt>{@link android.app.Fragment#onCreateView onCreateView()}</dt> + <dd>Вызывается для создания иерархии представлений, связанной с фрагментом.</dd> + <dt>{@link android.app.Fragment#onActivityCreated onActivityCreated()}</dt> + <dd>Вызывается, когда метод {@link android.app.Activity#onCreate +onCreate()}, принадлежащий операции, возвращает управление.</dd> + <dt>{@link android.app.Fragment#onDestroyView onDestroyView()}</dt> + <dd>Вызывается при удалении иерархии представлений, связанной с фрагментом.</dd> + <dt>{@link android.app.Fragment#onDetach onDetach()}</dt> + <dd>Вызывается при разрыве связи фрагмента с операцией.</dd> +</dl> + +<p>Зависимость жизненого цикла фрагмента от содержащей его операции иллюстрируется +рисунком 3. На этом рисунке можно видеть, что очередное состояние операции определяет, какие +методы обратного вызова может принимать фрагмент. Например, когда операция принимает свой метод обратного вызова {@link +android.app.Activity#onCreate onCreate()}, фрагмент внутри этой операции принимает всего лишь метод обратного вызова +{@link android.app.Fragment#onActivityCreated onActivityCreated()}.</p> + +<p>Когда операция переходит в состояние «возобновлена», можно свободно добавлять в нее фрагменты и удалять +их. Таким образом, жизненный цикл фрагмента может быть независимо изменен, только пока операция остается +в состоянии «возобновлена».</p> + +<p>Однако, когда операция выходит из этого состояния, продвижение фрагмента по его +жизненному циклу снова осуществляется операцией.</p> + + + + +<h2 id="Example">Пример:</h2> + +<p>Чтобы суммировать все сказанное в этом документе, рассмотрим пример операции, +использующей два фрагмента для создания макета с двумя панелями. Операция, код которой приведен ниже, включает в себя один фрагмент для +отображения списка пьес Шекспира, а другой — для отображения краткого содержания пьесы, выбранной +из списка. В примере показано, как следует организовывать различные конфигурации фрагментов +в зависимости от конфигурации экрана. </p> + +<p class="note"><strong>Примечание.</strong> Полный исходный код этой операции находится в разделе +<a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.html">{@code +FragmentLayout.java}</a>.</p> + +<p>Главная операция применяет макет обычным способом, в методе {@link +android.app.Activity#onCreate onCreate()}:</p> + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java main} + +<p>Здесь применяется макет {@code fragment_layout.xml}:</p> + +{@sample development/samples/ApiDemos/res/layout-land/fragment_layout.xml layout} + +<p>Пользуясь этим макетом, система создает экземпляр класса {@code TitlesFragment} (список +пьес), как только операция загрузит макет. При этом объект {@link android.widget.FrameLayout} +(в котором будет находиться фрагмент с кратким содержанием) занимает место в правой части +экрана, но поначалу остается пустым. Как будет показано ниже, фрагмент не помещается в {@link android.widget.FrameLayout}, пока пользователь не выберет элемент +в списке.</p> + +<p>Однако не все экраны достаточно широки, чтобы отображать +краткое содержание рядом со списком пьес. Поэтому описанный выше макет используется только при альбомной ориентации +экрана и хранится в файле {@code res/layout-land/fragment_layout.xml}.</p> + +<p>Когда же устройство находится в книжной ориентации, система применяет макет, приведенный ниже, который +хранится в файле{@code res/layout/fragment_layout.xml}:</p> + +{@sample development/samples/ApiDemos/res/layout/fragment_layout.xml layout} + +<p>В этом макете присутствует только объект {@code TitlesFragment}. Это означает, что при +книжной ориентации устройства виден только список пьес. Когда пользователь нажимает на элемент +списка в этой конфигурации, приложение запускает новую операцию для отображения краткого содержания, +а не загружает второй фрагмент.</p> + +<p>Далее можно видеть, как это реализовано в классах фрагмента. Вначале идет код класса {@code +TitlesFragment}, отображающий список пьес Шекспира. Этот фрагмент является расширением класса {@link +android.app.ListFragment} и использует его функции для выполнения основной работы со списком.</p> + +<p>Изучая код, обратите внимание на то, что в качестве реакции на нажатие пользователем +элемента списка возможны две модели поведения. В зависимости от того, какой из двух макетов активен, либо в рамках одной операции создается и отображается новый фрагмент +с кратким содержанием (за счет добавления фрагмента в объект {@link +android.widget.FrameLayout}), либо запускается новая операция (отображающая фрагмент).</p> + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java titles} + +<p>Второй фрагмент, {@code DetailsFragment}, отображает краткое содержание пьесы, выбранной в +списке {@code TitlesFragment}:</p> + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java details} + +<p>Вспомним код класса {@code TitlesFragment}: если пользователь нажимает на пункт списка, а +текущий макет <em>не</em> включает в себя представление {@code R.id.details} (которому принадлежит фрагмент +{@code DetailsFragment}), то приложение запускает операцию {@code DetailsActivity} +для отображения содержимого элемента.</p> + +<p>Далее идет код класса {@code DetailsActivity}, который всего лишь содержит объект {@code DetailsFragment} для отображения +краткого содержания выбранной пьесы на экране в книжной ориентации:</p> + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java +details_activity} + +<p>Обратите внимание, что в альбомной конфигурации эта операция самостоятельно завершается, чтобы главная +операция могла принять управление и отобразить фрагмент {@code DetailsFragment} рядом с фрагментом{@code TitlesFragment}. +Это может произойти, если пользователь запустит операцию {@code DetailsActivity} в книжной ориентации экрана, а +затем перевернет устройство в альбомную ориентацию (в результате чего текущая операция будет перезапущена).</p> + + +<p>Дополнительные образцы кода, использующего фрагменты (и файлы с полным исходным кодом этого примера), +доступны в приложении-примере API Demos в разделе <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/index.html#Fragment"> +ApiDemos</a> (которое можно загрузить из <a href="{@docRoot}resources/samples/get.html">компонента Samples SDK</a>).</p> + + diff --git a/docs/html-intl/intl/ru/guide/components/fundamentals.jd b/docs/html-intl/intl/ru/guide/components/fundamentals.jd new file mode 100644 index 000000000000..181cbbdfb107 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/fundamentals.jd @@ -0,0 +1,480 @@ +page.title=Основы создания приложений +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + +<h2>Содержание документа</h2> +<ol> +<li><a href="#Components">Компоненты приложения</a> + <ol> + <li><a href="#ActivatingComponents">Активация компонентов</a></li> + </ol> +</li> +<li><a href="#Manifest">Файл манифеста</a> + <ol> + <li><a href="#DeclaringComponents">Объявление компонентов</a></li> + <li><a href="#DeclaringRequirements">Объявление требований приложения</a></li> + </ol> +</li> +<li><a href="#Resources">Ресурсы приложения</a></li> +</ol> +</div> +</div> + +<p>Приложения для Android пишутся на языке программирования Java. Инструменты Android SDK (Software Development Kit – комплект разработки программного обеспечения) компилируют +написанный вами код — и все требуемые файлы данных и ресурсов — в файл APK – <i>программный пакет Android</i>, +который представляет собой файл архива с расширением {@code .apk}. В файле APK находится все, что требуется для работы +Android-приложения, и он позволяет установить приложение на любом устройстве под управлением системы Android.</p> + +<p>Каждое приложение Android, установленное на устройстве, работает в собственной "песочнице" (изолированной программной среде): </p> + +<ul> + <li>операционная система Android представляет собой многопользовательскую систему Linux, в которой каждое приложение является +отдельным пользователем;</li> + +<li>по умолчанию система назначает каждому приложению уникальный идентификатор пользователя Linux (этот идентификатор используется только +системой и неизвестен приложению); система устанавливает полномочия для всех файлов + в приложении, с тем чтобы доступ к ним был разрешен только пользователю с идентификатором, назначенным этому приложению; </li> + +<li>у каждого процесса имеется собственная виртуальная машина (ВМ), так что код приложения выполняется изолированно от +других приложений;</li> + +<li>по умолчанию каждое приложение выполняется в собственном процессе Linux. Android запускает процесс, когда требуется +выполнить какой-либо компонент приложения, а затем завершает процесс, когда он больше не +нужен либо когда системе требуется освободить память для других приложений.</li> +</ul> + +<p>Таким образом система Android реализует <em>принцип предоставления минимальных прав</em>. То есть +каждое приложение по умолчанию имеет доступ только к тем компонентам, которые ему необходимы для работы, и +ни к каким другим. Благодаря этому формируется исключительно безопасная среда, в которой приложение не имеет доступа к недозволенным областям +системы.</p> + +<p>Однако у приложения есть варианты предоставления своих данных другим приложениям и +доступа к системным службам:</p> + +<ul> + <li>двум приложениям можно назначить один идентификатор пользователя Linux. В этом случае +каждый из них сможет обращаться к файлам другого приложения. Для экономии ресурсов системы также можно +сделать так, чтобы приложения с одинаковым идентификатором пользователя выполнялись в одном процессе Linux и использовали одну ВМ ( +приложения также должны быть подписаны одним сертификатом);</li> + <li>приложение может запросить разрешение на доступ к данным устройства, например к контактам +пользователя, SMS-сообщениям, подключаемой карте памяти (SD-карте), камере, Bluetooth и др. Все +разрешения должны предоставляться приложению при его установке.</li> +</ul> + +<p>Это основные сведения о том, каким образом приложение Android существует в системе. В остальной части +этого документа раскрываются следующие темы:</p> +<ul> + <li>базовые компоненты, которые определяют приложение;</li> + <li>файл манифеста, в котором объявляются компоненты и функции устройства, необходимые для +приложения;</li> + <li>ресурсы, которые существуют отдельно от кода приложения и позволяют приложению +адаптировать свою работу к устройствам с различными конфигурациями.</li> +</ul> + + + +<h2 id="Components">Компоненты приложения</h2> + +<p>Компоненты приложения являются кирпичиками, из которых состоит приложение для Android. Каждый +компонент представляет собой отдельную точку, через которую система может войти в приложение. Не все +компоненты являются точками входа для пользователя, а некоторые из них зависят друг от друга. При этом каждый компонент является +самостоятельной структурной единицей и играет определенную роль — каждый из них представляет собой уникальный элемент структуры, который +определяет работу приложения в целом.</p> + +<p>Компоненты приложения можно отнести к одному из четырех типов. Компоненты каждого типа предназначены для определенной цели, +они имеют собственный жизненный цикл, который определяет способ создания и прекращения существования компонента.</p> + +<p>Четыре типа компонентов:</p> + +<dl> + +<dt><b>Операции</b></dt> + +<dd>Операция (Activity) <i>представляет</i> собой один экран с пользовательским интерфейсом. Например, +в приложении для работы с электронной почтой одна операция может служить для отображения списка новых +сообщений, другая – для составления сообщения и третья операция – для чтения сообщений. Несмотря на то что +операции совместно формируют связное взаимодействие пользователя с приложением по работе с электронной почтой, каждая из них +не зависит от других операций. Любые из этих операций могут быть запущены +другим приложением (если это позволяет приложение по работе с электронной почтой). Например, приложение для камеры может запустить +операцию в приложении по работе с электронной почтой, которая составляет новое сообщение, чтобы пользователь мог отослать фотографию. + +<p>Операция относится к подклассу класса {@link android.app.Activity}. Подробные сведения об этом можно +найти в руководстве для разработчиков в статье <a href="{@docRoot}guide/components/activities.html">Операции</a> +.</p> +</dd> + + +<dt><b>Службы</b></dt> + +<dd>Служба (Service) <i>представляет</i> собой компонент, который работает в фоновом режиме и выполняет длительные +операции, связанные с работой удаленных процессов. Служба +не имеет пользовательского интерфейса. Например, она может воспроизводить музыку в фоновом режиме, пока +пользователь работает в другом приложении, или же она может получать данные по сети, не +блокируя взаимодействие пользователя с операцией. Служба может быть запущена другим компонентом, который затем будут взаимодействовать с ней, – например +операцией. + +<p>Служба относится к подклассу класса {@link android.app.Service}. Подробные сведения об этом можно +найти в руководстве для разработчиков в статье <a href="{@docRoot}guide/components/services.html">Службы</a> +.</p> +</dd> + + +<dt><b>Поставщики контента</b></dt> + +<dd>Поставщик <i>контента (Content provider)</i> управляет общим набором данных приложения. Данные можно хранить в +файловой системе, базе данных SQLite, в Интернете или любом другом постоянном месте хранения, к которому у вашего +приложения имеется доступ. Посредством поставщика контента другие приложения могут запрашивать или даже изменять +данные (если поставщик контента позволяет делать это). Например, в системе Android есть поставщик +контента, который управляет информацией контактов пользователя. Любое приложение, получившее соответствующие +разрешения, может запросить часть этого поставщика контента (например {@link +android.provider.ContactsContract.Data}), для чтения и записи сведений об определенном человеке. + +<p>Поставщики контента также используются для чтения и записи данных, доступ к которым внешним компонентам +приложение не предоставляет. Например, в образце приложения <a href="{@docRoot}resources/samples/NotePad/index.html">Note Pad</a> с помощью +поставщика контента выполняется сохранение заметок.</p> + +<p>Поставщик контента относится к подклассу класса {@link android.content.ContentProvider}. +Он должен реализовывать стандартный набор API-интерфейсов, с помощью которых другие приложения будут выполнять +транзакции. Подробные сведения можно найти в руководстве для разработчиков в статье <a href="{@docRoot}guide/topics/providers/content-providers.html">Поставщики контента</a> +.</p> +</dd> + + +<dt><b>Приемники широковещательных сообщений</b></dt> + +<dd>Приемник широковещательных сообщений (Broadcast receiver) <i>представляет</i> собой компонент, который реагирует на объявления +распространяемые по всей системе. Многие из этих объявлений рассылает система — например объявление о том, +что экран выключился, аккумулятор разряжен или был сделан фотоснимок. +Объявления также могут рассылаться приложениями, — например, чтобы сообщить другим приложениям о том, что +какие-то данные были загружены на устройство и теперь готовы для использования. Несмотря на то что приемники широковещательных сообщений +не имеют пользовательского интерфейса, они могут<a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">создавать уведомления в строке состояния,</a> +чтобы предупредить пользователя о событии "рассылка объявления". Однако чаще всего они являются +просто "шлюзом" для других компонентов и предназначены для выполнения минимального объема работы. Например +, они могут инициировать выполнение службой определенных действий при возникновении события. + +<p>Приемник широковещательных сообщений относится к подклассу класса {@link android.content.BroadcastReceiver} +, а каждое такое сообщение предоставляется как объект {@link android.content.Intent}. Подробные сведения изложены +в руководстве, посвященном классу {@link android.content.BroadcastReceiver}.</p> +</dd> + +</dl> + + + +<p>Уникальной особенностью системы Android является то, что любое приложение может запустить компонент +другого приложения. Например, если вы хотите дать пользователю возможность фотографировать, используя +камеру устройства, то, поскольку наверняка имеется другое приложение, которое может выполнить это действие, вместо того чтобы разработать операцию фотографирования в своем приложении, вы можете вызвать +такое приложение. Вам не +нужно внедрять код из приложения для камеры или даже устанавливать на него ссылку. +Вместо этого вы можете просто запустить операцию фотографирования + из приложения для камеры. По завершении этой операции фотография будет возвращена в ваше приложение, и ее можно будет использовать. Для пользователя + это будет выглядеть как одно приложение.</p> + +<p>Когда система запускает компонент, она запускает процесс для этого приложения (если +он еще не был запущен) и создает экземпляры классов, которые требуются этому компоненту. Например, если ваше приложение +запустит операцию фотографирования в приложении для камеры, эта операция +будет выполняться в процессе, который относится к этому стороннему приложению, а не в процессе вашего приложения. +Поэтому, в отличие от приложений для большинства других систем, в приложениях для Android отсутствует единая +точка входа (например, в них нет функции {@code main()}).</p> + +<p>Поскольку система выполняет каждое приложение в отдельном процессе с такими правами доступа к файлам, которые +ограничивают доступ в другие приложения, ваше приложение не может напрямую вызвать компонент из +другого приложения. Это может сделать сама система Android. Поэтому, чтобы вызвать компонент в +другом приложении, необходимо сообщить системе о своем намерении (<em>Intent</em>) +запустить определенный компонент. После этого система активирует для вас этот компонент.</p> + + +<h3 id="ActivatingComponents">Активация компонентов</h3> + +<p>Компоненты трех из четырех возможных типов — операции, службы и +приемники широковещательных сообщений — активируются асинхронным сообщением, которое называется <em>Intent</em> (намерение). +Объекты Intent связывают друг с другом отдельные компоненты во время выполнения, будь то это компоненты + вашего или стороннего приложения (эти объекты Intent можно представить себе +в виде мессенджеров, которые посылают другим компонентам запрос на выполнение действий).</p> + +<p>Объект Intent создается с помощью объекта {@link android.content.Intent}, который описывает запрос на +активацию либо конкретного компонента, либо компонента конкретного <em>типа</em> — соответственно, намерение Intent +может быть явным или неявным.</p> + +<p>Для операций и служб Объект Intent определяет действие, которое требуется выполнить (например, просмотреть (view) или +отправить (send) что-то), а также может указывать URI (Uniform Resource Identifier – унифицированный идентификатор ресурса) данных, с которыми это действие нужно выполнить (помимо прочих сведений, которые +нужно знать запускаемому компоненту). Например, объект Intent может передавать запрос +на выполнение операции "показать изображение" или "открыть веб-страницу". В некоторых ситуациях операцию можно +запустить, чтобы получить результат. В этом случае операция возвращает +результат также в виде объекта {@link android.content.Intent} (например, можно отправить сообщение Intent, чтобы дать +пользователю возможность выбрать контакт и вернуть его вам — в ответном сообщении Intent будет содержаться +URI, указывающий на выбранный контакт).</p> + +<p>Для приемников широковещательных сообщений Intent просто определяет +передаваемое объявление (например, широковещательное сообщение о низком уровне заряда аккумулятора +содержит только строку "аккумулятор разряжен").</p> + +<p>Компоненты четвертого типа – поставщики контента – сообщениями Intent не активируются. Они +активируются по запросу от {@link android.content.ContentResolver}. Процедура определения + контента (content resolver) обрабатывает все прямые транзакции с поставщиком контента, с тем чтобы этого не пришлось делать компоненту, который +выполняет транзакции с поставщиком. Вместо этого он вызывает методы для объекта {@link +android.content.ContentResolver}. Это формирует слой, абстрагирующий (в целях безопасности) поставщика +контента от компонента, запрашивающего информацию.</p> + +<p>Для активации компонентов каждого типа имеются отдельные методы:</p> +<ul> + <li>Можно запустить операцию (или определить для нее какое-то новое действие), +передав объект {@link android.content.Intent} методу {@link android.content.Context#startActivity +startActivity()} или {@link android.app.Activity#startActivityForResult startActivityForResult()} +(если требуется, чтобы операция вернула результат).</li> + <li>Можно запустить службу (либо выдать работающей службе новые инструкции), +передав объект {@link android.content.Intent} методу {@link android.content.Context#startService +startService()}. Либо можно установить привязку к службе, передав объект{@link android.content.Intent} методу +{@link android.content.Context#bindService bindService()}.</li> + <li>Можно инициировать рассылку сообщений, передав объект {@link android.content.Intent} таким методам, как +{@link android.content.Context#sendBroadcast(Intent) sendBroadcast()}, {@link +android.content.Context#sendOrderedBroadcast(Intent, String) sendOrderedBroadcast()} и {@link +android.content.Context#sendStickyBroadcast sendStickyBroadcast()}.</li> + <li>Можно выполнить запрос к поставщику контента, вызвав метод {@link +android.content.ContentProvider#query query()} для объекта {@link android.content.ContentResolver}.</li> +</ul> + +<p>Подробные сведения об использовании объектов Intent приведены в документе <a href="{@docRoot}guide/components/intents-filters.html">Объекты Intent и +фильтры объектов Intent</a>. Более подробная информация об активации определенных компонентов также приведена +в следующих документах: <a href="{@docRoot}guide/components/activities.html">Операции</a>, <a href="{@docRoot}guide/components/services.html">Службы</a>, {@link +android.content.BroadcastReceiver} и <a href="{@docRoot}guide/topics/providers/content-providers.html">Поставщики контента</a>.</p> + + +<h2 id="Manifest">Файл манифеста</h2> + +<p>Для запуска компонента приложения системе Android необходимо знать, что +компонент существует. Для этого она читает файл {@code AndroidManifest.xml} приложения (файл +манифеста). В этом файле, который должен находиться в корневой папке +приложения, должны быть объявлены все компоненты приложения.</p> + +<p>Помимо объявления компонентов приложения, манифест служит и для других целей, +среди которых:</p> +<ul> + <li>указание всех полномочий пользователя, которые требуются приложению, например разрешения на доступ в Интернет или +на чтение контактов пользователя;</li> + <li>объявление минимального<a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#ApiLevels">уровня API</a>, +требуемого приложению, с учетом того, какие API-интерфейсы оно использует;</li> + <li>объявление аппаратных и программных функций, которые нужны приложению или используются им, например камеры, +службы Bluetooth или сенсорного экрана;</li> + <li>указание библиотек API, с которыми необходимо связать приложение (отличные от API-интерфейсов платформы +Android), например библиотеки <a href="http://code.google.com/android/add-ons/google-apis/maps-overview.html">Google Maps +</a>;</li> + <li>и многое другое.</li> +</ul> + + +<h3 id="DeclaringComponents">Объявление компонентов</h3> + +<p>Основная задача манифеста – это информировать систему о компонентах приложения. Например, + файл манифеста может объявлять операцию следующим образом: </p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<manifest ... > + <application android:icon="@drawable/app_icon.png" ... > + <activity android:name="com.example.project.ExampleActivity" + android:label="@string/example_label" ... > + </activity> + ... + </application> +</manifest></pre> + +<p>Атрибут {@code android:icon} в элементе <code><a +href="{@docRoot}guide/topics/manifest/application-element.html"><application></a></code> +указывает на ресурсы для значка, который обозначает +приложение.</p> + +<p>Атрибут {@code android:name} в элементе <code><a +href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> +указывает полное имя класса подкласса {@link +android.app.Activity}, а атрибут {@code android:label} указывает строку, +которую необходимо использовать в качестве метки операции, отображаемой для пользователя.</p> + +<p>Все компоненты приложения необходимо объявлять следующим образом:</p> +<ul> + <li>элементы <code><a +href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> +для операций;</li> + <li>элементы <code><a +href="{@docRoot}guide/topics/manifest/service-element.html"><service></a></code> +для служб;</li> + <li>элементы <code><a +href="{@docRoot}guide/topics/manifest/receiver-element.html"><receiver></a></code> +для приемников широковещательных сообщений;</li> + <li>элементы <code><a +href="{@docRoot}guide/topics/manifest/provider-element.html"><provider></a></code> +для поставщиков контента</li> +</ul> + +<p>Системе не видны операции, службы и поставщики контента, которые имеются в исходном коде, но не объявлены +в манифесте, поэтому они не могут быть запущены. А вот +приемники широковещательных сообщений +можно либо объявить в манифесте, либо создать динамически в коде (как объекты +{@link android.content.BroadcastReceiver}) и зарегистрировать в системе путем вызова +{@link android.content.Context#registerReceiver registerReceiver()}.</p> + +<p>Подробные сведения о структуризации файла манифеста для приложения см. в документе <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">Файл AndroidManifest.xml</a> +. </p> + + + +<h3 id="DeclaringComponentCapabilities">Объявление возможностей компонентов</h3> + +<p>Как уже говорилось в разделе <a href="#ActivatingComponents">Активация компонентов</a>, с помощью объекта +{@link android.content.Intent} можно запускать операции, службы и приемники широковещательных сообщений. Для этого в объекте Intent следует +явно указать имя целевого компонента (с помощью имени класса компонента). Однако +в полной мере возможности объектов Intent раскрываются при использовании концепции <em>неявных Intent</em>. В неявном сообщении Intent +просто описывается тип действия, которое требуется выполнить (а также, хотя это и не обязательно, дата, в которую вы бы хотели +выполнить это действие). Системе же предоставляется возможности найти на устройстве компонент, который может выполнить это +действие, и запустить его. При наличии нескольких компонентов, которые могут выполнить действие, описанное в сообщении +Intent, пользователь выбирает, какой из них будет использоваться.</p> + +<p>Система определяет компоненты, которые могут ответить на сообщение Intent, путем сравнения +полученного сообщения Intent с <i>фильтрами объектов Intent,</i> указанными в файле манифеста других приложений, имеющихся + на устройстве.</p> + +<p>При объявлении операции в манифесте своего приложения по желанию можно указать +фильтры объектов Intent, которые указывают возможности операции, с тем чтобы она могла реагировать на сообщения Intent +от других приложений. Чтобы объявить фильтр Intent для своего компонента, +необходимо добавить элемент <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a> в качестве дочернего для элемента объявления компонента.</p> + +<p>Например, если вы создали приложение для работы с электронной почтой с операцией составления нового сообщения, вы можете +объявить фильтр для ответа на сообщения Intent типа "send" (для отправки нового сообщения электронной почты) следующим образом:</p> +<pre> +<manifest ... > + ... + <application ... > + <activity android:name="com.example.project.ComposeEmailActivity"> + <intent-filter> + <action android:name="android.intent.action.SEND" /> + <data android:type="*/*" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + </application> +</manifest> +</pre> + +<p>Затем, если другое приложение создаст объект Intent с действием {@link +android.content.Intent#ACTION_SEND} и передаст его в {@link android.app.Activity#startActivity +startActivity()}, система сможет запустить вашу операцию, дав пользователю возможность написать и отправить +сообщение электронной почты.</p> + +<p>Подробные сведения о создании фильтров объектов Intent приведены в документе <a href="{@docRoot}guide/components/intents-filters.html">Объекты Intent и фильтры объектов Intent</a>. +</p> + + + +<h3 id="DeclaringRequirements">Объявление требований приложения</h3> + +<p>Существует огромное количество устройств, работающих под управлением Android, и не все они имеют +одинаковые функциональные возможности. Чтобы ваше приложение не могло быть установлено на устройствах, +в которых отсутствуют функции, необходимые приложению, важно четко определить профиль для +типов устройств, поддерживаемых вашим приложением, указав требования к аппаратному и программному обеспечению в +файле манифеста. Эти объявления по большей части носят информационный характер, система их не +читает. Однако их читают внешние службы, например Google Play, с целью обеспечения +фильтрации для пользователей, которые ищут приложения для своих устройств.</p> + +<p>Например, если вашему приложению требуется камера и оно использует API-интерфейсы из Android 2.1 (<a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#ApiLevels">уровень API</a> 7), +эти параметры следует объявить в файле манифеста в качестве требований следующим образом:</p> + +<pre> +<manifest ... > + <uses-feature android:name="android.hardware.camera.any" + android:required="true" /> + <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" /> + ... +</manifest> +</pre> + +<p>Теперь ваше приложение нельзя будет установить из Google Play на устройствах, в которых <em>нет</em> камеры, а также на устройствах, работающих под управлением +Android версии <em>ниже</em> 2.1.</p> + +<p>Однако можно также объявить, что приложение использует камеру, но для его работы она не является +<em>непременно необходимой</em>. В этом случае в приложении атрибуту <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html#required">{@code required}</a> +необходимо задать значение {@code "false"}, а во время работы оно должно проверять, имеется ли +на устройстве камера, и при необходимости отключать свои функции, которые используют камеру.</p> + +<p>Более подробные сведения об управлении совместимостью своего приложения с различными устройствами +приведены в документе <a href="{@docRoot}guide/practices/compatibility.html">Совместимость устройств</a> +.</p> + + + +<h2 id="Resources">Ресурсы приложения</h2> + +<p>Приложение Android состоит не только из кода — ему необходимы такие существующие отдельно от исходного кода +ресурсы, как изображения, аудиофайлы и все, что связано с визуальным +представлением приложения. Например, необходимо определять анимацию, меню, стили, цвета +и макет пользовательских интерфейсов операций в файлах XML. Используя ресурсы приложения, можно без труда +изменять его различные характеристики, не меняя код, а, кроме того, —путем предоставления +наборов альтернативных ресурсов — можно оптимизировать свое приложение для работы с различными +конфигурациями устройств (например, для различных языков или размеров экрана).</p> + +<p>Для каждого ресурса, включаемого в проект Android, инструменты SDK задают уникальный +целочисленный идентификатор, который может использоваться, чтобы сослаться на ресурс из кода приложения или из +других ресурсов, определенных в XML. Например, если в вашем приложении имеется файл изображения с именем {@code +logo.png} (сохраненный в папке {@code res/drawable/}), инструменты SDK сформируют идентификатор ресурса +под именем {@code R.drawable.logo}, с помощью которого на изображение можно будет ссылаться и вставлять его в +пользовательский интерфейс.</p> + +<p>Один из наиболее важных аспектов предоставления ресурсов отдельно от исходного кода +заключается в возможности использовать альтернативные ресурсы для различных конфигураций +устройств. Например, определив строки пользовательского интерфейса в XML, вы сможете перевести их на другие +языки и сохранить эти переводы в отдельных файлах. Затем по <em>квалификатору</em> языка +, добавленному к имени каталога ресурса (скажем {@code res/values-fr/} для строк на французском +языке), и выбранному пользователем языку система Android применит к вашему пользовательскому интерфейсу строки на +соответствующем языке.</p> + +<p>Android поддерживает разные <em>квалификаторы</em> для соответствующих ресурсов. Квалификатор + представляет собой короткую строку, которая включается в имена каталогов ресурсов с целью +определения конфигурации устройства, для которой эти ресурсы следует использовать. В качестве другого +примера можно сказать, что для своих операций следует создавать разные макеты, которые будут соответствовать +размеру и ориентации экрана устройства. Например, когда экран устройства имеет книжную +ориентацию (расположен вертикально), кнопки в макете можно также размещатьь по вертикали, а когда экран +развернут горизонтально (альбомная ориентация), кнопки следует размещать по горизонтали. Чтобы при изменении ориентации экрана изменялся макет, +можно определить два разных макета и применить соответствующий +квалификатор к имени каталога каждого макета. После этого система будет автоматически применять соответствующий +макет в зависимости от ориентации устройства.</p> + +<p>Подробные сведения о различных видах ресурсов, которые можно включить в приложение, а также о том, как +создавать альтернативные ресурсы для разных конфигурацией устройств, см. в разделе <a href="{@docRoot}guide/topics/resources/providing-resources.html">Предоставление ресурсов</a>.</p> + + + +<div class="next-docs"> +<div class="col-6"> + <h2 class="norule">Также читайте:</h2> + <dl> + <dt><a href="{@docRoot}guide/components/intents-filters.html">Объекты Intent и фильтры объектов Intent</a> + </dt> + <dd>Сведения об использовании API-интерфейсов {@link android.content.Intent} для + активации таких компонентов приложений, как операции и службы, а также о предоставлении возможности другим приложениям + использовать компоненты своего приложения.</dd> + <dt><a href="{@docRoot}guide/components/activities.html">Операции</a></dt> + <dd>Сведения о создании экземпляра класса {@link android.app.Activity}, + который выдает определенный экран в вашем приложении с пользовательским интерфейсом.</dd> + <dt><a href="{@docRoot}guide/topics/resources/providing-resources.html">Предоставление ресурсов</a></dt> + <dd>Описание структуры приложений Android, в которой ресурсы приложения существуют отдельно от + его кода, а также сведения о том, как предоставлять альтернативные ресурсы для определенных конфигураций + устройств. + </dd> + </dl> +</div> +<div class="col-6"> + <h2 class="norule">Возможно, вас также заинтересует:</h2> + <dl> + <dt><a href="{@docRoot}guide/practices/compatibility.html">Совместимость устройств</a></dt> + <dd>Сведения о том, каким образом система Android работает на устройствах разных типов, и общие сведения о том, + как оптимизировать свое приложение для каждого устройства или ограничить круг устройств, на которых может быть установлено + приложение.</dd> + <dt><a href="{@docRoot}guide/topics/security/permissions.html">Системные разрешения</a></dt> + <dd>Сведения о том, как система Android ограничивает доступ приложений к определенным API-интерфейсам с помощью системы + разрешений, которая требует согласия пользователя на использование этих API-интерфейсов вашим приложением.</dd> + </dl> +</div> +</div> + diff --git a/docs/html-intl/intl/ru/guide/components/index.jd b/docs/html-intl/intl/ru/guide/components/index.jd new file mode 100644 index 000000000000..41d5a3491bf2 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/index.jd @@ -0,0 +1,57 @@ +page.title=Компоненты приложения +page.landing=true +page.landing.intro=Платформа приложений системы Android позволяет создавать функциональные и инновационные приложения с помощью набора компонентов, которые можно использовать многократно. В этом разделе рассказывается о том, как создавать компоненты, определяющие элементы структуры вашего приложения, и как связывать их воедино с помощью объектов Intent. +page.metaDescription=Платформа приложений системы Android позволяет создавать функциональные и инновационные приложения с помощью набора компонентов, которые можно использовать многократно. В этом разделе рассказывается о том, как создавать компоненты, определяющие элементы структуры вашего приложения, и как связывать их воедино с помощью объектов Intent. +page.landing.image=images/develop/app_components.png +page.image=images/develop/app_components.png + +@jd:body + +<div class="landing-docs"> + + <div class="col-6"> + <h3>Статьи блога</h3> + + <a href="http://android-developers.blogspot.com/2012/05/using-dialogfragments.html"> + <h4>Использование класса DialogFragment</h4> + <p>В этой статье я расскажу, как с помощью DialogFragment с использованием вспомогательной библиотеки v4 (в целях обеспечения совместимости с устройствами, работающими под управлением системы с версией ниже, чем Honeycomb) можно отобразить простое диалоговое окно редактирования и вернуть результат в вызывающую операцию с помощью интерфейса.</p> + </a> + + <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html"> + <h4>Фрагменты для всех</h4> + <p>Сегодня мы выпустили библиотеку статических элементов, которая предоставляет доступ к тому же Fragments API (а также новому классу LoaderManager и нескольким другим классам), с тем чтобы приложения, совместимые с Android 1.6 и более поздними версиями, могли использовать фрагменты для создания пользовательских интерфейсов для планшетов. </p> + </a> + + <a href="http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html"> + <h4>Многопоточность для повышения производительности</h4> + <p>Для создания быстро реагирующих приложений рекомендуется, чтобы в основном потоке пользовательского интерфейса +выполнялся минимальный объем работы. Любая задача, которая в принципе может выполнять долго и привести к зависанию приложения, должна +обрабатываться в другом потоке.</p> + </a> + </div> + + <div class="col-6"> + <h3>Обучение</h3> + + <a href="http://developer.android.com/training/basics/activity-lifecycle/index.html"> + <h4>Управление жизненным циклом операций</h4> + <p>В этом учебном курсе разъясняются важные методы обратного вызова жизненного цикла, которые получает каждый экземпляр + операции, и описывается, как их использовать, чтобы операция выполнялась так, как этого ожидает пользователь, и не потребляла системные + ресурсы, когда они ей не нужны.</p> + </a> + + <a href="http://developer.android.com/training/basics/fragments/index.html"> + <h4>Создание динамического интерфейса пользователя с использованием фрагментов</h4> + <p>Данный курс обучения посвящен созданию динамического интерфейса пользователя с использованием +фрагментов и его оптимизации для устройств с экранами разных размеров, включая поддержку +устройств с версией Android 1.6.</p> + </a> + + <a href="http://developer.android.com/training/sharing/index.html"> + <h4>Общий доступ к контенту</h4> + <p>В этом курсе обучения рассказывается о некоторых стандартных способах отправки и получения контента + приложениями с помощью API-интерфейсов Intent и объекта ActionProvider.</p> + </a> + </div> + +</div> diff --git a/docs/html-intl/intl/ru/guide/components/intents-filters.jd b/docs/html-intl/intl/ru/guide/components/intents-filters.jd new file mode 100644 index 000000000000..d710081e4181 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/intents-filters.jd @@ -0,0 +1,899 @@ +page.title=Объекты Intent и фильтры объектов Intent +page.tags="IntentFilter" +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + +<h2>Содержание документа</h2> +<ol> + <li><a href="#Types">Типы объектов Intent</a></li> + <li><a href="#Building">Создание объекта Intent</a> + <ol> + <li><a href="#ExampleExplicit">Пример явного объекта Intent</a></li> + <li><a href="#ExampleSend">Пример неявного объекта Intent</a></li> + <li><a href="#ForceChooser">Принудительное использование блока выбора приложения</a></li> + </ol> + </li> + <li><a href="#Receiving">Получение неявного объекта Intent</a> + <ol> + <li><a href="#ExampleFilters">Примеры фильтров</a></li> + </ol> + </li> + <li><a href="#PendingIntent">Использование ожидающего объекта Intent</a></li> + <li><a href="#Resolution">Разрешение объектов Intent</a> + <ol> + <li><a href="#ActionTest">Тестирование действия</a></li> + <li><a href="#CategoryTest">Тестирование категории</a></li> + <li><a href="#DataTest">Тестирование данных</a></li> + <li><a href="#imatch">Сопоставление объектов Intent</a></li> + </ol> + </li> +</ol> + +<h2>См. также</h2> +<ol> +<li><a href="{@docRoot}training/basics/intents/index.html">Взаимодействие с другими приложениями</a></li> +<li><a href="{@docRoot}training/sharing/index.html">Общий доступ к контенту</a></li> +</ol> + +</div> +</div> + + + + +<p>{@link android.content.Intent} представляет собой объект обмена сообщениями, с помощью которого можно запросить выполнение действия +у <a href="{@docRoot}guide/components/fundamentals.html#Components">компонента другого приложения</a>. +Несмотря на то, что объекты Intent упрощают обмен данными между компонентами по нескольким аспектам, в основном они используются +в трех ситуациях:</p> + +<ul> +<li><b>Для запуска операции:</b> +<p>Компонент {@link android.app.Activity} представляет собой один экран в приложении. Для запуска нового +экземпляра компонента {@link android.app.Activity} необходимо передать объект {@link android.content.Intent} +методу{@link android.content.Context#startActivity startActivity()}. Объект {@link android.content.Intent} +описывает операцию, которую требуется запустить, а также содержит все остальные необходимые данные.</p> + +<p>Если после завершения операции от нее требуется получить результат, +вызовите метод {@link android.app.Activity#startActivityForResult +startActivityForResult()}. Ваша операция получит результат +в виде отдельного объекта {@link android.content.Intent} в обратном вызове метода {@link +android.app.Activity#onActivityResult onActivityResult()} операции. +Подробные сведения см. в руководстве <a href="{@docRoot}guide/components/activities.html">Операции</a>.</p></li> + +<li><b>Для запуска службы:</b> +<p>{@link android.app.Service} является компонентом, который выполняет действия в фоновом режиме +без пользовательского интерфейса. Службу можно запустить для выполнения однократного действия +(например, чтобы загрузить файл), передав объект{@link android.content.Intent} +методу {@link android.content.Context#startService startService()}. Объект {@link android.content.Intent} +описывает службу, которую требуется запустить, а также содержит все остальные необходимые данные.</p> + +<p>Если служба сконструирована с интерфейсом клиент-сервер, к ней +можно установить привязку из другого компонента, передав объект{@link android.content.Intent} методу {@link +android.content.Context#bindService bindService()}</code>. Подробные сведения см. в руководстве <a href="{@docRoot}guide/components/services.html">Службы</a>.</p></li> + +<li><b>Для рассылки широковещательных сообщений:</b> +<p>Широковещательное сообщение ― это сообщение, которое может принять любое приложение. Система выдает различные +широковещательные сообщения о системных событиях, например, когда система загружается или устройство начинает заряжаться. +Для выдачи широковещательных сообщений другим приложениям необходимо передать объект {@link android.content.Intent} +методу {@link android.content.Context#sendBroadcast(Intent) sendBroadcast()}, +{@link android.content.Context#sendOrderedBroadcast(Intent, String) +sendOrderedBroadcast()} или {@link +android.content.Context#sendStickyBroadcast sendStickyBroadcast()}.</p> +</li> +</ul> + + + + +<h2 id="Types">Типы объектов Intent</h2> + +<p>Есть два типа объектов Intent:</p> + +<ul> +<li><b>Явные объекты Intent</b> указывают компонент, который требуется запустить, по имени ( +полное имя класса). Явные объекты Intent обычно используются для запуска компонента из +вашего собственного приложения, поскольку вам известно имя класса операции или службы, которую необходимо запустить. Например +, можно запустить новую операцию в ответ на действие пользователя или запустить службу, чтобы загрузить +файл в фоновом режиме.</li> + +<li><b>Неявные объекты Intent</b> не содержат имени конкретного компонента. Вместо этого они в целом объявляют действие, +которое требуется выполнить, что дает возможность компоненту из другого приложения обработать этот запрос. Например, если требуется +показать пользователю место на карте, то с помощью неявного объекта Intent можно запросить, чтобы это сделало другое приложение, в котором +такая возможность предусмотрена.</li> +</ul> + +<p>Когда создан явный объект Intent для запуска операции или службы, система немедленно +запускает компонент приложения, указанный в объекте {@link android.content.Intent}.</p> + +<div class="figure" style="width:446px"> +<img src="{@docRoot}images/components/intent-filters@2x.png" width="446" alt="" /> +<p class="img-caption"><strong>Рисунок 1.</strong> Схематическое изображение процесса передачи неявного объекта Intent +по системе для запуска другой операции: <b>[1]</b> <em>Операция А</em> создает объект +{@link android.content.Intent} с описанием действия и передает его методу {@link +android.content.Context#startActivity startActivity()}. <b>[2]</b> Система Android ищет во всех +приложениях фильтры Intent, которые соответствуют данному объекту Intent. Когда приложение с подходящим фильтром найдено, <b>[3]</b> система +запускает соответствующую операцию (<em>Операция B</em>), вызвав ее метод {@link +android.app.Activity#onCreate onCreate()} и передав ему объект {@link android.content.Intent}. +</p> +</div> + +<p>Когда создан неявный объект Intent, система Android находит подходящий компонент путем +сравнения содержимого объекта Intent с <em>фильтрами Intent</em>, объявленными в <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">файлах манифеста</a> других приложений, имеющихся на +устройстве. Если объект Intent совпадает с фильтром Intent, система запускает этот компонент и передает ему +объект {@link android.content.Intent}. Если подходящими оказываются несколько фильтров Intent, система +выводит диалоговое окно, где пользователь может выбрать приложение для выполнения данного действия.</p> + +<p>Фильтр Intent представляет собой выражение в файле манифеста приложения, +указывающее типы объектов Intent, которые мог бы +принимать компонент. Например, объявив фильтр Intent для операции, +вы даете другим приложениям возможность напрямую запускать вашу операцию с помощью некоторого объекта Intent. +Точно так же, если вы <em>не</em> объявите какие-либо фильтры Intent для операции, то ее можно будет запустить +только с помощью явного объекта Intent.</p> + +<p class="caution"><strong>Внимание!</strong> В целях обеспечения безопасности приложения всегда используйте явный объект +Intent при запуске {@link android.app.Service} и не +объявляйте фильтры Intent для своих служб. Запуск служб с помощью неявных объектов Intent является +рискованным с точки зрения безопасности, поскольку нельзя быть на абсолютно уверенным, какая служба отреагирует на такой объект Intent, +а пользователь не может видеть, какая служба запускается. Начиная с Android 5.0 (уровень API 21) система +вызывает исключение при вызове метода {@link android.content.Context#bindService bindService()} +с помощью неявного объекта Intent.</p> + + + + + +<h2 id="Building">Создание объекта Intent</h2> + +<p>Объект {@link android.content.Intent} содержит информацию, на основании которой система Android +определяет, какой компонент требуется запустить (например, точное имя компонента или категорию +компонентов, которые должны получить этот объект Intent), а также сведения, которые необходимы компоненту-получателю, +чтобы надлежащим образом выполнить действие (а именно — выполняемое действие и данные, с которыми его требуется выполнить).</p> + + +<p>Основные сведения, содержащиеся в объекте {@link android.content.Intent}:</p> + +<dl> + +<dt><b>Имя компонента</b></dt> +<dd>Имя компонента, который требуется запустить. + +<p>Эта информация является необязательной, но именно она и делает объект Intent +<b>явным</b>. Ее наличие означает, что объект Intent следует доставить только компоненту приложения, +определенному по имени. При отсутствии имени компонента объект Intent является <b>неявным</b>, а +система определяет, какой компонент получит этот объект Intent по другим сведениям, которые в нем содержатся +(например, по действию, данным и категории — см. описание далее). Поэтому, если вам требуется запустить определенный +компонент из своего приложения, следует указать его имя.</p> + +<p class="note"><strong>Примечание.</strong> При запуске {@link android.app.Service} следует +<strong>всегда указывать имя компонента</strong>. В противном случае вы не сможете быть на абсолютно уверенным в том, какая служба +отреагирует на объект Intent, а пользователь не может видеть, какая служба запускается.</p> + +<p>Это поле объекта {@link android.content.Intent} представляет собой объект +{@link android.content.ComponentName}, который можно указать с помощью полного +имени класса целевого компонента, включая имя пакета приложения. Например, +{@code com.example.ExampleActivity}. Задать имя компонента можно с помощью метода {@link +android.content.Intent#setComponent setComponent()}, {@link android.content.Intent#setClass +setClass()}, {@link android.content.Intent#setClassName(String, String) setClassName()} или конструктора +{@link android.content.Intent}.</p> + +</dd> + +<p><dt><b>Действие</b></dt> +<dd>Строка, определяющая стандартное действие, которое требуется выполнить (например, <em>view</em> (просмотр) или <em>pick</em> (выбор)). + +<p>При выдаче объектов Intent с широковещательными сообщениями это действие, которое произошло и о котором сообщается. +Действие в значительной степени определяет, каким образом структурирована остальная часть объекта Intent,—в частности, +что именно содержится в разделе данных и дополнительных данных. + +<p>Для использования объектами Intent в пределах своего приложения (либо для использования другими +приложениями, чтобы вызывать компоненты из вашего приложения) можно указать собственные действия. Обычно же следует использовать константы действий, +определенные классом {@link android.content.Intent} или другими классами платформы. Вот несколько +стандартных действий для запуска операции:</p> + +<dl> +<dt>{@link android.content.Intent#ACTION_VIEW}</dt> + <dd>Используйте это действие в объекте Intent с методом {@link + android.content.Context#startActivity startActivity()}, когда имеется определенная информация, которую + операция может показать пользователю, например, фотография в приложении галереи или адрес для + просмотра в картографическом приложении.</dd> + +<dt>{@link android.content.Intent#ACTION_SEND}</dt> + <dd>Его еще называют объектом Intent "share" (намерение предоставить общий доступ). Это действие следует использовать в объекте Intent с методом {@link + android.content.Context#startActivity startActivity()}, при наличии определенных данных, доступ к которым пользователь может + предоставить через другое приложение, например приложение для работы с электронной почтой или социальными сетями.</dd> +</dl> + +<p>Другие константы, определяющие стандартные действия, см. в справочнике по классу {@link android.content.Intent} +. Другие действия определяются +в других частях платформы Android. Например, в {@link android.provider.Settings} определяются действия, +открывающие ряд экранов приложения настройки системы.</p> + +<p>Действие можно указать для объекта Intent с методом {@link android.content.Intent#setAction +setAction()} или конструктором {@link android.content.Intent}.</p> + +<p>Если вы определяете собственные действия, обязательно используйте в качестве их префикса имя пакета +вашего приложения. Например:</p> +<pre>static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";</pre> +</dd> + +<dt><b>Данные</b></dt> +<dd>URI (объект {@link android.net.Uri}), ссылающийся на данные, с которыми будет выполняться действие и/или +тип MIME этих данных. Тип передаваемых данных обычно определяется действием объекта Intent. Например, +если действием является {@link android.content.Intent#ACTION_EDIT}, в данных должен содержаться +URI документа, который требуется отредактировать. + +<p>При создании объекта Intent, +помимо URI, зачастую бывает важно указать тип данных (их тип MIME). +Например, операция, которая может выводить на экран изображения, скорее всего, не сможет +воспроизвести аудиофайл, даже если и у тех, и у других данных будут одинаковые форматы URI. +Поэтому указание типа MIME данных помогает системе Android +найти наиболее подходящий компонент для получения вашего объекта Intent. +Однако тип MIME иногда можно унаследовать от URI — в частности, когда данные представляют собой +{@code content:} URI, который указывает, что данные находятся на устройстве и ими управляет +{@link android.content.ContentProvider}, а это дает возможность системе видеть тип MIME данных.</p> + +<p>Чтобы задать только URI данных, вызовите {@link android.content.Intent#setData setData()}. +Чтобы задать только тип MIME, вызовите {@link android.content.Intent#setType setType()}. При необходимости +оба этих параметра можно в явном виде задать с помощью {@link +android.content.Intent#setDataAndType setDataAndType()}.</p> + +<p class="caution"><strong>Внимание!</strong> Если требуется задать и URI, и тип MIME, +<strong>не</strong> вызывайте {@link android.content.Intent#setData setData()} и +{@link android.content.Intent#setType setType()}, поскольку каждый из этих методов аннулирует результат выполнения другого. +Чтобы задать URI и тип MIME всегда используйте + метод {@link android.content.Intent#setDataAndType setDataAndType()}.</p> +</dd> + +<p><dt><b>Категория</b></dt> +<dd>Строка, содержащая прочие сведения о том, каким компонентом +должна выполняться обработка этого объекта Intent. В объект Intent можно поместить любое количество +описаний категорий, однако большинству объектов Intent категория не требуется. +Вот некоторые стандартные категории: + +<dl> +<dt>{@link android.content.Intent#CATEGORY_BROWSABLE}</dt> + <dd>Целевая операция позволяет запускать себя веб-браузером для отображения данных, + указанных по ссылке — например, изображения или сообщения электронной почты. + </dd> +<dt>{@link android.content.Intent#CATEGORY_LAUNCHER}</dt> + <dd>Эта операция является начальной операцией задачи, она указана в + средстве запуска приложений системы. + </dd> +</dl> + +<p>Полный список категорий см. в описании класса {@link android.content.Intent} +.</p> + +<p>Указать категорию можно с помощью{@link android.content.Intent#addCategory addCategory()}.</p> +</dd> +</dl> + + +<p>Приведенные выше свойства (имя компонента, действие, данные и категория) представляют собой +характеристики, определяющие объект Intent. На основании этих свойств система Android +может решить, какой компонент следует запустить.</p> + +<p>Однако в объекте Intent может быть приведена и другая информация, которая не влияет на то, +каким образом определяется требуемый компонент приложения. Объект Intent также может содержать:</p> + +<dl> +<dt><b>Дополнительные данные</b></dt> +<dd>Пары "ключ-значение", содержащие прочую информацию, которая необходима для выполнения запрошенного действия. +Точно так же, как некоторые действия используют определенные виды URI данных, некоторые действия используют определенные дополнительные данные. + +<p>Добавлять дополнительные данные можно с помощью различных методов {@link android.content.Intent#putExtra putExtra()}, +каждый из которых принимает два параметра: имя и значение ключа. +Также можно создать объект {@link android.os.Bundle} со всеми дополнительными данными, затем вставить +объект {@link android.os.Bundle} в объект {@link android.content.Intent} с помощью метода {@link +android.content.Intent#putExtras putExtras()}.</p> + +<p>Например, при создании объекта Intent для отправки сообщения электронной почты с методом +{@link android.content.Intent#ACTION_SEND} можно указать получателя с помощью ключа +{@link android.content.Intent#EXTRA_EMAIL}, а тему сообщения ― с помощью ключа +{@link android.content.Intent#EXTRA_SUBJECT}.</p> + +<p>Класс {@link android.content.Intent} указывает много констант {@code EXTRA_*} +для стандартных типов данных. Если вам требуется объявить собственные дополнительные ключи (для объектов Intent, которые +принимает ваше приложение), обязательно указывайте в качестве префикса +имя пакета своего приложения. Например:</p> +<pre>static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";</pre> +</dd> + +<dt><b>Флаги</b></dt> +<dd>Флаги, определенные в классе {@link android.content.Intent}, которые действуют как метаданные для объекта +Intent. Флаги должны указывать системе Android, каким образом следует запускать операцию (например, к какой +<a href="{@docRoot}guide/components/tasks-and-back-stack.html">задаче</a> должна принадлежать операция +) и как с ней обращаться после запуска (например, будет ли она указана в списке последних +операций). + +<p>Подробные сведения см. в документе, посвященном методу {@link android.content.Intent#setFlags setFlags()}.</p> +</dd> + +</dl> + + + + +<h3 id="ExampleExplicit">Пример явного объекта Intent</h3> + +<p>Явные объекты Intent используются для запуска конкретных компонентов приложения, например +определенной операции или службы. Чтобы создать явный объект Intent, задайте +имя компонента для объекта {@link android.content.Intent} — все +остальные свойства объекта Intent можно не задавать.</p> + +<p>Например, если в своем приложении вы создали службу с именем {@code DownloadService}, +предназначенную для загрузки файлов из Интернета, то для ее запуска можно использовать следующий код:</p> + +<pre> +// Executed in an Activity, so 'this' is the {@link android.content.Context} +// The fileUrl is a string URL, such as "http://www.example.com/image.png" +Intent downloadIntent = new Intent(this, DownloadService.class); +downloadIntent.setData({@link android.net.Uri#parse Uri.parse}(fileUrl)); +startService(downloadIntent); +</pre> + +<p>Конструктор {@link android.content.Intent#Intent(Context,Class)} +предоставляет {@link android.content.Context} приложению, а +компоненту ― объект {@link java.lang.Class}. Фактически +этот объект Intent явно запускает класс{@code DownloadService} в приложении.</p> + +<p>Подробные сведения о создании и запуске службы см. в руководстве +<a href="{@docRoot}guide/components/services.html">Службы</a>.</p> + + + + +<h3 id="ExampleSend">Пример неявного объекта Intent</h3> + +<p>Неявный объект Intent указывает действие, которым может быть вызвано любое имеющееся на устройстве приложение, способное +выполнить это действие. Неявные объекты Intent используются, когда ваше приложение не может выполнить +то или иное действие, а другие приложения, скорее всего, могут и вы хотите, чтобы пользователь имел возможность выбрать, какое приложение для этого использовать.</p> + +<p>Например, если у вас есть контент и вы хотите, чтобы пользователь поделился им с другими людьми, создайте объект Intent +с действием {@link android.content.Intent#ACTION_SEND} +и добавьте дополнительные данные, указывающие на контент, общий доступ к которому следует предоставить. Когда с помощью этого объекта Intent вы вызываете +{@link android.content.Context#startActivity startActivity()}, пользователь сможет +выбрать приложение, посредством которого к контенту будет предоставлен общий доступ.</p> + +<p class="caution"><strong>Внимание!</strong> Возможна ситуация, когда на устройстве пользователя не будет<em>никакого</em> +приложения, которое может откликнуться на неявный объект Intent, отправленный вами методу {@link android.content.Context#startActivity +startActivity()}. В этом случае вызов закончится неудачей, а работа приложения аварийно завершится. Чтобы проверить, +будет получен ли операцией объект Intent, вызовите метод {@link android.content.Intent#resolveActivity +resolveActivity()} для своего объекта {@link android.content.Intent}. Если результатом будет значение, отличное от null, +значит, имеется хотя бы одно приложение, которое способно откликнуться на объект Intent и можно вызывать +{@link android.content.Context#startActivity startActivity()}. Если же результатом будет значение null, +объект Intent не следует использовать и по возможности следует отключить функцию, которая выдает +этот объект Intent.</p> + + +<pre> +// Create the text message with a string +Intent sendIntent = new Intent(); +sendIntent.setAction(Intent.ACTION_SEND); +sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage); +sendIntent.setType("text/plain"); + +// Verify that the intent will resolve to an activity +if (sendIntent.resolveActivity(getPackageManager()) != null) { + startActivity(sendIntent); +} +</pre> + +<p class="note"><strong>Примечание.</strong> В этом случае URI не используется, а вместо этого следует объявить +тип данных объекта Intent, чтобы указать контент, содержащийся в дополнительных данных.</p> + + +<p>При вызове метода {@link android.content.Context#startActivity startActivity()} система +анализирует все установленные приложения, чтобы определить, какие из них могут откликнуться на объект Intent этого вида (объект +Intent с действием {@link android.content.Intent#ACTION_SEND} и данными "text/plain" +). Если имеется только одно подходящее приложение, оно будет сразу же открыто и получит данный объект +Intent. Если объект Intent принимают несколько операций, система +отображает диалоговое окно, в котором пользователь может выбрать приложение для выполнения данного действия.</p> + + +<div class="figure" style="width:200px"> + <img src="{@docRoot}images/training/basics/intent-chooser.png" alt=""> + <p class="img-caption"><strong>Рисунок 2.</strong> Диалоговое окно выбора.</p> +</div> + +<h3 id="ForceChooser">Принудительное использование блока выбора приложения</h3> + +<p>При наличии нескольких приложений, откликающихся на ваш неявный объект Intent, +пользователь может выбрать требуемое приложение и указать, что оно будет по умолчанию выполнять это +действие. Это удобно в случае действия, для выполнения которого +пользователь обычно хочет всегда использовать одно и то же приложение, например, при открытии веб-страницы (пользователи +обычно используют один и тот же браузер).</p> + +<p>Однако если на объект Intent могут откликнуться несколько приложений, возможно, пользователь предпочтет каждый раз использовать другое +приложение, поэтому следует явно выводить диалоговое окно выбора. В диалоговом окне +выбора приложения пользователю предлагается при каждом запуске выбирать, какое приложение использовать для действия (пользователь не может выбрать приложение, +используемое по умолчанию). Например, когда ваше приложение выполняет операцию "share" (поделиться) с помощью действия {@link +android.content.Intent#ACTION_SEND}, пользователи могут, в зависимости от ситуации, предпочесть каждый раз делать это с помощью разных приложений +, поэтому следует всегда использовать диалоговое окно выбора, как показано на рисунке 2.</p> + + + + +<p>Чтобы вывести на экран блок выбора приложения, создайте {@link android.content.Intent} с помощью {@link +android.content.Intent#createChooser createChooser()} и передайте его {@link +android.app.Activity#startActivity startActivity()}. Например:</p> + +<pre> +Intent sendIntent = new Intent(Intent.ACTION_SEND); +... + +// Always use string resources for UI text. +// This says something like "Share this photo with" +String title = getResources().getString(R.string.chooser_title); +// Create intent to show the chooser dialog +Intent chooser = Intent.createChooser(sendIntent, title); + +// Verify the original intent will resolve to at least one activity +if (sendIntent.resolveActivity(getPackageManager()) != null) { + startActivity(chooser); +} +</pre> + +<p>В результате на экран будет выведено диалоговое окно со списком приложений, которые могут отреагировать на объект Intent, переданный методу {@link +android.content.Intent#createChooser createChooser()} и используют указанный текст в качестве +заголовка диалога.</p> + + + + + + + + + +<h2 id="Receiving">Получение неявного объекта Intent</h2> + +<p>Чтобы указать, какие неявные объекты Intent может принимать ваше приложение, объявите один или несколько фильтров Intent для +каждого компонента приложения с помощью элемента <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a> +в <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">файле манифеста</a>. +Каждый фильтр Intent указывает тип объектов Intent, которые принимает компонент на основании действия, +данных и категории, заданных в объекте Intent. Система передаст неявный объект Intent вашему приложению, только если +он может пройти через один из ваших фильтров Intent.</p> + +<p class="note"><strong>Примечание.</strong> Явный объект Intent всегда доставляется его целевому компоненту, +без учета любых фильтров Intent, объявленных компонентом.</p> + +<p>Компонент приложения должен объявлять отдельные фильтры для каждой уникальной работы, которую он может выполнить. +Например, у операции из приложения для работы с галереей изображений может быть два фильтра: один фильтр +для просмотра изображения, и второй для его редактирования. Когда операция запускается, +она анализирует объект {@link android.content.Intent} и выбирает режим своей работы на основании информации, +приведенной в {@link android.content.Intent} (например, показывать элементы управления редактора или нет).</p> + +<p>Каждый фильтр Intent определяется элементом <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a> +в файле манифеста приложения, указанном в объявлении соответствующего компонента приложения (например, +в элементе <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a>). + Внутри элемента <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a>, +можно указать тип объектов Intent, которые будут приниматься, с помощью одного или нескольких +из следующих трех элементов:</p> + +<dl> +<dt><a href="{@docRoot}guide/topics/manifest/action-element.html">{@code <action>}</a></dt> + <dd>Объявляет принимаемое действие, заданное в объекте Intent, в атрибуте {@code name}. Значение + должно быть текстовой строкой действия, а не константой класса.</dd> +<dt><a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a></dt> + <dd>Объявляет тип принимаемых данных, для чего используется один или несколько атрибутов, указывающих различные + составные части URI данных (<code>scheme</code>, <code>host</code>, <code>port</code>, + <code>path</code> и т. д.) и тип MIME.</dd> +<dt><a href="{@docRoot}guide/topics/manifest/category-element.html">{@code <category>}</a></dt> + <dd>Объявляет принимаемую категорию, заданную в объекте Intent, в атрибуте {@code name}. Значение + должно быть текстовой строкой действия, а не константой класса. + + <p class="note"><strong>Примечание.</strong> Для получения неявных объектов Intent + <strong>необходимо включить</strong> категорию +{@link android.content.Intent#CATEGORY_DEFAULT} в фильтр Intent. Методы + {@link android.app.Activity#startActivity startActivity()} и + {@link android.app.Activity#startActivityForResult startActivityForResult()} обрабатывают все объекты Intent, как если бы они объявляли + категорию{@link android.content.Intent#CATEGORY_DEFAULT}. + Если вы не объявляете эту категорию в своем фильтре Intent, никакие неявные объекты Intent не будут переданы в +вашу операцию.</p> + </dd> +</dl> + +<p>В следующем примере объявлена операция с фильтром Intent, определяющим получение объекта Intent +{@link android.content.Intent#ACTION_SEND}, когда данные относятся к типу text:</p> + +<pre> +<activity android:name="ShareActivity"> + <intent-filter> + <action android:name="android.intent.action.SEND"/> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="text/plain"/> + </intent-filter> +</activity> +</pre> + +<p>Можно создавать фильтры, в которых будет несколько экземпляров +<a href="{@docRoot}guide/topics/manifest/action-element.html">{@code <action>}</a>, +<a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a> или +<a href="{@docRoot}guide/topics/manifest/category-element.html">{@code <category>}</a>. +В этом случае просто нужно убедиться в том, что компонент может справиться с любыми сочетаниями +этих элементов фильтра.</p> + +<p>Когда требуется обрабатывать объекты Intent нескольких видов, но только в определенных сочетаниях +действия, типа данных и категории, необходимо создать несколько фильтров Intent.</p> + + +<div class="sidebox-wrapper"> +<div class="sidebox"> +<h2>Ограничение доступа к компонентам</h2> +<p>Использование фильтра Intent не является безопасным способом предотвращения запуска ваших компонентов +другими приложениями. Несмотря на то, что после применения фильтров Intent компонент будет реагировать только +на неявные объекты Intent определенного вида, другое приложение теоретически может запустить компонент вашего приложения +с помощью явного объекта Intent, если разработчик определит имена ваших компонентов. +Если важно, чтобы <em>только ваше собственное приложение</em> могло запускать один из ваших компонентов, +задайте для атрибута <a href="{@docRoot}guide/topics/manifest/activity-element.html#exported">{@code +exported}</a> этого компонента значение {@code "false"}. +</p> +</div> +</div> + +<p>Неявный объект Intent проверяется фильтром путем сравнения объекта Intent с каждым из +этих трех элементов. Чтобы объект Intent был доставлен компоненту, он должен пройти все три теста. +Если он не будет соответствовать хотя бы одному из них, система Android не доставит этот объект Intent +компоненту. Однако, поскольку у компонента может быть несколько фильтров Intent, объект Intent, который +не проходит через один из фильтров компонента, может пройти через другой фильтр. +Подробные сведения о том, каким образом система принимает решения по поводу объектов Intent, см. в приведенном далее разделе + <a href="#Resolution">Разрешение объектов Intent</a>.</p> + +<p class="caution"><strong>Внимание!</strong> Чтобы случайно не запустить +{@link android.app.Service} другого приложения, всегда используйте явные объекты Intent для запуска собственных служб и не +объявляйте для них фильтры Intent.</p> + +<p class="note"><strong>Примечание.</strong> +Фильтры Intent необходимо объявлять в файле манифеста для всех операций. +Фильтры для приемников широковещательных сообщений можно регистрировать динамически путем вызова +{@link android.content.Context#registerReceiver(BroadcastReceiver, IntentFilter, String, +Handler) registerReceiver()}. После этого регистрацию приемника широковещательных сообщений можно отменить с помощью {@link +android.content.Context#unregisterReceiver unregisterReceiver()}. В результате ваше приложение +сможет воспринимать определенные объявления только в течение указанного периода времени в процессе работы +приложения.</p> + + + + + + + +<h3 id="ExampleFilters">Примеры фильтров</h3> + +<p>Чтобы лучше понять различные режимы работы фильтров Intent, рассмотрите следующий фрагмент +из файла манифеста приложения для работы с социальными сетями.</p> + +<pre> +<activity android:name="MainActivity"> + <!-- This activity is the main entry, should appear in app launcher --> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> +</activity> + +<activity android:name="ShareActivity"> + <!-- This activity handles "SEND" actions with text data --> + <intent-filter> + <action android:name="android.intent.action.SEND"/> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="text/plain"/> + </intent-filter> + <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data --> + <intent-filter> + <action android:name="android.intent.action.SEND"/> + <action android:name="android.intent.action.SEND_MULTIPLE"/> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="application/vnd.google.panorama360+jpg"/> + <data android:mimeType="image/*"/> + <data android:mimeType="video/*"/> + </intent-filter> +</activity> +</pre> + +<p>Первая операция ({@code MainActivity}) является основной точкой входа приложения — это операция, которая +открывается, когда пользователь запускает приложение нажатием на его значок:</p> +<ul> + <li>Действие {@link android.content.Intent#ACTION_MAIN} + указывает на то, что это основная точка входа, и не ожидает никаких данных объекта Intent.</li> + <li>Категория {@link android.content.Intent#CATEGORY_LAUNCHER} указывает, что значок этой операции + следует поместить в средство запуска приложений системы. Если элемент <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> + не содержит указаний на конкретный значок с помощью {@code icon}, то система воспользуется значком из элемента <a href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a> +.</li> +</ul> +<p>Чтобы операция отображалась в средстве запуска приложений системы, два этих элемента необходимо связать вместе.</p> + +<p>Вторая операция ({@code ShareActivity}) предназначена для упрощения обмена текстовым и мультимедийным +контентом. Несмотря на то, что пользователи могут входить в эту операцию, выбрав ее из {@code MainActivity}, +они также могут входить в {@code ShareActivity} напрямую из другого приложения, которое выдает неявный объект +Intent, соответствующий одному из двух фильтров Intent.</p> + +<p class="note"><strong>Примечание.</strong> Тип MIME +(<a href="https://developers.google.com/panorama/android/">{@code +application/vnd.google.panorama360+jpg}</a>) является особым типом данных, указывающим на +панорамные фотографии, с которыми можно работать с помощью API-интерфейсов <a href="{@docRoot}reference/com/google/android/gms/panorama/package-summary.html">Google +panorama</a>.</p> + + + + + + + + + + + + + +<h2 id="PendingIntent">Использование ожидающего объекта Intent</h2> + +<p>Объект {@link android.app.PendingIntent} является оболочкой, в которую заключается объект {@link +android.content.Intent}. Объект {@link android.app.PendingIntent} +в основном предназначен для того, чтобы предоставлять разрешение внешним приложениям +на использование содержащегося в нем объекта {@link android.content.Intent}, как если бы он исполнялся из +процесса вашего собственного приложения.</p> + +<p>Основные варианты использования ожидающего объекта Intent:</p> +<ul> + <li>Объявление объекта Intent, который должен будет исполняться, когда пользователь выполняет действие с вашим <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">уведомлением</a> + ({@link android.app.NotificationManager} системы Android + исполняет {@link android.content.Intent}). + <li>Объявление объекта Intent, который должен будет исполняться, когда пользователь выполняет действие с вашим +<a href="{@docRoot}guide/topics/appwidgets/index.html">виджетом приложения</a> + (приложение главного экрана исполняет {@link android.content.Intent}). + <li>Объявление объекта Intent, который должен будет исполняться в указанное время в будущем ({@link android.app.AlarmManager} системы +Android исполняет {@link android.content.Intent}). +</ul> + +<p>Поскольку каждый объект {@link android.content.Intent} предназначен для обработки компонентом приложения, +который относится к определенному типу ({@link android.app.Activity}, {@link android.app.Service} или + {@link android.content.BroadcastReceiver}), объект {@link android.app.PendingIntent} также следует создавать +с учетом этого обстоятельства. При использовании ожидающего объекта Intent ваше приложение не будет +исполнять объект Intent вызовом, например, {@link android.content.Context#startActivity +startActivity()}. Вместо этого вам необходимо будет объявить требуемый тип компонента при создании +{@link android.app.PendingIntent} путем вызова соответствующего метода-создателя:</p> + +<ul> + <li>Метод {@link android.app.PendingIntent#getActivity PendingIntent.getActivity()} для + {@link android.content.Intent}, который запускает {@link android.app.Activity}.</li> + <li>Метод {@link android.app.PendingIntent#getService PendingIntent.getService()} для + {@link android.content.Intent}, который запускает {@link android.app.Service}.</li> + <li>Метод {@link android.app.PendingIntent#getBroadcast PendingIntent.getBroadcast()} для + {@link android.content.Intent}, который запускает {@link android.content.BroadcastReceiver}.</li> +</ul> + +<p>Если только ваше приложение не <em>принимает</em> ожидающие объекты Intent от других приложений, +указанные выше методы создания {@link android.app.PendingIntent} являются единственными методами +{@link android.app.PendingIntent}, которые вам когда-либо понадобятся.</p> + +<p>Каждый метод принимает текущий {@link android.content.Context} приложения, объект +{@link android.content.Intent}, который требуется поместить в оболочку, и один или несколько флагов, указывающих, +каким образом следует использовать объект Intent (например, можно ли использовать объект Intent неоднократно).</p> + +<p>Подробные сведения об использовании ожидающих объектов Intent приведены в документации по каждому +из соответствующих вариантов использования, например, в руководствах, посвященным API-интерфейсам: <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Уведомления</a> +и <a href="{@docRoot}guide/topics/appwidgets/index.html">Виджеты приложений</a>.</p> + + + + + + + +<h2 id="Resolution">Разрешение объектов Intent</h2> + + +<p>Когда система получает неявный объект Intent для запуска операции, она выполняет поиск +наиболее подходящей операции путем сравнения объекта Intent с фильтрами Intent по трем критериям:</p> + +<ul> + <li>действие объекта Intent; + <li>данные объекта Intent (тип URI и данных); + <li>категория объекта Intent. +</ul> + +<p>В следующих разделах описывается, каким образом объекты Intent сопоставляются с соответствующими компонентами, +а именно, как должен быть фильтр Intent объявлен в файле манифеста приложения.</p> + + +<h3 id="ActionTest">Тестирование действия</h3> + +<p>Для указания принимаемых действий объекта Intent фильтр Intent может объявлять любое (в том числе нулевое) число элементов +<a href="{@docRoot}guide/topics/manifest/action-element.html">{@code +action>}</a>. Например:</p> + +<pre> +<intent-filter> + <action android:name="android.intent.action.EDIT" /> + <action android:name="android.intent.action.VIEW" /> + ... +</intent-filter> +</pre> + +<p>Чтобы пройти через этот фильтр, действие, указанное в объекте {@link android.content.Intent}, + должно соответствовать одному или нескольким действиям, перечисленным в фильтре.</p> + +<p>Если в фильтре не перечислены какие-либо действия, объекту +Intent будет нечему соответствовать, поэтому все объекты Intent не пройдут этот тест. Однако, если в объекте {@link android.content.Intent} +не указано действие, он пройдет тест (если в фильтре +содержится хотя бы одно действие).</p> + + + +<h3 id="CategoryTest">Тестирование категории</h3> + +<p>Для указания принимаемых категорий объекта Intent фильтр Intent может объявлять любое (в том числе нулевое) число элементов +<a href="{@docRoot}guide/topics/manifest/category-element.html">{@code +<category>}</a>. Например:</p> + +<pre> +<intent-filter> + <category android:name="android.intent.category.DEFAULT" /> + <category android:name="android.intent.category.BROWSABLE" /> + ... +</intent-filter> +</pre> + +<p>Чтобы объект Intent прошел тестирование категории, все категории, приведенные в объекте {@link android.content.Intent}, +должны соответствовать категории из фильтра. Обратное не требуется — фильтр Intent может +объявлять и другие категории, которых нет в объекте {@link android.content.Intent}, объект +{@link android.content.Intent} при этом все равно пройдет тест. Поэтому объект Intent без категорий +всегда пройдет этот тест, независимо от того, какие категории объявлены в фильтре.</p> + +<p class="note"><strong>Примечание.</strong> +Система Android автоматически применяет категорию {@link android.content.Intent#CATEGORY_DEFAULT} +ко всем неявным объектам Intent, которые передаются в {@link +android.content.Context#startActivity startActivity()} и {@link +android.app.Activity#startActivityForResult startActivityForResult()}. +Поэтому, если вы хотите, чтобы ваша операция принимала неявные объекты Intent, в ее фильтрах Intent +должна быть указана категория для {@code "android.intent.category.DEFAULT"} (как +показано в предыдущем примере {@code <intent-filter>}).</p> + + + +<h3 id="DataTest">Тестирование данных</h3> + +<p>Для указания принимаемых данных объекта Intent фильтр Intent может объявлять любое (в том числе нулевое) число элементов +<a href="{@docRoot}guide/topics/manifest/data-element.html">{@code +<data>}</a>. Например:</p> + +<pre> +<intent-filter> + <data android:mimeType="video/mpeg" android:scheme="http" ... /> + <data android:mimeType="audio/mpeg" android:scheme="http" ... /> + ... +</intent-filter> +</pre> + +<p>Каждый элемент <code><a href="{@docRoot}guide/topics/manifest/data-element.html"><data></a></code> +может конкретизировать структуру URI и тип данных (тип мультимедиа MIME). Имеются отдельные +атрибуты — {@code scheme}, {@code host}, {@code port} +и {@code path} — для каждой составной части URI: +</p> + +<p style="margin-left: 2em">{@code <scheme>://<host>:<port>/<path>}</p> + +<p> +Например: +</p> + +<p style="margin-left: 2em">{@code content://com.example.project:200/folder/subfolder/etc}</p> + +<p>В этом URI схема имеет вид {@code content}, узел ― {@code com.example.project}, +порт ― {@code 200}, а путь ― {@code folder/subfolder/etc}. +</p> + +<p>Каждый из этих атрибутов является необязательным в элементе <a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a>, +однако имеются линейные зависимости:</p> +<ul> + <li>Если схема не указана, узел игнорируется.</li> + <li>Если узел не указан, порт игнорируется.</li> + <li>Если не указана ни схема, ни узел, путь игнорируется.</li> +</ul> + +<p>Когда URI, указанный в объекте Intent, сравнивается с URI из фильтра, +сравнение выполняется только с теми составными частями URI, которые приведены в фильтре. Например:</p> +<ul> + <li>Если в фильтре указана только схема, то все URI с этой схемой будет соответствовать +фильтру.</li> + <li>Если в фильтре указаны схема и полномочия, но отсутствует путь, все URI +с такими же схемой и полномочиями пройдут фильтр, а их пути учитываться не будут.</li> + <li>Если в фильтре указаны схема, полномочия и путь, то только URI с такими же схемой, +полномочиями и путем пройдут фильтр.</li> +</ul> + +<p class="note"><strong>Примечание.</strong> Путь может быть +указан с подстановочным символом (*), чтобы потребовалось только частичное соответствие имени пути.</p> + +<p>При выполнении тестирования данных сравнивается и URI, и тип MIME, указанные в объекте Intent, с URI +и типом MIME из фильтра. Действуют следующие правила: +</p> + +<ol type="a"> +<li>Объект Intent, который не содержит ни URI, ни тип MIME, пройдет этот +тест, только если в фильтре не указано никаких URI или типов MIME.</li> + +<li>Объект Intent, в котором имеется URI, но отсутствует тип MIME (ни явный, ни тот, который можно вывести из +URI), пройдет этот тест, только если URI соответствует формату URI из фильтра +, а в фильтре также не указан тип MIME.</li> + +<li>Объект Intent, в котором имеется тип MIME, но отсутствует URI, пройдет этот тест, +только если в фильтре указан тот же тип MIME и не указан формат URI.</li> + +<li>Объект Intent, в котором имеется и URI, и тип MIME (явный или тот, который можно вывести из +URI), пройдет только часть этого теста, проверяющую тип MIME, +в том случае, если этот тип совпадает с типом, приведенным в фильтре. Он пройдет часть этого теста, которая проверяет URI, +либо если его URI совпадает с URI из фильтра, либо если этот объект содержит URI {@code content:} +или {@code file:}, а в фильтре URI не указан. Другими словами, +предполагается, что компонент поддерживает данные {@code content:} и {@code file:}, если в его +фильтре указан <em>только</em> тип MIME.</p></li> +</ol> + +<p> +Это последнее правило (правило (d)) отражает ожидание того, +что компоненты будут в состоянии получать локальные данные из файла или от поставщика контента. +Поэтому их фильтры могут содержать только тип данных, а явно +указывать схемы {@code content:} и {@code file:} не требуется. +Это типичный случай. Например, такой элемент <a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a>, как +приведенный далее, сообщает системе Android, что компонент может получать данные изображений от поставщика +контента и выводить их на экран: +</p> + +<pre> +<intent-filter> + <data android:mimeType="image/*" /> + ... +</intent-filter></pre> + +<p> +Поскольку имеющиеся данные преимущественно распространяются поставщиками контента, фильтры, в которых +указан тип данных и нет URI, вероятно, являются самыми распространенными. +</p> + +<p> +Другой стандартной конфигурацией являются фильтры со схемой и типом данных. Например, +такой элемент <a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a>, +как приведенный далее, сообщает системе Android, что +компонент может получать видеоданные из сети для выполнения действия: +</p> + +<pre> +<intent-filter> + <data android:scheme="http" android:type="video/*" /> + ... +</intent-filter></pre> + + + +<h3 id="imatch">Сопоставление объектов Intent</h3> + +<p>Объекты Intent сопоставляются с фильтрами Intent не только для определения целевого +компонента, который требуется активировать, но также для выявления определенных сведений о наборе +компонентов, имеющихся на устройстве. Например, приложение главного экрана заполняет средство запуска приложений +путем поиска всех операций с фильтрами Intent, в которых указано действие +{@link android.content.Intent#ACTION_MAIN} и категория +{@link android.content.Intent#CATEGORY_LAUNCHER}.</p> + +<p>Ваше приложение может использовать сопоставление объектов Intent таким же образом. +В {@link android.content.pm.PackageManager} имеется набор методов {@code query...()}, +которые возвращают все компоненты, способные принять определенный объект, а также +схожий набор методов {@code resolve...()}, которые определяют наиболее подходящий +компонент, способный реагировать на объект Intent. Например, метод +{@link android.content.pm.PackageManager#queryIntentActivities +queryIntentActivities()} возвращает передаваемый как аргумент список всех операций, которые могут выполнить +объект Intent, а метод {@link +android.content.pm.PackageManager#queryIntentServices +queryIntentServices()} возвращает такой же список служб. +Ни тот, ни другой метод не активирует компоненты; они просто перечисляют те из них, которые +могут откликнуться. Имеется схожий метод для приемников широковещательных сообщений ( +{@link android.content.pm.PackageManager#queryBroadcastReceivers +). +</p> + + + + diff --git a/docs/html-intl/intl/ru/guide/components/loaders.jd b/docs/html-intl/intl/ru/guide/components/loaders.jd new file mode 100644 index 000000000000..eea72a2296ee --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/loaders.jd @@ -0,0 +1,494 @@ +page.title=Загрузчики +parent.title=Операции +parent.link=activities.html +@jd:body +<div id="qv-wrapper"> +<div id="qv"> + <h2>Содержание документа</h2> + <ol> + <li><a href="#summary">Сводная информация об API-интерфейсе загрузчика</a></li> + <li><a href="#app">Использование загрузчиков в приложении</a> + <ol> + <li><a href="#requirements"></a></li> + <li><a href="#starting">Запуск загрузчика</a></li> + <li><a href="#restarting">Перезапуск загрузчика</a></li> + <li><a href="#callback">Использование обратных вызовов класса LoaderManager</a></li> + </ol> + </li> + <li><a href="#example">Пример</a> + <ol> + <li><a href="#more_examples">Другие примеры</a></li> + </ol> + </li> + </ol> + + <h2>Основные классы</h2> + <ol> + <li>{@link android.app.LoaderManager}</li> + <li>{@link android.content.Loader}</li> + + </ol> + + <h2>Образцы кода по теме</h2> + <ol> + <li> <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.html"> +LoaderCursor</a></li> + <li> <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> +LoaderThrottle</a></li> + </ol> + </div> +</div> + +<p>Загрузчики, которые появились в Android 3.0, упрощают асинхронную загрузку данных +в операцию или фрагмент. Загрузчики имеют следующие свойства:</p> + <ul> + <li>Они имеются для любых операций {@link android.app.Activity} и фрагментов {@link +android.app.Fragment}.</li> + <li>Они обеспечивают асинхронную загрузку данных.</li> + <li>Они отслеживают источник своих данных и выдают новые результаты при +изменении контента.</li> + <li>Они автоматически переподключаются к последнему курсору загрузчика при +воссоздании после изменения конфигурации. Таким образом, им не требуется повторно запрашивать свои +данные.</li> + </ul> + +<h2 id="summary">Сводная информация об API-интерфейсе загрузчика</h2> + +<p>Имеется несколько классов и интерфейсов, которые могут использовать +загрузчики в приложении. Они приведены в этой таблице:</p> + +<table> + <tr> + <th>Класс/интерфейс</th> + <th>Описание</th> + </tr> + <tr> + <td>{@link android.app.LoaderManager}</td> + <td>Абстрактный класс, связываемый с {@link android.app.Activity} или +{@link android.app.Fragment} для управления одним или несколькими интерфейсами {@link +android.content.Loader}. Это позволяет приложению управлять +длительно выполняющимися операциями вместе с жизненным циклом {@link android.app.Activity} +или {@link android.app.Fragment}; чаще всего этот класс используется с +{@link android.content.CursorLoader}, однако приложения могут писать +свои собственные загрузчики для работы с другими типами данных. + <br /> + <br /> + Имеется только один класс {@link android.app.LoaderManager} на операцию или фрагмент. Однако у класса {@link android.app.LoaderManager} может быть +несколько загрузчиков.</td> + </tr> + <tr> + <td>{@link android.app.LoaderManager.LoaderCallbacks}</td> + <td>Интерфейс обратного вызова, обеспечивающий взаимодействие клиента с {@link +android.app.LoaderManager}. Например, с помощью метода обратного вызова {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} +создается новый загрузчик.</td> + </tr> + <tr> + <td>{@link android.content.Loader}</td> + <td>Абстрактный класс, который выполняет асинхронную загрузку данных. Это +базовый класс для загрузчика. Обычно используется {@link +android.content.CursorLoader}, но можно реализовать и собственный подкласс. Когда +загрузчики активны, они должны отслеживать источник своих данных и выдавать новые +результаты при изменении контента. </td> + </tr> + <tr> + <td>{@link android.content.AsyncTaskLoader}</td> + <td>Абстрактный загрузчик, который предоставляет {@link android.os.AsyncTask} для выполнения работы.</td> + </tr> + <tr> + <td>{@link android.content.CursorLoader}</td> + <td>Подкласс класса {@link android.content.AsyncTaskLoader}, который запрашивает +{@link android.content.ContentResolver} и возвращает {@link +android.database.Cursor}. Этот класс реализует протокол {@link +android.content.Loader} стандартным способом для выполнения запросов к курсорам. +Он строится на {@link android.content.AsyncTaskLoader} для выполнения запроса к курсору +в фоновом потоке, чтобы не блокировать пользовательский интерфейс приложения. Использование +этого загрузчика — это лучший способ асинхронной загрузки данных из {@link +android.content.ContentProvider} вместо выполнения управляемого запроса через +платформу или API-интерфейсы операции.</td> + </tr> +</table> + +<p>Приведенные в этой таблице классы и интерфейсы являются наиболее важными компонентами, +с помощью которых в приложении реализуется загрузчик. При создании каждого загрузчика +не нужно использовать все эти компоненты, однако всегда следует указывать ссылку на {@link +android.app.LoaderManager} для инициализации загрузчика и использовать реализацию +класса {@link android.content.Loader}, например {@link +android.content.CursorLoader}. В следующих разделах рассказывается, как использовать эти +классы и интерфейсы в приложении.</p> + +<h2 id ="app">Использование загрузчиков в приложении</h2> +<p>В этом разделе описывается использование загрузчиков в приложении для Android. В +приложениях, использующих загрузчики, обычно имеются следующие элементы:</p> +<ul> + <li>{@link android.app.Activity} или {@link android.app.Fragment};</li> + <li>экземпляр {@link android.app.LoaderManager};</li> + <li>{@link android.content.CursorLoader} для загрузки данных, выдаваемых {@link +android.content.ContentProvider}. Вы также можете реализовать собственный подкласс +класса {@link android.content.Loader} или {@link android.content.AsyncTaskLoader} для +загрузки данных из другого источника;</li> + <li>реализация для {@link android.app.LoaderManager.LoaderCallbacks}. +Именно здесь создаются новые загрузчики и ведется управление ссылками на существующие +загрузчики;</li> +<li>способ отображения данных загрузчика, например {@link +android.widget.SimpleCursorAdapter};</li> + <li>источник данных, например {@link android.content.ContentProvider}, когда используется +{@link android.content.CursorLoader}.</li> +</ul> +<h3 id="starting">Запуск загрузчика</h3> + +<p>{@link android.app.LoaderManager} управляет одним или несколькими экземплярами {@link +android.content.Loader} в {@link android.app.Activity} или +{@link android.app.Fragment}. Имеется только один {@link +android.app.LoaderManager} на каждую операцию или каждый фрагмент.</p> + +<p>{@link android.content.Loader} обычно +инициализируется в методе {@link +android.app.Activity#onCreate onCreate()} операции или в методе фрагмента +{@link android.app.Fragment#onActivityCreated onActivityCreated()}. Делается +это следующим образом:</p> + +<pre>// Prepare the loader. Either re-connect with an existing one, +// or start a new one. +getLoaderManager().initLoader(0, null, this);</pre> + +<p>Метод {@link android.app.LoaderManager#initLoader initLoader()} принимает +следующие параметры:</p> +<ul> + <li>уникальный идентификатор, обозначающий загрузчик. В данном примере идентификатором является 0;</li> +<li>необязательные аргументы, которые передаются загрузчику при +построении (в данном примере это <code>null</code>);</li> + +<li>реализация {@link android.app.LoaderManager.LoaderCallbacks}, которая +вызывает класс {@link android.app.LoaderManager} для выдачи событий загрузчика. В данном +примере локальный класс реализует интерфейс {@link +android.app.LoaderManager.LoaderCallbacks}, поэтому он передает ссылку +самому себе: {@code this}.</li> +</ul> +<p>Вызов {@link android.app.LoaderManager#initLoader initLoader()} обеспечивает инициализацию +загрузчика. Возможен один из двух результатов:</p> +<ul> + <li>Если загрузчик, указанный с помощью идентификатора, уже существует, будет повторно использован загрузчик, созданный +последним.</li> + <li>Если загрузчик, указанный с помощью идентификатора, <em>не</em> существует, +{@link android.app.LoaderManager#initLoader initLoader()} вызывает метод +{@link android.app.LoaderManager.LoaderCallbacks} из {@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}. +Именно здесь реализуется код для создания экземпляра и возврата нового загрузчика. +Более подробные сведения см. в разделе <a href="#onCreateLoader">onCreateLoader</a>.</li> +</ul> +<p>В любом случае данная реализация {@link android.app.LoaderManager.LoaderCallbacks} +связывается с загрузчиком и будет вызываться +при изменении состояния загрузчика. Если в момент этого вызова вызывающий компонент находится в +запущенном состоянии, это означает, что запрошенный загрузчик уже существует и сформировал свои +данные. В этом случае система сразу же вызовет {@link +android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} + (во время{@link android.app.LoaderManager#initLoader initLoader()}), +будьте готовы к этому. Более подробные сведения об этом обратном вызове см. в разделе <a href="#onLoadFinished"> +onLoadFinished</a>.</p> + +<p>Обратите внимание, что метод {@link android.app.LoaderManager#initLoader initLoader()} +возвращает создаваемый класс {@link android.content.Loader}, но записывать +ссылку на него не требуется. Класс {@link android.app.LoaderManager} управляет +жизненным циклом загрузчика автоматически. Класс {@link android.app.LoaderManager} +начинает загрузку и заканчивает ее при необходимости, а также поддерживает состояние загрузчика +и связанного с ним контента. А это подразумевает, что вы будете редко взаимодействовать с загрузчиками +напрямую (однако пример использования методов загрузчика для тонкой настройки его +поведения см. в образце кода <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> LoaderThrottle</a>). +Для вмешательства в процесс загрузки при возникновении определенных событий обычно используются методы {@link +android.app.LoaderManager.LoaderCallbacks} +. Более подробные сведения об этом см. в разделе <a href="#callback">Использование обратных вызовов LoaderManager</a>.</p> + +<h3 id="restarting">Перезапуск загрузчика</h3> + +<p>При использовании метода {@link android.app.LoaderManager#initLoader initLoader()}, как +показано выше, он задействует существующий загрузчик с указанным идентификатором — в случае его наличия. +Если такого загрузчика нет, метод его создаст. Однако иногда старые данные нужно отбросить +и начать все заново.</p> + +<p>Для удаления старых данных используется метод {@link +android.app.LoaderManager#restartLoader restartLoader()}. Например, эта +реализация метода{@link android.widget.SearchView.OnQueryTextListener} перезапускает +загрузчик, когда изменяется запрос пользователя. Загрузчик необходимо перезагрузить, + с тем чтобы он мог использовать измененный фильтр поиска для выполнения нового запроса:</p> + +<pre> +public boolean onQueryTextChanged(String newText) { + // Called when the action bar search text has changed. Update + // the search filter, and restart the loader to do a new query + // with this filter. + mCurFilter = !TextUtils.isEmpty(newText) ? newText : null; + getLoaderManager().restartLoader(0, null, this); + return true; +}</pre> + +<h3 id="callback">Использование обратных вызовов класса LoaderManager</h3> + +<p>{@link android.app.LoaderManager.LoaderCallbacks} представляет собой интерфейс обратного вызова, +который позволяет клиенту взаимодействовать с классом {@link android.app.LoaderManager}. </p> +<p>Ожидается, что загрузчики, в частности {@link android.content.CursorLoader}, будут сохранять +свои данные после их остановки. Это позволяет приложениям сохранять свои +данные в методах {@link android.app.Activity#onStop +onStop()} и {@link android.app.Activity#onStart onStart()} операции или фрагмента, с тем чтобы, +когда пользователь вернется в приложение, ему не пришлось ждать, пока данные +загрузятся заново. Методы {@link android.app.LoaderManager.LoaderCallbacks} используются, +чтобы узнать, когда требуется создать новый загрузчик, а также для того, чтобы указать приложению, когда +пришло время перестать использовать данные загрузчика.</p> + +<p>Интерфейс {@link android.app.LoaderManager.LoaderCallbacks} использует следующие +методы:</p> +<ul> + <li>{@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} — +создает экземпляр и возвращает новый класс {@link android.content.Loader} для данного идентификатора. +</li></ul> +<ul> + <li> {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} +— вызывается, когда созданный ранее загрузчик завершил загрузку. +</li></ul> +<ul> + <li>{@link android.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()} +— вызывается, когда состояние созданного ранее загрузчика сбрасывается, в результате чего его данные +теряются. +</li> +</ul> +<p>Более подробно эти методы описаны в разделах ниже.</p> + +<h4 id ="onCreateLoader">onCreateLoader</h4> + +<p>При попытке доступа к загрузчику (например, посредством метода {@link +android.app.LoaderManager#initLoader initLoader()}), он проверяет, существует ли +загрузчик, указанный с помощью идентификатора. Если он не существует, он вызывает метод {@link +android.app.LoaderManager.LoaderCallbacks} {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}. Именно +здесь и создается новый загрузчик. Обычно это будет класс {@link +android.content.CursorLoader}, однако можно реализовать и собственный подкласс класса {@link +android.content.Loader}. </p> + +<p>В этом примере метод обратного вызова {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} +создает класс {@link android.content.CursorLoader}. Вы должны построить +класс {@link android.content.CursorLoader} с помощью его метода конструктора, для чего +требуется полный набор информации, которая нужна для выполнения запроса к {@link +android.content.ContentProvider}. В частности, требуется:</p> +<ul> + <li><em>uri</em> — URI контента, который необходимо получить; </li> + <li><em>projection</em> — список столбцов, которые будут возвращены. При передаче +<code>null</code> будут возвращены все столбцы, а это неэффективно; </li> + <li><em>selection</em> — фильтр, объявляющий, какие строки возвращать, +отформатированный в виде предложения SQL WHERE (за исключением самого WHERE). При передаче +<code>null</code> будут возвращены все строки для данного URI; </li> + <li><em>selectionArgs</em> — в выборку можно включить символы "?", которые будут +заменены значениями из <em>selectionArgs</em> в порядке следования в +выборке. Значения будут привязаны как строки; </li> + <li><em>sortOrder</em> — порядок расположения строк, отформатированный в виде предложения SQL +ORDER BY (за исключением самого ORDER BY). При передаче <code>null</code> будет +использоваться стандартный порядок сортировки, поэтому, список, возможно, будет неотсортирован.</li> +</ul> +<p>Например:</p> +<pre> + // If non-null, this is the current filter the user has provided. +String mCurFilter; +... +public Loader<Cursor> onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + // First, pick the base URI to use depending on whether we are + // currently filtering. + Uri baseUri; + if (mCurFilter != null) { + baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, + Uri.encode(mCurFilter)); + } else { + baseUri = Contacts.CONTENT_URI; + } + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + + Contacts.DISPLAY_NAME + " != '' ))"; + return new CursorLoader(getActivity(), baseUri, + CONTACTS_SUMMARY_PROJECTION, select, null, + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); +}</pre> +<h4 id="onLoadFinished">onLoadFinished</h4> + +<p>Этот метод вызывается, когда созданный ранее загрузчик завершил загрузку. +Этот метод гарантировано вызывается до высвобождения последних данных, +которые были предоставлены этому загрузчику. К этому моменту необходимо полностью перестать использовать +старые данные (поскольку они скоро будут заменены). Однако этого не следует делать +самостоятельно, поскольку данными владеет загрузчик и он позаботится об этом.</p> + + +<p>Загрузчик высвободит данные, как только узнает, что приложение их больше не +использует. Например, если данными является курсор из {@link +android.content.CursorLoader}, не следует вызывать {@link +android.database.Cursor#close close()} самостоятельно. Если курсор +размещается в {@link android.widget.CursorAdapter}, следует использовать метод {@link +android.widget.SimpleCursorAdapter#swapCursor swapCursor()} с тем, чтобы +старый {@link android.database.Cursor} не закрылся. Например:</p> + +<pre> +// This is the Adapter being used to display the list's data.<br +/>SimpleCursorAdapter mAdapter; +... + +public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); +}</pre> + +<h4 id="onLoaderReset">onLoaderReset</h4> + +<p>Этот метод вызывается, когда состояние созданного ранее загрузчика сбрасывается, в результате чего +его данные теряются. Этот обратный вызов позволяет узнать, когда данные +вот-вот будут высвобождены, с тем чтобы можно было удалить свою ссылку на них. </p> +<p>Данная реализация вызывает +{@link android.widget.SimpleCursorAdapter#swapCursor swapCursor()} +со значением <code>null</code>:</p> + +<pre> +// This is the Adapter being used to display the list's data. +SimpleCursorAdapter mAdapter; +... + +public void onLoaderReset(Loader<Cursor> loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.swapCursor(null); +}</pre> + + +<h2 id="example">Пример</h2> + +<p>В качестве примера далее приведена полная реализация фрагмента {@link +android.app.Fragment}, который отображает {@link android.widget.ListView} с +результатами запроса к поставщику такого контента, как контакты. Для управления запросом к поставщику используется класс {@link +android.content.CursorLoader}.</p> + +<p>Чтобы приложение могло обращаться к контактам пользователя, как показано в этом примере, в его +манифесте должно присутствовать разрешение +{@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS}.</p> + +<pre> +public static class CursorLoaderListFragment extends ListFragment + implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> { + + // This is the Adapter being used to display the list's data. + SimpleCursorAdapter mAdapter; + + // If non-null, this is the current filter the user has provided. + String mCurFilter; + + @Override public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + // Give some text to display if there is no data. In a real + // application this would come from a resource. + setEmptyText("No phone numbers"); + + // We have a menu item to show in action bar. + setHasOptionsMenu(true); + + // Create an empty adapter we will use to display the loaded data. + mAdapter = new SimpleCursorAdapter(getActivity(), + android.R.layout.simple_list_item_2, null, + new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS }, + new int[] { android.R.id.text1, android.R.id.text2 }, 0); + setListAdapter(mAdapter); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + getLoaderManager().initLoader(0, null, this); + } + + @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + // Place an action bar item for searching. + MenuItem item = menu.add("Search"); + item.setIcon(android.R.drawable.ic_menu_search); + item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + SearchView sv = new SearchView(getActivity()); + sv.setOnQueryTextListener(this); + item.setActionView(sv); + } + + public boolean onQueryTextChange(String newText) { + // Called when the action bar search text has changed. Update + // the search filter, and restart the loader to do a new query + // with this filter. + mCurFilter = !TextUtils.isEmpty(newText) ? newText : null; + getLoaderManager().restartLoader(0, null, this); + return true; + } + + @Override public boolean onQueryTextSubmit(String query) { + // Don't care about this. + return true; + } + + @Override public void onListItemClick(ListView l, View v, int position, long id) { + // Insert desired behavior here. + Log.i("FragmentComplexList", "Item clicked: " + id); + } + + // These are the Contacts rows that we will retrieve. + static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { + Contacts._ID, + Contacts.DISPLAY_NAME, + Contacts.CONTACT_STATUS, + Contacts.CONTACT_PRESENCE, + Contacts.PHOTO_ID, + Contacts.LOOKUP_KEY, + }; + public Loader<Cursor> onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + // First, pick the base URI to use depending on whether we are + // currently filtering. + Uri baseUri; + if (mCurFilter != null) { + baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, + Uri.encode(mCurFilter)); + } else { + baseUri = Contacts.CONTENT_URI; + } + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + + Contacts.DISPLAY_NAME + " != '' ))"; + return new CursorLoader(getActivity(), baseUri, + CONTACTS_SUMMARY_PROJECTION, select, null, + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); + } + + public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); + } + + public void onLoaderReset(Loader<Cursor> loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.swapCursor(null); + } +}</pre> +<h3 id="more_examples">Другие примеры</h3> + +<p>В <strong>ApiDemos</strong> есть несколько различных примеров, которые +иллюстрируют использование загрузчиков:</p> +<ul> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.html"> +LoaderCursor</a> — полная версия +показанного выше фрагмента.</li> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> LoaderThrottle</a> — пример того, как использовать регулирование +для сокращения числа запросов, выполняемых поставщиком контента при изменении его данных.</li> +</ul> + +<p>Сведения о загрузке и установке образцов кода SDK см. в статье <a href="http://developer.android.com/resources/samples/get.html"> Получение +образцов кода</a>. </p> + diff --git a/docs/html-intl/intl/ru/guide/components/processes-and-threads.jd b/docs/html-intl/intl/ru/guide/components/processes-and-threads.jd new file mode 100644 index 000000000000..fd298e0d3c49 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/processes-and-threads.jd @@ -0,0 +1,411 @@ +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>Запись манифеста для каждого типа элементов компонента —<a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code +<activity>}</a>, <a href="{@docRoot}guide/topics/manifest/service-element.html">{@code +<service>}</a>, <a href="{@docRoot}guide/topics/manifest/receiver-element.html">{@code +<receiver>}</a> и <a href="{@docRoot}guide/topics/manifest/provider-element.html">{@code +<provider>}</a> —поддерживает атрибут {@code android:process}, позволяющий задавать +процесс, в котором следует выполнять этот компонент. Можно установить этот атрибут так, чтобы каждый компонент выполнялся +в собственном процессе, или так, чтобы только некоторые компоненты совместно использовали один процесс. Можно также настроить процесс +{@code android:process} так, чтобы компоненты разных приложений выполнялись в одном +процессе, при условии что приложения совместно используют один идентификатор пользователя Linux и выполняют вход с +одним сертификатом.</p> + +<p>Элемент <a href="{@docRoot}guide/topics/manifest/application-element.html">{@code +<application>}</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}, которая выполняется "на переднем плане", — службу, +которая называется {@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> — этот пример изменяет {@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<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); + } +} +</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}, которые отвечают на эти запросы, —методы {@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()} —вызываются из пула потоков в процессе поставщика контента, а не в процессе +потока пользовательского интерфейса. Поскольку эти методы могут вызываться из любого числа потоков одновременно, +они также должны быть реализованы с сохранением потокобезопасности. </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> +--> diff --git a/docs/html-intl/intl/ru/guide/components/recents.jd b/docs/html-intl/intl/ru/guide/components/recents.jd new file mode 100644 index 000000000000..37206318e2f3 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/recents.jd @@ -0,0 +1,256 @@ +page.title=Экран обзора +page.tags="recents","overview" + +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + + <h2>Содержание документа</h2> + <ol> + <li><a href="#adding">Добавление задач на экран обзора</a> + <ol> + <li><a href="#flag-new-doc">Использование флага Intent для добавления задачи</a></li> + <li><a href="#attr-doclaunch">Использование атрибута Операция для добавления задачи</a></li> + </ol> + </li> + <li><a href="#removing">Удаление задач</a> + <ol> + <li><a href="#apptask-remove">Использование класса AppTask для удаления задач</a></li> + <li><a href="#retain-finished">Сохранение завершенных задач</a></li> + </ol> + </li> + </ol> + + <h2>Ключевые классы</h2> + <ol> + <li>{@link android.app.ActivityManager.AppTask}</li> + <li>{@link android.content.Intent}</li> + </ol> + + <h2>Пример кода</h2> + <ol> + <li><a href="{@docRoot}samples/DocumentCentricApps/index.html">Приложения для работы с документами</a></li> + </ol> + +</div> +</div> + +<p>Экран обзора (также используются названия: экран последних задач, список последних задач или последние приложения) +является элементом пользовательского интерфейса системного уровня, в котором содержится список последних <a href="{@docRoot}guide/components/activities.html"> +операций</a> и <a href="{@docRoot}guide/components/tasks-and-back-stack.html">задач</a>. Пользователь +может перемещаться по списку и выбирать задачи для возобновления, или жестом удалять задачи +из списка. В версии Android 5.0 (уровень API 21) несколько экземпляров +одной операции, содержащие различные документы, могут отображаться в виде задач на экране обзора. Например, +Google Диск может иметь задачу для каждого из нескольких документов Google. На экране обзора каждый документ +отображается в виде задачи.</p> + +<img src="{@docRoot}images/components/recents.png" alt="" width="284" /> +<p class="img-caption"><strong>Рисунок 1.</strong> Экран обзора, на котором показаны три документа Google Диск, + представленные в виде отдельных задач.</p> + +<p>Обычно следует разрешить системе определить способ представления ваших задач и +операций на экране обзора. Вам не нужно менять это поведение. +Однако приложение может определять способ и время появления операции на экране обзора. С помощью класса +{@link android.app.ActivityManager.AppTask} можно управлять задачами, а с помощью флагов операции класса +{@link android.content.Intent} указывается, когда операция добавляется на экран обзора +или удаляется с него. Кроме того, атрибуты <code><a href="{@docRoot}guide/topics/manifest/activity-element.html"> +<activity></a></code> позволяют устанавливать поведение в манифесте.</p> + +<h2 id="adding">Добавление задач на экран обзора</h2> + +<p>Использование флагов класса {@link android.content.Intent} для добавления задачи обеспечивает лучшее управление +временем и способом открытия или повторного открытия документа на экране обзора. С помощью атрибутов +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> +можно выбрать открытие документа в новой задаче или повторное использование +существующей задачи для документа.</p> + +<h3 id="flag-new-doc">Использование флага Intent для добавления задачи</h3> + +<p>При создании нового документа для операции вы вызываете метод +{@link android.app.ActivityManager.AppTask#startActivity(android.content.Context, android.content.Intent, android.os.Bundle) startActivity()} +класса {@link android.app.ActivityManager.AppTask}, передавая ему intent, +который запускает операцию. Для вставки логического разрыва, чтобы система обрабатывала вашу операцию как новую +задачу на экране обзора, передайте флаг {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} +в метод {@link android.content.Intent#addFlags(int) addFlags()} {@link android.content.Intent}, +который запускает операцию.</p> + +<p class="note"><strong>Примечание.</strong> Флаг {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} +замещает флаг {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET}, +который является устаревшим для систем Android 5.0 и выше (уровень API 21).</p> + +<p>Если вы установили флаг {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} при создании +нового документа, система всегда создает новую задачу с целевой операцией в качестве корня. +Этот параметр позволяет открывать один документ в нескольких задачах. Следующий код показывает, +как это делает основная операция:</p> + +<p class="code-caption"><a href="{@docRoot}samples/DocumentCentricApps/index.html"> +DocumentCentricActivity.java</a></p> +<pre> +public void createNewDocument(View view) { + final Intent newDocumentIntent = newDocumentIntent(); + if (useMultipleTasks) { + newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + } + startActivity(newDocumentIntent); + } + + private Intent newDocumentIntent() { + boolean useMultipleTasks = mCheckbox.isChecked(); + final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class); + newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, incrementAndGet()); + return newDocumentIntent; + } + + private static int incrementAndGet() { + Log.d(TAG, "incrementAndGet(): " + mDocumentCounter); + return mDocumentCounter++; + } +} +</pre> + +<p class="note"><strong>Примечание.</strong> Операции, запущенные с флагом {@code FLAG_ACTIVITY_NEW_DOCUMENT}, +должны иметь значение атрибута {@code android:launchMode="standard"} (по умолчанию), установленное +в манифесте.</p> + +<p>Когда основная операция запускает новую операцию, система ищет в существующих задачах одну, +значение intent которой соответствует имени компонента и данным Intent для операции. Если задача +не найдена или intent содержит флаг {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}, +создается новая задача с операцией в качестве корня. Если задача найдена, система выводит + эту задачу на передний план и передает новое значение intent в {@link android.app.Activity#onNewIntent onNewIntent()}. +Новая операция получает intent и создает новый документ на экране обзора, как +в следующем примере:</p> + +<p class="code-caption"><a href="{@docRoot}samples/DocumentCentricApps/index.html"> +NewDocumentActivity.java</a></p> +<pre> +@Override +protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_new_document); + mDocumentCount = getIntent() + .getIntExtra(DocumentCentricActivity.KEY_EXTRA_NEW_DOCUMENT_COUNTER, 0); + mDocumentCounterTextView = (TextView) findViewById( + R.id.hello_new_document_text_view); + setDocumentCounterText(R.string.hello_new_document_counter); +} + +@Override +protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + /* If FLAG_ACTIVITY_MULTIPLE_TASK has not been used, this activity + is reused to create a new document. + */ + setDocumentCounterText(R.string.reusing_document_counter); +} +</pre> + + +<h3 id="#attr-doclaunch">Использование атрибута Операция для добавления задачи</h3> + +<p>В манифесте операции можно также указать, что операция всегда запускается в новой задаче. Для этого используется атрибут +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> +, <a href="{@docRoot}guide/topics/manifest/activity-element.html#dlmode"> +{@code android:documentLaunchMode}</a>. Этот атрибут имеет четыре значения, которые работают следующим образом, +когда пользователь открывает документ в приложении:</p> + +<dl> + <dt>"{@code intoExisting}"</dt> + <dd>Операция повторно использует существующую задачу для документа. Это равносильно установке +флага {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} <em>без</em> установки +флага {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}, как описано в разделе +<a href="#flag-new-doc">Использование флага Intent для добавления задачи</a> выше.</dd> + + <dt>"{@code always}"</dt> + <dd>Операция создает новую задачу для документа, даже если документ уже открыт. Использование +этого значения равносильно установке обоих флагов {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} +и {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}.</dd> + + <dt>"{@code none”}"</dt> + <dd>Операция не создает новой задачи для документа. Экран обзора обрабатывает +операцию как операцию по умолчанию: на экране обзора отображается одна задача для приложения, которая +возобновляется с любой последней операции, вызванной пользователем.</dd> + + <dt>"{@code never}"</dt> + <dd>Операция не создает новой задачи для документа. Установка этого значения +переопределяет поведение флагов {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} + и {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}, если оба они установлены в +intent, и на экране обзора отображается одна задача для приложения, которая +возобновляется с любой последней операции, вызванной пользователем.</dd> +</dl> + +<p class="note"><strong>Примечание.</strong> Для значений кроме {@code none} и {@code never} +операция должна быть определена с атрибутом {@code launchMode="standard"}. Если этот атрибут не указан, +используется {@code documentLaunchMode="none"}.</p> + +<h2 id="removing">Удаление задач</h2> + +<p>По умолчанию задача документа автоматически удаляется с экрана обзора после завершения +соответствующей операции. Можно переопределить это поведение с помощью класса {@link android.app.ActivityManager.AppTask}, +с флагом {@link android.content.Intent} или атрибутом <code><a href="{@docRoot}guide/topics/manifest/activity-element.html"> +<activity></a></code>.</p> + +<p>Можно в любой момент полностью убрать задачу с экрана обзора, установив для атрибута +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> + <a href="{@docRoot}guide/topics/manifest/activity-element.html#exclude"> +{@code android:excludeFromRecents}</a> значение {@code true}.</p> + +<p>Можно установить максимальное число задач, которое ваше приложение может включить в экран обзора, установив для атрибута +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> + <a href="{@docRoot}guide/topics/manifest/activity-element.html#maxrecents">{@code android:maxRecents} +</a> целое значение. Значение по умолчанию: 16. При достижении максимального количества задач самая +долго не используемая задача удаляется с экрана обзора. Максимальное значение {@code android:maxRecents} составляет +50 (25 для устройств с малым объемом памяти); Значения менее 1 не допускаются.</p> + +<h3 id="#apptask-remove">Использование класса AppTask для удаления задач</h3> + +<p>В операции, которая создает новую задачу на экране обзора, +можно указать время удаления задачи и завершения всех связанных с ней операций, вызвав +метод {@link android.app.ActivityManager.AppTask#finishAndRemoveTask() finishAndRemoveTask()}.</p> + +<p class="code-caption"><a href="{@docRoot}samples/DocumentCentricApps/index.html"> +NewDocumentActivity.java</a></p> +<pre> +public void onRemoveFromRecents(View view) { + // The document is no longer needed; remove its task. + finishAndRemoveTask(); +} +</pre> + +<p class="note"><strong>Примечание.</strong> Использование +метода {@link android.app.ActivityManager.AppTask#finishAndRemoveTask() finishAndRemoveTask()}, +который переопределяет использование тега {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS}, +рассмотрен ниже.</p> + +<h3 id="#retain-finished">Сохранение завершенных задач</h3> + +<p>Чтобы сохранить задачу на экране обзора, даже если ее операция завершена, передайте +флаг {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS} в метод +{@link android.content.Intent#addFlags(int) addFlags()} объекта Intent, который запускает операцию.</p> + +<p class="code-caption"><a href="{@docRoot}samples/DocumentCentricApps/index.html"> +DocumentCentricActivity.java</a></p> +<pre> +private Intent newDocumentIntent() { + final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class); + newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | + android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS); + newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, incrementAndGet()); + return newDocumentIntent; +} +</pre> + +<p>Для достижения того же результата установите для атрибута +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> + <a href="{@docRoot}guide/topics/manifest/activity-element.html#autoremrecents"> +{@code android:autoRemoveFromRecents}</a> значение {@code false}. Значение по умолчанию {@code true} +для операций документа и {@code false} для обычных операций. Использование этого атрибута переопределяет +флаг {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS}, описанный выше.</p> + + + + + + + diff --git a/docs/html-intl/intl/ru/guide/components/services.jd b/docs/html-intl/intl/ru/guide/components/services.jd new file mode 100644 index 000000000000..62a6a7ef9d15 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/services.jd @@ -0,0 +1,813 @@ +page.title=Службы +@jd:body + +<div id="qv-wrapper"> +<ol id="qv"> +<h2>Содержание документа</h2> +<ol> +<li><a href="#Basics">Основы</a></li> +<ol> + <li><a href="#Declaring">Объявление службы в манифесте</a></li> +</ol> +<li><a href="#CreatingAService">Создание запущенной службы</a> + <ol> + <li><a href="#ExtendingIntentService">Наследование класса IntentService</a></li> + <li><a href="#ExtendingService">Наследование класса Service</a></li> + <li><a href="#StartingAService">Запуск службы</a></li> + <li><a href="#Stopping">Остановка службы</a></li> + </ol> +</li> +<li><a href="#CreatingBoundService">Создание привязанной службы</a></li> +<li><a href="#Notifications">Отправка уведомлений пользователю</a></li> +<li><a href="#Foreground">Запуск службы на переднем плане</a></li> +<li><a href="#Lifecycle">Управление жизненным циклом службы</a> +<ol> + <li><a href="#LifecycleCallbacks">Реализация обратных вызовов жизненного цикла</a></li> +</ol> +</li> +</ol> + +<h2>Ключевые классы</h2> +<ol> + <li>{@link android.app.Service}</li> + <li>{@link android.app.IntentService}</li> +</ol> + +<h2>Примеры</h2> +<ol> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArguments.html">{@code + ServiceStartArguments}</a></li> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html">{@code + LocalService}</a></li> +</ol> + +<h2>См. также:</h2> +<ol> +<li><a href="{@docRoot}guide/components/bound-services.html">Привязанные службы</a></li> +</ol> + +</div> + + +<p>{@link android.app.Service} является компонентом приложения, который может выполнять +длительные операции в фоновом режиме и не содержит пользовательского интерфейса. Другой компонент +приложения может запустить службу, которая продолжит работу в фоновом режиме даже в том случае, когда пользователь +перейдет в другое приложение. Кроме того, компонент может привязаться к службе для +взаимодействия с ней и даже выполнять межпроцессное взаимодействие (IPC). Например, служба может +обрабатывать сетевые транзакции, воспроизводить музыку, выполнять ввод-вывод файла или взаимодействовать с поставщиком контента, и все +это в фоновом режиме.</p> + +<p>Фактически служба может принимать две формы:</p> + +<dl> + <dt>Запущенная</dt> + <dd>Служба является «запущенной», когда компонент приложения (например, операция) запускает ее +вызовом {@link android.content.Context#startService startService()}. После запуска служба +может работать в фоновом режиме в течение неограниченного времени, даже если уничтожен компонент, который ее запустил. Обычно +запущенная служба выполняет одну операцию и не возвращает результатов вызывающему компоненту. +Например, она может загружать или выгружать файл по сети. Когда операция выполнена, +служба должна остановиться самостоятельно.</dd> + <dt>Привязанная</dt> + <dd>Служба является «привязанной», когда компонент приложения привязывается к ней вызовом {@link +android.content.Context#bindService bindService()}. Привязанная служба предлагает интерфейс клиент-сервер, +который позволяет компонентам взаимодействовать со службой, отправлять запросы, получать результаты и даже +делать это между разными процессами посредством межпроцессного взаимодействия (IPC). Привязанная служба работает только пока +к ней привязан другой компонент приложения. К службе могут быть привязаны несколько компонентов одновременно, +но когда все они отменяют привязку, служба уничтожается.</dd> +</dl> + +<p>Хотя в этой документации эти два типа служб обсуждаются отдельно, служба может +работать обеими способами — она может быть запущенной (и работать в течение неограниченного времени) и допускать привязку. +Это зависит от реализации пары методов обратного вызова: {@link +android.app.Service#onStartCommand onStartCommand()} позволяет компонентам запускать службу, а {@link +android.app.Service#onBind onBind()} позволяет выполнять привязку.</p> + +<p>Независимо от состояния приложения (запущенное, привязанное или и оба сразу) любой компонент приложения +может использовать службу (даже из отдельного приложения) подобно тому, как любой компонент может использовать +операцию — запустив ее с помощью {@link android.content.Intent}. Однако вы можете объявить закрытую +службу в файле манифеста и заблокировать доступ к ней из других приложений. Более подробно +это обсуждается в разделе <a href="#Declaring">Объявление службы +в манифесте</a>.</p> + +<p class="caution"><strong>Внимание!</strong> Служба работает +в основном потоке ведущего процесса — служба <strong>не</strong> создает своего потока +и <strong>не</strong> выполняется в отдельном процессе (если вы не указали иное). Это означает, +что если ваша служба собирается выполнять любую работу с высокой нагрузкой ЦП или блокирующие операции (например, воспроизведение MP3 +или сетевые операции), вы должны создать в службе новый поток для выполнения этой работы. Используя +отдельный поток, вы снижаете риск возникновения ошибок «Приложение не отвечает», и +основной поток приложения может отрабатывать взаимодействие пользователя с вашими операциями.</p> + + +<h2 id="Basics">Основы</h2> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h3>Что лучше использовать — службу или поток?</h3> + <p>Служба — это просто компонент, который может выполняться в фоновом режиме, даже когда пользователь +не взаимодействует с приложением. Следовательно, вы должны создавать службу только в том случае, если вам нужно +именно это.</p> + <p>Если вам требуется выполнить работу за пределами основного потока, но только в то время, когда пользователь взаимодействует +с приложением, то вам, вероятно, следует создать новый поток, а не службу. Например, +если вы хотите воспроизводить определенную музыку, но только во время работы операции, вы можете создать +поток в {@link android.app.Activity#onCreate onCreate()}, запустить его выполнение в методе {@link +android.app.Activity#onStart onStart()}, а затем остановить его в методе {@link android.app.Activity#onStop +onStop()}. Также рассмотрите возможность использования класса {@link android.os.AsyncTask} или {@link android.os.HandlerThread} +вместо обычного класса {@link java.lang.Thread}. В документе <a href="{@docRoot}guide/components/processes-and-threads.html#Threads">Процессы +и потоки</a> содержится дополнительная информация об этих потоках.</p> + <p>Помните, что если вы действительно используете службу, она выполняется в основном потоке вашего приложения по умолчанию, +поэтому вы должны создать новый поток в службе, если она выполняет интенсивные или +блокирующие операции.</p> +</div> +</div> + +<p>Чтобы создать службу, необходимо создать подкласс класса {@link android.app.Service} (или одного +из существующих его подклассов). В вашей реализации необходимо переопределить некоторые методы обратного вызова, +которые обрабатывают ключевые моменты жизненного цикла службы и при необходимости +предоставляют механизм привязывания компонентов. Наиболее важные методы обратного вызова, которые необходимо переопределить:</p> + +<dl> + <dt>{@link android.app.Service#onStartCommand onStartCommand()}</dt> + <dd>Система вызывает этот метод, когда другой компонент, например, операция, +запрашивает запуск этой службы, вызывая {@link android.content.Context#startService +startService()}. После выполнения этого метода служба запускается и может в течение неограниченного времени +работать в фоновом режиме. Если вы реализуете такой метод, вы обязаны остановить службу +посредством вызова {@link android.app.Service#stopSelf stopSelf()} или {@link +android.content.Context#stopService stopService()}. (Если требуется только обеспечить привязку, +реализовывать этот метод не обязательно).</dd> + <dt>{@link android.app.Service#onBind onBind()}</dt> + <dd>Система вызывает этот метод, когда другой компонент хочет выполнить привязку +к службе (например, для выполнения удаленного вызова процедуры) путем вызова {@link android.content.Context#bindService +bindService()}. В вашей реализации этого метода вы должны обеспечить интерфейс, который клиенты +используют для взаимодействия со службой, возвращая {@link android.os.IBinder}. Всегда необходимо реализовывать +этот метод, но если вы не хотите разрешать привязку, необходимо возвращать значение null.</dd> + <dt>{@link android.app.Service#onCreate()}</dt> + <dd>Система вызывает этот метод при первом создании службы для выполнения однократных +процедур настройки (перед вызовом {@link android.app.Service#onStartCommand onStartCommand()} или +{@link android.app.Service#onBind onBind()}). Если служба уже запущена, этот метод не +вызывается.</dd> + <dt>{@link android.app.Service#onDestroy()}</dt> + <dd>Система вызывает этот метод, когда служба более не используется и выполняется ее уничтожение. +Ваша служба должна реализовать это для очистки ресурсов, таких как потоки, зарегистрированные +приемники, ресиверы и т. д. Это последний вызов, который получает служба.</dd> +</dl> + +<p>Если компонент запускает службу посредством вызова {@link +android.content.Context#startService startService()} (что приводит к вызову {@link +android.app.Service#onStartCommand onStartCommand()}), то служба +продолжает работу, пока она не остановится самостоятельно с помощью {@link android.app.Service#stopSelf()} или другой +компонент не остановит ее посредством вызова {@link android.content.Context#stopService stopService()}.</p> + +<p>Если компонент вызывает +{@link android.content.Context#bindService bindService()} для создания службы (и {@link +android.app.Service#onStartCommand onStartCommand()} <em>не</em> вызывается), то служба работает, пока +к ней привязан компонент. Как только выполняется отмена привязки службы ко всем клиентам, +система уничтожает службу.</p> + +<p>Система Android будет принудительно останавливать службу только в том случае, когда не хватает памяти, и необходимо восстановить системные +для операции, которая отображается на переднем плане. Если служба привязана к операции, которая отображается на переднем плане, +менее вероятно, что она будет уничтожена, и если служба объявлена для <a href="#Foreground">выполнения в фоновом режиме</a> (как обсуждалось выше), она почти никогда не будет уничтожаться. +В противном случае, если служба была запущена и является длительной, система со временем будет опускать ее положение в списке +фоновых задач, и служба станет очень чувствительной к +уничтожению — если ваша служба запущена, вы должны предусмотреть изящную обработку ее перезапуска +системой. Если система уничтожает вашу службу, она перезапускает ее, как только снова появляется +доступ к ресурсам (хотя это также зависит от значения, возвращаемого методом {@link +android.app.Service#onStartCommand onStartCommand()}, как обсуждается ниже). Дополнительная информация +о ситуациях, в которых система может уничтожить службу приведена в документе <a href="{@docRoot}guide/components/processes-and-threads.html">Процессы и потоки</a> +.</p> + +<p>В следующих разделах описаны способы создания служб каждого типа и использования +их из других компонентов приложения.</p> + + + +<h3 id="Declaring">Объявление службы в манифесте</h3> + +<p>Все службы, как и операции (и другие компоненты), должны быть объявлены в файле +манифеста вашего приложения.</p> + +<p>Чтобы объявить службу, добавьте элемент <a href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> +, в качестве дочернегоэлемента <a href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a> +. Например:</p> + +<pre> +<manifest ... > + ... + <application ... > + <service android:name=".ExampleService" /> + ... + </application> +</manifest> +</pre> + +<p>Дополнительные сведения об объявлении службы +в манифесте см. в справке по элементу <a href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a>.</p> + +<p>Имеются другие атрибуты, которые можно включить в элемент <a href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> для +задания свойств, например, необходимых для запуска разрешений, и процесса, +в котором должна выполняться служба. Атрибут <a href="{@docRoot}guide/topics/manifest/service-element.html#nm">{@code android:name}</a> +является единственным обязательным атрибутом — он указывает имя класса для службы. После +публикации вашего приложения вам не следует менять это имя, поскольку это может разрушить +код из-за зависимости от явных намерений, используемых, чтобы запустить или привязать службу (ознакомьтесь с публикацией <a href="http://android-developers.blogspot.com/2011/06/things-that-cannot-change.html">Вещи, которые +нельзя менять</a> в блоге разработчиков). + +<p>Для обеспечения безопасности приложения <strong>всегда используйте явное намерение при запуске +или привязке {@link android.app.Service}</strong> и не объявляйте фильтров намерений для службы. Если вам +важно допустить некоторую неопределенность в отношении того, какая служба запускается, вы можете +предоставить фильтры намерений для ваших служб и исключить имя компонента из {@link +android.content.Intent}, но затем вы должны установить пакет для намерения с помощью {@link +android.content.Intent#setPackage setPackage()}, который обеспечивает достаточное устранение неоднозначности +для целевой службы.</p> + +<p>Дополнительно можно обеспечить доступность вашей службы только для вашего приложения, +включив атрибут <a href="{@docRoot}guide/topics/manifest/service-element.html#exported">{@code android:exported}</a> +и установив для него значение {@code "false"}. Это не позволяет другим приложениям запускать +вашу службу даже при использовании явного намерения.</p> + + + + +<h2 id="CreatingStartedService">Создание запущенной службы</h2> + +<p>Запущенная служба — это служба, которую запускает другой компонент вызовом {@link +android.content.Context#startService startService()}, что приводит к вызову +метода {@link android.app.Service#onStartCommand onStartCommand()} службы.</p> + +<p>При запуске служба обладает сроком жизни, не зависящим от запустившего ее компонента, +и может работать в фоновом режиме в течение неограниченного времени, +даже если уничтожен компонент, который ее запустил. Поэтому после выполнения своей работы служба должна остановиться самостоятельно +посредством вызова метода {@link android.app.Service#stopSelf stopSelf()}, либо ее может остановить другой компонент +посредством вызова метода{@link android.content.Context#stopService stopService()}.</p> + +<p>Компонент приложения, например, операция, может запустить службу, вызвав метод {@link +android.content.Context#startService startService()} и передав объект {@link android.content.Intent}, +который указывает службу и любые данные, которые служба должна использовать. Служба получает +этот объект {@link android.content.Intent} в методе {@link android.app.Service#onStartCommand +onStartCommand()}.</p> + +<p>Предположим, что операции требуется сохранить некоторые данные в сетевой базе данных. Операция может +запустить службу и предоставить ей данные для сохранения, передав намерение в метод {@link +android.content.Context#startService startService()}. Служба получает намерение в методе {@link +android.app.Service#onStartCommand onStartCommand()}, подключается к Интернету и выполняет транзакцию +с базой данных. Когда транзакция выполнена, служба останавливается +самостоятельно и уничтожается.</p> + +<p class="caution"><strong>Внимание!</strong> По умолчанию службы работают в том же процессе, что и приложение, +в котором они объявлены, а также в основном потоке этого приложения. Поэтому, если ваша служба +выполняет интенсивные или блокирующие операции, в то время как пользователь взаимодействует с операцией из того же +приложения, служба будет замедлять выполнение операции. Чтобы избежать негативного воздействия на скорость работы +приложения, вы должны запустить новый поток внутри службы.</p> + +<p>Традиционно имеется два класса, которые вы можете наследовать для создания запущенной службы:</p> +<dl> + <dt>{@link android.app.Service}</dt> + <dd>Это базовый класс для всех служб. Когда вы наследуете этот класс, важно +создать новый поток, в котором будет выполняться вся работа службы, поскольку по умолчанию служба использует основной поток вашего +приложения, что может замедлить любую операцию, которую +выполняет ваше приложение.</dd> + <dt>{@link android.app.IntentService}</dt> + <dd>Это подкласс класса {@link android.app.Service}, который использует рабочий поток для обработки всех +запросов запуска поочередно. Это оптимальный вариант, если вам не требуется, чтобы ваша служба +обрабатывала несколько запросов одновременно. Достаточно реализовать метод {@link +android.app.IntentService#onHandleIntent onHandleIntent()}, который получает намерение для каждого +запроса запуска, позволяя выполнять фоновую работу.</dd> +</dl> + +<p>В следующих разделах описано, как реализовать службу с помощью любого их этих +классов.</p> + + +<h3 id="ExtendingIntentService">Наследование класса IntentService</h3> + +<p>Так как большинству запущенных приложений не требуется обрабатывать несколько запросов одновременно, +(что может быть действительно опасным сценарием), вероятно будет лучше, если вы +реализуете свою службу с помощью класса {@link android.app.IntentService}.</p> + +<p>Класс {@link android.app.IntentService} делает следующее:</p> + +<ul> + <li>Создает рабочий поток по умолчанию, который выполняет все намерения, доставленные в метод {@link +android.app.Service#onStartCommand onStartCommand()}, отдельно от основного потока +вашего приложения.</li> + <li>Создает рабочую очередь, которая передает намерения по одному в вашу реализацию метода {@link +android.app.IntentService#onHandleIntent onHandleIntent()}, поэтому вы не должны беспокоиться +относительно многопоточности.</li> + <li>Останавливает службу после обработки всех запросов запуска, поэтому вам никогда не требуется вызывать +{@link android.app.Service#stopSelf}.</li> + <li>Предоставляет реализацию метода {@link android.app.IntentService#onBind onBind()} по умолчанию, которая +возвращает null.</li> + <li>Предоставляет реализацию метода {@link android.app.IntentService#onStartCommand +onStartCommand()} по умолчанию, которая отправляет намерение в рабочую очередь и затем в вашу реализацию {@link +android.app.IntentService#onHandleIntent onHandleIntent()}.</li> +</ul> + +<p>Все это означает, что вам достаточно реализовать метод {@link +android.app.IntentService#onHandleIntent onHandleIntent()} для выполнения работы, предоставленной +клиентом. (Хотя, кроме того, вы должны предоставить маленький конструктор для службы).</p> + +<p>Здесь приведен пример реализации класса {@link android.app.IntentService}:</p> + +<pre> +public class HelloIntentService extends IntentService { + + /** + * A constructor is required, and must call the super {@link android.app.IntentService#IntentService} + * constructor with a name for the worker thread. + */ + public HelloIntentService() { + super("HelloIntentService"); + } + + /** + * The IntentService calls this method from the default worker thread with + * the intent that started the service. When this method returns, IntentService + * stops the service, as appropriate. + */ + @Override + protected void onHandleIntent(Intent intent) { + // Normally we would do some work here, like download a file. + // For our sample, we just sleep for 5 seconds. + long endTime = System.currentTimeMillis() + 5*1000; + while (System.currentTimeMillis() < endTime) { + synchronized (this) { + try { + wait(endTime - System.currentTimeMillis()); + } catch (Exception e) { + } + } + } + } +} +</pre> + +<p>Это все, что нужно: конструктор и реализация класса {@link +android.app.IntentService#onHandleIntent onHandleIntent()}.</p> + +<p>Если вы решили переопределить также и другие методы обратного вызова, такие как {@link +android.app.IntentService#onCreate onCreate()}, {@link +android.app.IntentService#onStartCommand onStartCommand()} или {@link +android.app.IntentService#onDestroy onDestroy()}, обязательно вызовите реализацию суперкласса, +чтобы класс {@link android.app.IntentService} мог правильно обрабатывать жизненный цикл рабочего потока.</p> + +<p>Например, метод {@link android.app.IntentService#onStartCommand onStartCommand()} должен возвращать +реализацию по умолчанию (которая доставляет намерение в {@link +android.app.IntentService#onHandleIntent onHandleIntent()}):</p> + +<pre> +@Override +public int onStartCommand(Intent intent, int flags, int startId) { + Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); + return super.onStartCommand(intent,flags,startId); +} +</pre> + +<p>Помимо {@link android.app.IntentService#onHandleIntent onHandleIntent()}, единственный метод, +из которого вам не требуется вызывать суперкласс, это метод {@link android.app.IntentService#onBind +onBind()} (но его нужно реализовывать только в случае, если ваша служба допускает привязку).</p> + +<p>В следующем разделе вы увидите, как реализовывается служба такого же типа при наследовании +базового класса {@link android.app.Service}, которая содержит намного больше кода, но которая может +подойти, если вам требуется обрабатывать одновременные запросы запуска.</p> + + +<h3 id="ExtendingService">Наследование класса Service</h3> + +<p>Как вы видели в предыдущем разделе, использование класса {@link android.app.IntentService} значительно упрощает +реализацию запущенной службы. Однако, если необходимо, чтобы ваша служба +поддерживала многопоточность (вместо обработки запросов запуска через рабочую очередь), можно +наследовать класс {@link android.app.Service} для обработки каждого намерения.</p> + +<p>В качестве примера приведена следующая реализация класса {@link +android.app.Service}, которая выполняет ту же работу, как и пример выше, использующий класс {@link +android.app.IntentService}. То есть для каждого запроса запуска он использует рабочий поток для выполнения +задания и обрабатывает запросы по одному.</p> + +<pre> +public class HelloService extends Service { + private Looper mServiceLooper; + private ServiceHandler mServiceHandler; + + // Handler that receives messages from the thread + private final class ServiceHandler extends Handler { + public ServiceHandler(Looper looper) { + super(looper); + } + @Override + public void handleMessage(Message msg) { + // Normally we would do some work here, like download a file. + // For our sample, we just sleep for 5 seconds. + long endTime = System.currentTimeMillis() + 5*1000; + while (System.currentTimeMillis() < endTime) { + synchronized (this) { + try { + wait(endTime - System.currentTimeMillis()); + } catch (Exception e) { + } + } + } + // Stop the service using the startId, so that we don't stop + // the service in the middle of handling another job + stopSelf(msg.arg1); + } + } + + @Override + public void onCreate() { + // Start up the thread running the service. Note that we create a + // separate thread because the service normally runs in the process's + // main thread, which we don't want to block. We also make it + // background priority so CPU-intensive work will not disrupt our UI. + HandlerThread thread = new HandlerThread("ServiceStartArguments", + Process.THREAD_PRIORITY_BACKGROUND); + thread.start(); + + // Get the HandlerThread's Looper and use it for our Handler + mServiceLooper = thread.getLooper(); + mServiceHandler = new ServiceHandler(mServiceLooper); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); + + // For each start request, send a message to start a job and deliver the + // start ID so we know which request we're stopping when we finish the job + Message msg = mServiceHandler.obtainMessage(); + msg.arg1 = startId; + mServiceHandler.sendMessage(msg); + + // If we get killed, after returning from here, restart + return START_STICKY; + } + + @Override + public IBinder onBind(Intent intent) { + // We don't provide binding, so return null + return null; + } + + @Override + public void onDestroy() { + Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); + } +} +</pre> + +<p>Как можно видеть, этот код значительно длиннее, чем код с использованием класса {@link android.app.IntentService}.</p> + +<p>Однако, так как вы обрабатываете каждый вызов {@link android.app.Service#onStartCommand +onStartCommand()} самостоятельно, вы можете выполнять несколько запросов одновременно. Данный код +выполняет не совсем эту работу, но при необходимости вы можете создавать новые потоки для каждого +запроса и сразу запускать их (а не ожидать завершения предыдущего запроса).</p> + +<p>Обратите внимание, что метод {@link android.app.Service#onStartCommand onStartCommand()} должен +возвращать целое число. Это целое число описывает, как система должна продолжать выполнение службы в случае, +когда система уничтожила ее (как описано выше, реализация по умолчанию для класса {@link +android.app.IntentService} обрабатывает эту ситуацию, хотя вы изменить ход реализации). Значение, +возвращаемое методом {@link android.app.Service#onStartCommand onStartCommand()}, должно быть одной из следующих +констант:</p> + +<dl> + <dt>{@link android.app.Service#START_NOT_STICKY}</dt> + <dd>Если система уничтожает службу после возвращения из {@link android.app.Service#onStartCommand +onStartCommand()}, <em>не нужно</em> повторно создавать службу, если нет ожидающих +доставки намерений. Это самый безопасный вариант, позволяющий избежать запуска вашей службы, когда это не нужно +и когда ваше приложение может просто перезапустить любые незавершенные задания.</dd> + <dt>{@link android.app.Service#START_STICKY}</dt> + <dd>Если система уничтожает службу после возвращения из {@link android.app.Service#onStartCommand +onStartCommand()}, повторно создайте службу и вызовите {@link +android.app.Service#onStartCommand onStartCommand()}, но <em>не</em> передавайте последнее намерение повторно. +Вместо этого система вызывает метод {@link android.app.Service#onStartCommand onStartCommand()} с намерением, +которое имеет значение null, если нет ожидающих намерений для запуска службы. Если ожидающие намерения есть, +они доставляются. Это подходит для мультимедийных проигрывателей (или подобных служб), которые не +выполняют команды, а работают независимо и ожидают задание.</dd> + <dt>{@link android.app.Service#START_REDELIVER_INTENT}</dt> + <dd>Если система уничтожает службу после возвращения из {@link android.app.Service#onStartCommand +onStartCommand()}, повторно создайте службу и вызовите {@link +android.app.Service#onStartCommand onStartCommand()} с последним намерением, которое было доставлено +в службу. Все ожидающие намерения доставляются по очереди. Это подходит для служб, +активно выполняющих задание, которое должно быть возобновлено немедленно, например, для загрузок файла.</dd> +</dl> +<p>Для получения дополнительных сведений об этих возвращаемых значениях см. справочную документацию по ссылке для каждой +константы.</p> + + + +<h3 id="StartingAService">Запуск службы</h3> + +<p>Можно запустить службу из операции или другого компонента приложения, передав объект +{@link android.content.Intent} (указывающий службу, которую требуется запустить) в {@link +android.content.Context#startService startService()}. Система Android вызывает метод {@link +android.app.Service#onStartCommand onStartCommand()} службы и передает ей {@link +android.content.Intent}. (Ни в коем случае не следует вызывать метод {@link android.app.Service#onStartCommand +onStartCommand()} напрямую).</p> + +<p>Например, операция может запустить службу из примера в предыдущем разделе ({@code +HelloSevice}), используя явное намерение с помощью {@link android.content.Context#startService +startService()}:</p> + +<pre> +Intent intent = new Intent(this, HelloService.class); +startService(intent); +</pre> + +<p>Метод {@link android.content.Context#startService startService()} возвращается немедленно, и система +Android вызывает метод службы {@link android.app.Service#onStartCommand +onStartCommand()}. Если служба еще не выполняется, система сначала вызывает {@link +android.app.Service#onCreate onCreate()}, а затем {@link android.app.Service#onStartCommand +onStartCommand()}.</p> + +<p>Если служба также не представляет привязку, намерение, доставляемое с помощью {@link +android.content.Context#startService startService()}, является единственным режимом связи между +компонентом приложения и службой. Однако, если вы хотите, чтобы служба оправляла результат обратно, +клиент, который запускает службу, может создать объект {@link android.app.PendingIntent} для сообщения +(с помощью {@link android.app.PendingIntent#getBroadcast getBroadcast()}) и доставить его в службу +в объекте {@link android.content.Intent}, который запускает службу. Затем служба может использовать +сообщение для доставки результата.</p> + +<p>Несколько запросов запуска службы приводят к нескольким соответствующим вызовам метода +{@link android.app.Service#onStartCommand onStartCommand()} службы. Однако для ее остановки достаточно только одного запроса на остановку +службы (с помощью {@link android.app.Service#stopSelf stopSelf()} или {@link +android.content.Context#stopService stopService()}).</p> + + +<h3 id="Stopping">Остановка службы</h3> + +<p>Запущенная служба должна управлять своим жизненным циклом. То есть, система не останавливает и не +уничтожает службу, если не требуется восстановить память системы, и служба +продолжает работу после возвращения из метода {@link android.app.Service#onStartCommand onStartCommand()}. Поэтому +служба должна останавливаться самостоятельно посредством вызова метода {@link android.app.Service#stopSelf stopSelf()}, либо другой +компонент может остановить ее посредством вызова метода {@link android.content.Context#stopService stopService()}.</p> + +<p>Получив запрос на остановку посредством {@link android.app.Service#stopSelf stopSelf()} или {@link +android.content.Context#stopService stopService()}, система как можно скорее уничтожает службу +.</p> + +<p>Однако, если служба обрабатывает несколько запросов {@link +android.app.Service#onStartCommand onStartCommand()} одновременно, вы не должны останавливать службу +после завершения обработки запроса запуска, поскольку вы, вероятно, уже получили новый +запрос запуска (остановка в конце первого запроса привела бы к прерыванию второго). Чтобы избежать +этой проблемы, вы можете использовать метод {@link android.app.Service#stopSelf(int)}, гарантирующий, что ваш запрос на +остановку службы всегда основан на самом последнем запросе запуска. То есть, когда вы вызываете {@link +android.app.Service#stopSelf(int)}, вы передаете идентификатор запроса запуска (идентификатор <code>startId</code>, +доставленный в {@link android.app.Service#onStartCommand onStartCommand()}), которому соответствует ваш +запрос остановки. Тогда, если служба получит новый запрос запуска до того, как вы сможете вызвать {@link +android.app.Service#stopSelf(int)}, идентификатор не будет совпадать и служба не будет остановлена.</p> + +<p class="caution"><strong>Внимание!</strong> Ваше приложение обязательно должно останавливать свои службы по окончании работы, +чтобы избежать расходования ресурсов системы и потребления энергии аккумулятора. При необходимости +другие компоненты могут остановить службу посредством вызова метода {@link +android.content.Context#stopService stopService()}. Даже если вы можете выполнять привязку службы, +следует всегда останавливать службу самостоятельно, если она когда-либо получила вызов {@link +android.app.Service#onStartCommand onStartCommand()}.</p> + +<p>Дополнительные сведения о жизненном цикле службы представлены в разделе <a href="#Lifecycle">Управление жизненным циклом службы</a> ниже.</p> + + + +<h2 id="CreatingBoundService">Создание привязанной службы</h2> + +<p>Привязанная служба — это служба, которая допускает привязку к ней компонентов приложения посредством вызова {@link +android.content.Context#bindService bindService()} для создания долговременного соединения +(и обычно не позволяет компонентам <em>запускать</em> ее посредством вызова {@link +android.content.Context#startService startService()}).</p> + +<p>Вы должны создать привязанную службу, когда вы хотите взаимодействовать со службой из операций +и других компонентов вашего приложения или показывать некоторые функции вашего приложения +другим приложениям посредством межпроцессного взаимодействия (IPC).</p> + +<p>Чтобы создать привязанную службу, необходимо реализовать метод обратного вызова {@link +android.app.Service#onBind onBind()} для возвращения объекта {@link android.os.IBinder}, +который определяет интерфейс взаимодействия со службой. После этого другие компоненты приложения могут вызвать +метод {@link android.content.Context#bindService bindService()} для извлечения интерфейса и +начать вызывать методы службы. Служба существует только для обслуживания привязанного к ней компонента приложения, +поэтому, когда нет компонентов, привязанных к службе, система уничтожает ее +(вам <em>не</em> требуется останавливать привязанную службу, как это требуется для службы, запущенной +посредством {@link android.app.Service#onStartCommand onStartCommand()}).</p> + +<p>Чтобы создать привязанную службу, необходимо в первую очередь определить интерфейс, взаимодействия +клиента со службой. Этот интерфейс между службой +и клиентом должен быть реализацией объекта {@link android.os.IBinder}, которую ваша служба должна +возвращать из метода обратного вызова {@link android.app.Service#onBind +onBind()}. После того, как клиент получает объект {@link android.os.IBinder}, он может начать +взаимодействие со службой посредством этого интерфейса.</p> + +<p>Одновременно к службе могут быть привязаны несколько клиентов. Когда клиент заканчивает взаимодействие +со службой, он вызывает {@link android.content.Context#unbindService unbindService()} для отмены привязки. Как только +не остается ни одного клиента, привязанного к службе, система уничтожает службу.</p> + +<p>Существует несколько способов реализации привязанной службы, и эти реализации сложнее, +чем реализации запущенной службы, поэтому обсуждение привязанной службы приведено в отдельном +документе <a href="{@docRoot}guide/components/bound-services.html">Привязанные службы</a>.</p> + + + +<h2 id="Notifications">Отправка уведомлений пользователю</h2> + +<p>После запуска служба может уведомлять пользователя о событиях, используя <a href="{@docRoot}guide/topics/ui/notifiers/toasts.html">Всплывающие уведомления</a> или <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Уведомления в строке состояния</a>.</p> + +<p>Всплывающее уведомление — это сообщение, кратковременно появляющееся на поверхности текущего окна, +тогда как уведомление в строке состояния — это значок в строке состояния с сообщением, +который пользователь может выбрать, чтобы выполнить действие (такое как запуск операции).</p> + +<p>Обычно уведомление в строке состояния является самым удобным решением, когда завершается какая-то фоновая работа +(например, завершена +загрузка файла), и пользователь может действовать. Когда пользователь выбирает уведомление в +расширенном виде, уведомление может запустить операцию (например, для просмотра загруженного файла).</p> + +<p>Дополнительную информацию см. в руководствах для разработчиков <a href="{@docRoot}guide/topics/ui/notifiers/toasts.html">Всплывающие уведомления</a> и<a href="{@docRoot}guide/topics/ui/notifiers/notifications.html"> +Уведомления в строке состояния</a>.</p> + + + +<h2 id="Foreground">Запуск службы на переднем плане</h2> + +<p>Служба переднего плана — это служба, о которой пользователь активно +осведомлен, и поэтому она не является кандидатом для удаления системой в случае нехватки памяти. Служба +переднего плана должна выводить уведомление в строку состояния, которая находится под заголовком +«Постоянные». Это означает, что уведомление не может быть удалено, пока служба +не будет остановлена или удалена с переднего плана.</p> + +<p>Например, музыкальный проигрыватель, который воспроизводит музыку из службы, должен быть настроен на работу +на переднем плане, так как пользователь точно знает о +его работе. Уведомление в строке состояния может показывать текущее произведение и позволять пользователю +запускать операцию для взаимодействия с музыкальным проигрывателем.</p> + +<p>Для запроса на выполнение вашей службы на переднем плане вызовите метод {@link +android.app.Service#startForeground startForeground()}. Этот метод имеет два параметра: целое число, +которое однозначно идентифицирует уведомление и объект {@link +android.app.Notification} для строки состояния. Например:</p> + +<pre> +Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text), + System.currentTimeMillis()); +Intent notificationIntent = new Intent(this, ExampleActivity.class); +PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); +notification.setLatestEventInfo(this, getText(R.string.notification_title), + getText(R.string.notification_message), pendingIntent); +startForeground(ONGOING_NOTIFICATION_ID, notification); +</pre> + +<p class="caution"><strong>Внимание!</strong> Целочисленный идентификатор ID, который вы передаете в метод {@link +android.app.Service#startForeground startForeground()}, не должен быть равен 0.</p> + + +<p>Чтобы удалить службу с переднего плана, вызовите {@link +android.app.Service#stopForeground stopForeground()}. Этот метод содержит логическое значение, указывающее, +следует ли также удалять уведомление в строке состояния. Этот метод <em>не</em> останавливает +службу. Однако, если вы останавливаете службу, работающую на переднем плане, +уведомление также удаляется.</p> + +<p>Дополнительную информацию об уведомлениях см. в разделе <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Создание уведомлений +в строке состояния</a>.</p> + + + +<h2 id="Lifecycle">Управление жизненным циклом службы</h2> + +<p>Жизненный цикл службы намного проще, чем жизненный цикл операции. Однако, намного важнее +уделить пристальное внимание тому, как ваша служба создается и уничтожается, так как служба +может работать в фоновом режиме без ведома пользователя.</p> + +<p>Жизненный цикл службы от создания до уничтожения может следовать двум +разным путям:</p> + +<ul> +<li>Запущенная служба + <p>Служба создается, когда другой компонент вызывает метод {@link +android.content.Context#startService startService()}. Затем служба работает в течение неограниченного времени и должна +остановиться самостоятельно посредством вызова метода {@link +android.app.Service#stopSelf() stopSelf()}. Другой компонент также может остановить службу +посредством вызова метода {@link android.content.Context#stopService +stopService()}. Когда служба останавливается, система уничтожает ее.</p></li> + +<li>Привязанная служба + <p>Служба создается, когда другой компонент (клиент) вызывает метод {@link +android.content.Context#bindService bindService()}. Затем клиент взаимодействует со службой +через интерфейс {@link android.os.IBinder}. Клиент может закрыть соединение посредством вызова +метода {@link android.content.Context#unbindService unbindService()}. К одной службе могут быть привязано +несколько клиентов, и когда все они отменяют привязку, система уничтожает службу. (Служба +<em>не</em> должна останавливаться самостоятельно.)</p></li> +</ul> + +<p>Эти два способа необязательно работают независимо друг от друга. То есть вы можете привязать службу, которая уже была +запущена посредством метода {@link android.content.Context#startService startService()}. Например, фоновая +музыкальная служба может быть запущена посредством вызова метода {@link android.content.Context#startService +startService()} с объектом {@link android.content.Intent}, который идентифицирует музыку для воспроизведения. Позже, +например, когда пользователь хочет получить доступ к управлению проигрывателем или информацию о текущем произведении, +операция может установить привязку к службе посредством вызова метода {@link +android.content.Context#bindService bindService()}. В подобных случаях методы {@link +android.content.Context#stopService stopService()} и {@link android.app.Service#stopSelf +stopSelf()} фактически не останавливают службу, пока не будет отменена привязка всех клиентов. </p> + + +<h3 id="LifecycleCallbacks">Реализация обратных вызовов жизненного цикла</h3> + +<p>Подобно операции, служба содержит методы обратного вызова жизненного цикла, которые можно реализовать для контроля +изменений состояния службы и выполнения работы в соответствующие моменты времени. Указанная ниже базовая +служба показывает каждый из методов жизненного цикла.</p> + +<pre> +public class ExampleService extends Service { + int mStartMode; // indicates how to behave if the service is killed + IBinder mBinder; // interface for clients that bind + boolean mAllowRebind; // indicates whether onRebind should be used + + @Override + public void {@link android.app.Service#onCreate onCreate}() { + // The service is being created + } + @Override + public int {@link android.app.Service#onStartCommand onStartCommand}(Intent intent, int flags, int startId) { + // The service is starting, due to a call to {@link android.content.Context#startService startService()} + return <em>mStartMode</em>; + } + @Override + public IBinder {@link android.app.Service#onBind onBind}(Intent intent) { + // A client is binding to the service with {@link android.content.Context#bindService bindService()} + return <em>mBinder</em>; + } + @Override + public boolean {@link android.app.Service#onUnbind onUnbind}(Intent intent) { + // All clients have unbound with {@link android.content.Context#unbindService unbindService()} + return <em>mAllowRebind</em>; + } + @Override + public void {@link android.app.Service#onRebind onRebind}(Intent intent) { + // A client is binding to the service with {@link android.content.Context#bindService bindService()}, + // after onUnbind() has already been called + } + @Override + public void {@link android.app.Service#onDestroy onDestroy}() { + // The service is no longer used and is being destroyed + } +} +</pre> + +<p class="note"><strong>Примечание.</strong> В отличие от методов обратного вызова жизненного цикла операции, вам +<em>не</em> требуется вызывать реализацию суперкласса этих методов обратного вызова.</p> + +<img src="{@docRoot}images/service_lifecycle.png" alt="" /> +<p class="img-caption"><strong>Рисунок 2.</strong> Жизненный цикл службы. На схеме слева +показан жизненный цикл, когда служба создана посредством метода {@link android.content.Context#startService +startService()}, а на схеме справа показан жизненный цикл, когда служба создана +посредством метода {@link android.content.Context#bindService bindService()}.</p> + +<p>С помощью реализации этих методов можно отслеживать два вложенных цикла в жизненном цикле службы: </p> + +<ul> +<li><strong>Весь жизненный цикл</strong> службы происходит между вызовом метода {@link +android.app.Service#onCreate onCreate()} и возвратом из метода {@link +android.app.Service#onDestroy}. Подобно операции, служба выполняет начальную настройку в методе +{@link android.app.Service#onCreate onCreate()} и освобождает все оставшиеся ресурсы в методе {@link +android.app.Service#onDestroy onDestroy()}. Например, +служба воспроизведения музыки может создать поток для воспроизведения музыки в методе {@link +android.app.Service#onCreate onCreate()}, затем остановить поток в методе {@link +android.app.Service#onDestroy onDestroy()}. + +<p>Методы {@link android.app.Service#onCreate onCreate()} и {@link android.app.Service#onDestroy +onDestroy()} вызываются для всех служб, независимо от метода создания: +{@link android.content.Context#startService startService()} или {@link +android.content.Context#bindService bindService()}.</p></li> + +<li><strong>Активный жизненный цикл</strong> службы начинается с вызова метода {@link +android.app.Service#onStartCommand onStartCommand()} или {@link android.app.Service#onBind onBind()}. +Каждый метод направляется намерением {@link +android.content.Intent}, которое было передано методу {@link android.content.Context#startService +startService()} или {@link android.content.Context#bindService bindService()}, соответственно. +<p>Если служба запущена, активный жизненный цикл заканчивается одновременно с окончанием +всего жизненного цикла (служба активна даже после возврата из метода {@link android.app.Service#onStartCommand +onStartCommand()}). Если служба является привязанной, активный жизненный цикл заканчивается, когда возвращается метод {@link +android.app.Service#onUnbind onUnbind()}.</p> +</li> +</ul> + +<p class="note"><strong>Примечание.</strong> Хотя запущенная служба останавливается посредством вызова +метода {@link android.app.Service#stopSelf stopSelf()} или {@link +android.content.Context#stopService stopService()}, для службы не существует соответствующего обратного вызова +(нет обратного вызова {@code onStop()}). Поэтому, если служба не привязана к клиенту, +система уничтожает ее при остановке службы — метод {@link +android.app.Service#onDestroy onDestroy()} является единственным получаемым методом обратного вызова.</p> + +<p>Рисунок 2 иллюстрирует типичные методы обратного вызова для службы. Хотя на рисунке отделены +службы, созданные посредством метода {@link android.content.Context#startService startService()}, от служб, +созданных посредством метода {@link android.content.Context#bindService bindService()}, помните, +что любая служба, независимо от способа запуска, позволяет клиентам выполнять привязку к ней. +Поэтому служба, изначально созданная посредством метода {@link android.app.Service#onStartCommand +onStartCommand()} (клиентом, который вызвал {@link android.content.Context#startService startService()}), +может получать вызов метода {@link android.app.Service#onBind onBind()} (когда клиент вызывает +метод {@link android.content.Context#bindService bindService()}).</p> + +<p>Дополнительные сведения о создании службы, которая обеспечивает привязку, см. в документе <a href="{@docRoot}guide/components/bound-services.html">Привязанные службы</a>, +который содержит дополнительную информацию о методе обратного вызова {@link android.app.Service#onRebind onRebind()} +в разделе <a href="{@docRoot}guide/components/bound-services.html#Lifecycle">Управление жизненным циклом +привязанной службы</a>.</p> + + +<!-- +<h2>Beginner's Path</h2> + +<p>To learn how to query data from the system or other applications (such as contacts or media +stored on the device), continue with the <b><a +href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a></b> +document.</p> +--> diff --git a/docs/html-intl/intl/ru/guide/components/tasks-and-back-stack.jd b/docs/html-intl/intl/ru/guide/components/tasks-and-back-stack.jd new file mode 100644 index 000000000000..c9fdc0e069bb --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/tasks-and-back-stack.jd @@ -0,0 +1,578 @@ +page.title=Задачи и стек переходов назад +parent.title=Операции +parent.link=activities.html +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + +<h2>Содержание документа</h2> +<ol> +<li><a href="#ActivityState">Сохранение состояния операции</a></li></li> +<li><a href="#ManagingTasks">Управление задачами</a> + <ol> + <li><a href="#TaskLaunchModes">Определение режимов запуска</a></li> + <li><a href="#Affinities">Обработка привязок</a></li> + <li><a href="#Clearing">Очистка стека переходов назад</a></li> + <li><a href="#Starting">Запуск задачи</a></li> + </ol> +</li> +</ol> + +<h2>Статьи</h2> +<ol> + <li><a href="http://android-developers.blogspot.com/2010/04/multitasking-android-way.html"> +Многозадачность в системе Android</a></li> +</ol> + +<h2>См. также:</h2> +<ol> + <li><a href="{@docRoot}design/patterns/navigation.html">Дизайн Android: +навигация</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html">Элемент манифеста +{@code <activity>}</a></li> + <li><a href="{@docRoot}guide/components/recents.html">Экран обзора</a></li> +</ol> +</div> +</div> + + +<p>Обычно приложение содержит несколько <a href="{@docRoot}guide/components/activities.html">операций</a>. Каждая операция +должна разрабатываться в связи с действием определенного типа, которое пользователь может выполнять, и может запускать другие +операции. Например, приложение электронной почты может содержать одну операцию для отображения списка новых сообщений. +Когда пользователь выбирает сообщение, открывается новая операция для просмотра этого сообщения.</p> + +<p>Операция может даже запускать операции, существующие в других приложениях на устройстве. Например, +если ваше приложение хочет отправить сообщение электронной почты, вы можете определить намерение для выполнения +действия «отправить» и включить в него некоторые данные, например, адрес электронной почты и текст сообщения. После этого открывается операция из другого +приложения, которая объявила, что она обрабатывает намерения такого типа. В этом случае намерение состоит в том, чтобы +отправить сообщение электронной почты, поэтому в приложении электронной почты запускается операция «составить сообщение» (если одно намерение +может обрабатываться несколькими операциями, система предлагает пользователю выбрать, какую из операций использовать). После отправки сообщения электронной почты +ваша операция возобновляет работу, и все выглядит так, будто операция отправки электронной почты является частью вашего приложения. Хотя +операции могут быть частями разных приложений, система Android поддерживает удобство работы +пользователя, сохраняя обе операции в одной <em>задаче</em>.</p> + +<p>Задача — это коллекция операций, с которыми взаимодействует пользователь +при выполнении определенного задания. Операции упорядочены в виде стека (<em>стека переходов назад</em>), в том +порядке, в котором открывались операции.</p> + +<!-- SAVE FOR WHEN THE FRAGMENT DOC IS ADDED +<div class="sidebox-wrapper"> +<div class="sidebox"> +<h3>Adding fragments to a task's back stack</h3> + +<p>Your activity can also include {@link android.app.Fragment}s to the back stack. For example, +suppose you have a two-pane layout using fragments, one of which is a list view (fragment A) and the +other being a layout to display an item from the list (fragment B). When the user selects an item +from the list, fragment B is replaced by a new fragment (fragment C). In this case, it might be +desireable for the user to navigate back to reveal fragment B, using the <em>Back</em> button.</p> +<p>In order to add fragment B to the back stack so that this is possible, you must call {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()} before you {@link +android.app.FragmentTransaction#commit()} the transaction that replaces fragment B with fragment +C.</p> +<p>For more information about using fragments and adding them to the back stack, see the {@link +android.app.Fragment} class documentation.</p> + +</div> +</div> +--> + +<p>Начальным местом для большинства задач является главный экран устройства. Когда пользователь касается значка в средстве +запуска +приложений (или ярлыка на главном экране), эта задача приложения переходит на передний план. Если для +приложения нет задач (приложение не использовалось в последнее время), тогда создается новая задача +и открывается «основная» операция для этого приложения в качестве корневой операции в стеке.</p> + +<p>Когда текущая операция запускает другую, новая операция помещается в вершину стека +и получает фокус. Предыдущая операция остается в стеке, но ее выполнение останавливается. Когда операция останавливается +, система сохраняет текущее состояние ее пользовательского интерфейса. Когда пользователь нажимает кнопку +<em>Назад</em>, +текущая операция удаляется из вершины стека (операция уничтожается) и возобновляется +работа предыдущей операции (восстанавливается предыдущее состояние ее пользовательского интерфейса). Операции в стеке +никогда не переупорядочиваются, только добавляются в стек и удаляются из него — добавляются в стек при запуске текущей операцией +и удаляются, когда пользователь выходит из нее с помощью кнопки <em>Назад</em>. По существу, +стек +переходов назад работает по принципу «последним вошел — первым вышел». На рисунке 1 это поведение +показано на временной шкале: состояние операций и текущее состояние стека переходов назад + показано в каждый момент времени.</p> + +<img src="{@docRoot}images/fundamentals/diagram_backstack.png" alt="" /> +<p class="img-caption"><strong>Рисунок 1.</strong> Иллюстрация того, как каждая новая операция в задаче +добавляет элемент в стек переходов назад. Когда пользователь нажимает кнопку<em>Назад</em>, текущая +операция +уничтожается, и возобновляется работа предыдущей операции.</p> + + +<p>Если пользователь продолжает нажимать кнопку <em>Назад</em>, операции поочередно удаляются из стека, +открывая +предыдущую операцию, пока пользователь не вернется на главный экран (или в операцию, которая была запущена +в начале выполнения задачи). Когда все операции удалены из стека, задача прекращает существование.</p> + +<div class="figure" style="width:287px"> +<img src="{@docRoot}images/fundamentals/diagram_multitasking.png" alt="" /> <p +class="img-caption"><strong>Рисунок 2.</strong> Две задачи: Задача B взаимодействует с пользователем +на переднем плане, тогда как Задача A находится в фоновом режиме, ожидая возобновления.</p> +</div> +<div class="figure" style="width:215px"> + <img src="{@docRoot}images/fundamentals/diagram_multiple_instances.png" alt="" /> <p +class="img-caption"><strong>Рисунок 3.</strong> Создается несколько экземпляров одной операции.</p> +</div> + +<p>Задача — это связанный блок, который может переходить в фоновый режим, когда пользователи начинают новую задачу или переходят +на главный экран с помощью кнопки <em>Домой</em>. В фоновом режиме все операции +задачи +остановлены, но стек обратного вызова для задачи остается неизменным. Задача просто потеряла фокус во время +выполнения другой задачи, как показано на рисунке 2. Затем задача может вернуться на передний план, так что пользователи +могут продолжить ее с прерванного места. Предположим, например, что текущая задача (Задача A) содержит три операции +в своем стеке — две операции под текущей операцией. Пользователь нажимает кнопку <em>Домой</em>, + затем запускает +новое приложение из средства запуска приложений. Когда появляется главный экран, Задача A переходит +в фоновый режим. Когда запускается новое приложение, система запускает задачу для этого приложения +(Задачу B) со своим собственным стеком операций. После взаимодействия с этим +приложением пользователь снова возвращается на главный экран и выбирает изначально запущенную +Задачу A. Теперь Задача A переходит на передний +план — все три операции ее стека остались неизменными, и возобновляется операция, находящаяся на +вершине стека. В этот +момент пользователь может также переключиться обратно на Задачу B, перейдя на главный экран и выбрав значок приложения, +которое запустило эту задачу (или выбрав задачу приложения на +<a href="{@docRoot}guide/components/recents.html">экране обзора</a>). +Это пример многозадачности в системе Android.</p> + +<p class="note"><strong>Примечание.</strong> В фоновом режиме может находиться несколько задач одновременно. +Однако, если пользователь запускает много фоновых задач одновременно, система может начать +уничтожение фоновых операций для освобождения памяти, что приведет к потере состояния задач. +См. следующий раздел <a href="#ActivityState">Состояние операции</a>.</p> + +<p>Поскольку операции в стеке никогда не переупорядочиваются, если ваше приложение позволяет +пользователям запускать определенную операцию из нескольких операций, новый экземпляр +такой операции создается и помещается в стек (вместо помещения любого из предыдущих экземпляров +операции на вершину стека). По существу, для одной операции вашего приложения может быть создано несколько +экземпляров (даже из разных задач), как показано на рисунке 3. Поэтому, если пользователь переходит назад с помощью +кнопки <em>Назад</em>, каждый экземпляр операции появляется в том порядке, в котором они +были открыты (каждый +со своим состоянием пользовательского интерфейса). Однако вы можете изменить это поведение, если вы не хотите, чтобы создавалось несколько +экземпляров операции. Это описано в разделе <a href="#ManagingTasks">Управление задачами</a> ниже.</p> + + +<p>Подведем итоги поведения операций и задач:</p> + +<ul> + <li>Когда Операция A запускает Операцию B, Операция A останавливается, но система сохраняет ее состояние +(например, положение прокрутки и текст, введенный в формы). +Если пользователь нажимает кнопку <em>Назад</em> в Операции B, Операция A возобновляет работу +из сохраненного состояния.</li> + <li>Когда пользователь выходит из задачи нажатием кнопки <em>Домой</em>, текущая операция +останавливается и +ее задача переводится в фоновый режим. Система сохраняет состояние каждой операции в задаче. Если +пользователь впоследствии возобновляет задачу, выбирая значок запуска задачи, она переводится +на передний план и возобновляет операцию на вершине стека.</li> + <li>Если пользователь нажимает кнопку <em>Назад</em>, текущая операция удаляется из стека +и +уничтожается. Возобновляется предыдущая операция в стеке. Когда операция уничтожается, система +<em>не</em> сохраняет состояние операции.</li> + <li>Можно создавать несколько экземпляров операции, даже из других задач.</li> +</ul> + + +<div class="note design"> +<p><strong>Дизайн навигации</strong></p> + <p>Для получения дополнительной информации о работе навигации в приложении Android, прочитайте раздел <a href="{@docRoot}design/patterns/navigation.html">Навигация</a> руководства «Дизайн для Android».</p> +</div> + + +<h2 id="ActivityState">Сохранение состояния операции</h2> + +<p>Как говорилось выше, система по умолчанию сохраняет состояние операции, когда она +останавливается. Таким образом, когда пользователи возвращаются обратно в предыдущую операцию, восстанавливается ее пользовательский интерфейс +в момент остановки. Однако вы можете — и <strong>должны</strong> — с упреждением сохранять +состояние ваших операций посредством методов обратного вызова на случай уничтожения операции и необходимости ее +повторного создания.</p> + +<p>Когда система останавливает одну из ваших операций (например, когда запускается новая операция или задача +перемещается в фоновый режим), система может полностью уничтожить эту операцию, если необходимо восстановить +память системы. Когда это происходит, информация о состоянии операции теряется. Если это происходит, +система +знает, что операция находится в стеке переходов назад, но когда операция переходит +на вершину стека, система должна создать ее повторно (а не возобновить ее). Чтобы избежать +потери работы пользователя, вы должны с упреждением сохранять ее путем реализации методов +обратного вызова {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} +в вашей операции.</p> + +<p>Дополнительную информацию о сохранении состояния вашей операции см. в документе +<a href="{@docRoot}guide/components/activities.html#SavingActivityState">Операции</a>.</p> + + + +<h2 id="ManagingTasks">Управление задачами</h2> + +<p>Для большинства приложений способ, которым Android управляет задачами и стеком переходов назад, описанный выше, — помещение всех +операций последовательно в одну задачу в стек «последним вошёл — первым вышел», — +работает хорошо, и вы не должны беспокоиться о связи ваших операций с задачами +или об их существовании в стеке переходов назад. Однако вы можете решить, что вы хотите прервать +обычное поведение. Возможно, вы хотите, чтобы операция в вашем приложении начинала новую задачу +при запуске (вместо помещения в текущую задачу), или при запуске операции вы хотите +перенести на передний план ее существующий экземпляр (вместо создания нового +экземпляра на вершине стека переходов назад), или вы хотите чтобы при выходе пользователя из задачи из вашего стек переходов удалялись все +операции, кроме корневой операции.</p> + +<p>Вы можете совершать эти и многие другие действия с помощью атрибутов в элементе манифеста +<a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> +и с помощью флагов в намерении, которое вы передаете в +{@link android.app.Activity#startActivity startActivity()}.</p> + +<p>В этом смысле главными атрибутами <a href="{@docRoot}guide/topics/manifest/activity-element.html"> +{@code <activity>}</a>, которые вы можете использовать, являются следующие:</p> + +<ul class="nolist"> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#aff"> + {@code taskAffinity}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode"> + {@code launchMode}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#reparent"> + {@code allowTaskReparenting}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#clear"> + {@code clearTaskOnLaunch}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#always"> + {@code alwaysRetainTaskState}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#finish"> + {@code finishOnTaskLaunch}</a></li> +</ul> + +<p>А главными флагами намерений, которые вы можете использовать, являются следующие:</p> + +<ul class="nolist"> + <li>{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}</li> + <li>{@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}</li> + <li>{@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}</li> +</ul> + +<p>В следующих разделах показано, как можно использовать эти атрибуты манифеста и флаги +намерений для определения связи операций с задачами и их поведения в стеке переходов назад.</p> + +<p>Кроме того, отдельно обсуждаются рекомендации о представлении задач и операций и управлении ими +на экране обзора. Дополнительную информацию см. в разделе +<a href="{@docRoot}guide/components/recents.html">Экран обзора</a>. Обычно следует разрешить системе определить способ представления вашей задачи и +операций на экране обзора. Вам не нужно менять это поведение.</p> + +<p class="caution"><strong>Внимание!</strong> В большинстве приложений не следует прерывать поведение +операций и задач по умолчанию. Если вы обнаружили, что вашей операции необходимо изменить +поведение по умолчанию, будьте внимательны и протестируйте удобство работы с операцией во время +запуска и при обратной навигации к ней из других операций и задач с помощью кнопки <em>Назад</em>. +Обязательно протестируйте поведение навигации, которое может противоречить поведению, ожидаемому пользователем.</p> + + +<h3 id="TaskLaunchModes">Определение режимов запуска</h3> + +<p>Режимы запуска позволяют вам определять связь нового экземпляра операции +с текущей задачей. Вы можете задавать различные режимы запуска двумя способами:</p> +<ul class="nolist"> + <li><a href="#ManifestForTasks">Использование файла манифеста</a> + <p>Когда вы объявляете операцию в вашем файле манифеста, вы можете указать, как операция +должна связываться с задачами при ее запуске.</li> + <li><a href="#IntentFlagsForTasks">Использование флагов намерений</a> + <p>Когда вы вызываете {@link android.app.Activity#startActivity startActivity()}, +вы можете включить флаг в {@link android.content.Intent}, который объявляет, как должна быть связана +новая операция с текущей задачей (и должна ли).</p></li> +</ul> + +<p>По существу, если Операция A запускает Операцию B, Операция B может определить в своем манифесте, как она +должна быть связана с текущей задачей (если вообще должна), а Операция A может также запросить, как Операция B +должна быть связана с текущей задачей. Если обе операции определяют, как Операция B +должна быть связана с задачей, тогда запрос Операции A (как определено в намерении) обрабатывается через +запрос Операции B (как определено в ее манифесте).</p> + +<p class="note"><strong>Примечание.</strong> Некоторые режимы запуска, доступные для файла манифеста, +недоступны в виде флагов для намерения и, аналогичным образом, некоторые режимы запуска, доступные в виде флагов +для намерения, не могут быть определены в манифесте.</p> + + +<h4 id="ManifestForTasks">Использование файла манифеста</h4> + +<p>При объявлении операции в вашем файле манифеста вы можете указать, как операция должна +быть связана с задачей посредством атрибута <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code +launchMode}</a> +элемента <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a>.</p> + +<p>Атрибут <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code +launchMode}</a> указывает инструкцию по запуску операции в +задаче. Существует четыре различных режима запуска, +которые вы можете назначить атрибуту +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">launchMode</a></code>:</p> + +<dl> +<dt>{@code "standard"} (режим по умолчанию)</dt> + <dd>Режим по умолчанию. Система создает новый экземпляр операции в задаче, из +которой она была запущена, и направляет ему намерение. Может быть создано несколько экземпляров операции, +каждый экземпляр может принадлежать различным задачам, и одна задача может содержать несколько экземпляров.</dd> +<dt>{@code "singleTop"}</dt> + <dd>Если экземпляр операции уже существует на вершине текущей задачи, система +направляет намерение в этот экземпляр путем вызова его метода {@link +android.app.Activity#onNewIntent onNewIntent()}, а не путем создания нового экземпляра +операции. Может быть создано несколько экземпляров операции, каждый экземпляр может +принадлежать различным задачам, и одна задача может содержать несколько экземпляров (но только если +операция на вершине стека переходов назад <em>не</em> является существующим экземпляром операции). + <p>Предположим, что стек переходов назад задачи состоит из корневой операции A с операциями B, C +и D на вершине (стек имеет вид A-B-C-D и D находится на вершине). Поступает намерение для операции типа D. +Если D имеет режим запуска {@code "standard"} по умолчанию, запускается новый экземпляр класса и +стек принимает вид A-B-C-D-D. Однако, если D имеет режим запуска {@code "singleTop"}, существующий экземпляр +D получает намерение через {@link +android.app.Activity#onNewIntent onNewIntent()}, так как этот экземпляр находится на вершине стека — +стек сохраняет вид A-B-C-D. Однако, если поступает намерение для операции типа B, тогда в стек +добавляется новый экземпляр B, даже если он имеет режим запуска {@code "singleTop"}.</p> + <p class="note"><strong>Примечание.</strong> Когда создается новый экземпляр операции, +пользователь может нажать кнопку <em>Назад</em> для возврата к предыдущей операции. Но когда существующий +экземпляр +операции обрабатывает новое намерение, пользователь не может нажать кнопку <em>Назад</em> для возврата к +состоянию +операции до поступления нового намерения в {@link android.app.Activity#onNewIntent +onNewIntent()}.</p> +</dd> + +<dt>{@code "singleTask"}</dt> + <dd>Система создает новую задачу и создает экземпляр операции в корне новой задачи. +Однако, если экземпляр операции уже существует в отдельной задаче, система направляет +намерение в существующий экземпляр путем вызова его метода {@link +android.app.Activity#onNewIntent onNewIntent()}, а не путем создания нового экземпляра. Одновременно +может существовать только один экземпляр операции. + <p class="note"><strong>Примечание.</strong> Хотя операция запускает новую задачу, кнопка +<em>Назад</em> возвращает пользователя к предыдущей операции.</p></dd> +<dt>{@code "singleInstance"}.</dt> + <dd>То же, что и {@code "singleTask"}, но при этом система не запускает никаких других операций +в задаче, содержащей этот экземпляр. Операция всегда является единственным членом своей задачи; +любые операции, запущенные этой операцией, открываются в отдельной задаче.</dd> +</dl> + + +<p>В качестве другого примера: приложение Android Browser объявляет, что операция веб-браузера должна +всегда открываться в своей собственной задаче — путем указания режима запуска {@code singleTask} в элементе <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a>. +Это означает, что если ваше приложение выдает +намерение открыть Android Browser, его операция <em>не</em> помещается в ту же +задачу, что и ваше приложение. Вместо этого, либо для браузера запускается новая задача, либо, если браузер уже +имеет задачу, работающую в фоновом режиме, эта задача переводится на передний план для обработки нового +намерения.</p> + +<p>И при запуске операции в новой задаче, и при запуске операции в существующей задаче, + кнопка <em>Назад</em> всегда возвращает пользователя к предыдущей операции. Однако, если вы +запускаете операцию, которая указывает режим запуска {@code singleTask}, вся задача переводится на передний план, если экземпляр +этой операции существует в фоновой задаче. В этот момент +стек переходов назад помещает все операции из задачи, переведенной на передний план, на вершину +стека. Рисунок 4 иллюстрирует сценарий этого типа.</p> + +<img src="{@docRoot}images/fundamentals/diagram_backstack_singletask_multiactivity.png" alt="" /> +<p class="img-caption"><strong>Рисунок 4.</strong> Представление того, как операция с режимом +запуска singleTask добавляется в стек переходов назад. Если операция уже является частью +фоновой задачи со своим собственным стеком переходов назад, то весь стек переходов назад также переносится вверх, +на вершину текущей задачи.</p> + +<p>Дополнительную информацию об использовании режимов запуска в файле манифеста см. в документации элемента +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code>, + где более подробно обсуждаются атрибут {@code launchMode} +и принимаемые значения.</p> + +<p class="note"><strong>Примечание.</strong> Поведение, которое вы указываете для вашей операции с помощью атрибута <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a>, +может быть переопределено флагами, включенными в намерение, которое запускает вашу операцию, как описано +в следующем разделе.</p> + + + +<h4 id="#IntentFlagsForTasks">Использование флагов намерений</h4> + +<p>При запуске операции вы можете изменить связывание операции с ее задачей по умолчанию +путем включения флагов в намерение, которое доставляется в {@link +android.app.Activity#startActivity startActivity()}. Для изменения поведения по умолчанию +вы можете использовать следующие флаги:</p> + +<p> + <dt>{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}</dt> + <dd>Запуск операции в новой задаче. Если задача уже работает для операции, которую вы запускаете +сейчас, эта задача переводится на передний план, ее последнее состояние восстанавливается, и операция получает +новое намерение в {@link android.app.Activity#onNewIntent onNewIntent()}. + <p>Это приводит к тому же поведению, что и значение <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a> в режиме {@code "singleTask"}, +как описано в предыдущем разделе.</p></dd> + <dt>{@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}</dt> + <dd>Если запускаемая операция является текущей операцией (находится на вершине стека переходов назад), тогда +вызов в {@link android.app.Activity#onNewIntent onNewIntent()} получает существующий экземпляр, + без создания нового экземпляра операции. + <p>Это приводит к тому же поведению, что и значение <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a> в режиме {@code "singleTop"}, +как описано в предыдущем разделе.</p></dd> + <dt>{@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}</dt> + <dd>Если запускаемая операция уже работает в текущей задаче, тогда вместо +запуска нового экземпляра этой операции уничтожаются все другие операции, расположенные в стеке выше нее +, и это намерение доставляется в возобновленный экземпляр этой операции (которая теперь находится на вершине стека) +посредством {@link android.app.Activity#onNewIntent onNewIntent()}). + <p>Для формирования такого поведения не существует значения для атрибута +<a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a>.</p> + <p>Флаг {@code FLAG_ACTIVITY_CLEAR_TOP} чаще всего используется совместно с +флагом {@code FLAG_ACTIVITY_NEW_TASK}. +При использовании вместе эти флаги позволяют найти существующую операцию +в другой задаче и поместить ее в положение, где она сможет реагировать на намерение. </p> + <p class="note"><strong>Примечание.</strong> Если для назначенной операции установлен режим запуска +{@code "standard"}, +она также удаляется из стека и на ее месте запускается новый экземпляр, чтобы обработать +входящее намерение. Именно поэтому в режиме запуска {@code "standard"} всегда создается новый +экземпляр для нового намерения. </p> +</dd> +</dl> + + + + + +<h3 id="Affinities">Обработка привязок</h3> + +<p><em>Привязка</em> указывает предпочтительную принадлежность операции к задаче. По умолчанию все +операции из одного приложения имеют привязку друг к другу. Поэтому по умолчанию все +операции одного приложения предпочитают находиться в одной задаче. Однако вы можете изменить +привязку по умолчанию для операции. Операции, определенные +в разных приложениях, могут совместно использовать одну привязку; таким же образом операции, определенные в одном приложении, могут получить +привязки к разным задачам.</p> + +<p>Вы можете изменить привязку любой данный операции с помощью +атрибута <a href="{@docRoot}guide/topics/manifest/activity-element.html#aff">{@code taskAffinity}</a> +элемента <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a>.</p> + +<p>Атрибут <a href="{@docRoot}guide/topics/manifest/activity-element.html#aff">{@code taskAffinity}</a> +принимает строковое значение, которое должно отличаться от имени пакета по умолчанию, +объявленного в элементе <a href="{@docRoot}guide/topics/manifest/manifest-element.html"> +{@code <manifest>} +</a>, поскольку система использует это имя для идентификации привязки задачи по умолчанию +для приложения.</p> + +<p>Привязка вступает в игру в двух случаях:</p> +<ul> + <li>Когда намерение, запускающее +операцию, содержит флаг +{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}. + +<p>Новая операция по умолчанию запускается в задаче той операции, +которая вызвала {@link android.app.Activity#startActivity startActivity()}. Она помещается в тот же +стек переходов назад, что и вызывающая операция. Однако, если намерение, переданное +в {@link android.app.Activity#startActivity startActivity()}, +содержит флаг {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}, + система ищет другую задачу для помещения новой операции. Часто это новая задача. +Но необязательно. Если уже существует задача с той же привязкой, что +и у новой операции, операция запускается в этой задаче. Если нет, операция начинает новую задачу.</p> + +<p>Если этот флаг приводит к тому, что операция начинает новую задачу, и пользователь нажимает кнопку <em>Домой</em> +для выхода из нее, +должен существовать способ, позволяющий пользователю вернуться к задаче. Некоторые объекты (такие как +диспетчер уведомлений) всегда запускают операции во внешней задаче, а не в составе собственной, поэтому +они всегда помещают флаг {@code FLAG_ACTIVITY_NEW_TASK} в намерения, которые они передают +в {@link android.app.Activity#startActivity startActivity()}. +Если у вас есть операция, которую можно вызвать +внешним объектом, использующим этот флаг, позаботьтесь о том, чтобы у пользователя был независимый способ +вернуться в запущенную задачу, например, с помощью значка запуска (корневая операция задачи +содержит фильтр намерений {@link android.content.Intent#CATEGORY_LAUNCHER}; см. раздел <a href="#Starting">Запуск задачи</a> ниже).</p> +</li> + + <li>Если для атрибута <a href="{@docRoot}guide/topics/manifest/activity-element.html#reparent"> +{@code allowTaskReparenting}</a> операции установлено значение {@code "true"}. + <p>В этом случае операция может переместиться из запустившей ее задачи в задачу, к которой у операции есть привязка, +когда эта задача переходит на передний план.</p> + <p>Предположим, что операция, которая сообщает о погодных условиях в выбранных городах, +определена в составе приложения для путешественников. Она имеет ту же привязку, что и другие операции в том же +приложении (привязка приложения по умолчанию), и допускает переподчинение с этим атрибутом. +Когда одна из ваших операций запускает операцию прогноза погоды, она изначально принадлежит той же +задаче, что и ваша операция. Однако, когда задача из приложения для путешественников переходит на передний план, +операция прогноза погоды переназначается этой задаче и отображается внутри нее.</p> +</li> +</ul> + +<p class="note"><strong>Совет.</strong> Если файл {@code .apk} содержит более одного «приложения» +с точки зрения пользователя, вы, вероятно, захотите использовать атрибут <a href="{@docRoot}guide/topics/manifest/activity-element.html#aff">{@code taskAffinity}</a> +для назначения разных привязок операциям, связанным с каждым «приложением».</p> + + + +<h3 id="Clearing">Очистка стека переходов назад</h3> + +<p>Если пользователь выходит из задачи на длительное время, система удаляет из задачи все операции, кроме +корневой операции. Когда пользователь возвращается в задачу, восстанавливается только корневая операция. +Система ведет себя таким образом, так как после продолжительного времени пользователи обычно уже забросили то, +чем они занимались ранее, и возвращаются в задачу, чтобы начать что-то новое. </p> + +<p>Для изменения такого поведения предусмотрено несколько атрибутов операции, которыми вы можете воспользоваться: </p> + +<dl> +<dt><code><a +href="{@docRoot}guide/topics/manifest/activity-element.html#always">alwaysRetainTaskState</a></code> +</dt> +<dd>Если для этого атрибута установлено значение {@code "true"} в корневой операции задачи, +описанное выше поведение по умолчанию не происходит. +Задача восстанавливает все операции в своем стеке даже по истечении длительного периода времени.</dd> + +<dt><code><a +href="{@docRoot}guide/topics/manifest/activity-element.html#clear">clearTaskOnLaunch</a></code></dt> +<dd>Если для этого атрибута установлено значение {@code "true"} в корневой операции задачи, +стек очищается до корневой операции каждый раз, когда пользователь выходит из задачи +и возвращается в нее. Другими словами, этот атрибут противоположен атрибуту +<a href="{@docRoot}guide/topics/manifest/activity-element.html#always"> +{@code alwaysRetainTaskState}</a>. Пользователь всегда возвращается в задачу в ее +исходном состоянии, даже после кратковременного выхода из нее.</dd> + +<dt><code><a +href="{@docRoot}guide/topics/manifest/activity-element.html#finish">finishOnTaskLaunch</a></code> +</dt> +<dd>Этот атрибут похож на <a href="{@docRoot}guide/topics/manifest/activity-element.html#clear">{@code clearTaskOnLaunch}</a>, +но он действует на +одну операцию, а не на всю задачу. Он также может приводить к удалению любой операции, +включая корневую операцию. Когда для него установлено значение {@code "true"}, +операция остается частью задачи только для текущего сеанса. Если пользователь +выходит из задачи, а затем возвращается в нее, операция уже отсутствует.</dd> +</dl> + + + + +<h3 id="Starting">Запуск задачи</h3> + +<p>Вы можете сделать операцию точкой входа, назначая ей фильтр намерений со значением +{@code "android.intent.action.MAIN"} в качестве указанного действия и +{@code "android.intent.category.LAUNCHER"} +в качестве указанной категории. Например:</p> + +<pre> +<activity ... > + <intent-filter ... > + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + ... +</activity> +</pre> + +<p>Фильтр намерений такого типа приводит к тому, что в средстве запуска приложения отображаются значок и метка для +операции, что позволяет пользователю запускать операцию +и возвращаться в создавшую ее задачу в любой момент после ее запуска. +</p> + +<p>Эта вторая возможность очень важна: пользователи должны иметь возможность выходить из задачи и затем возвращаться в нее +с помощью этого средства запуска операции. Поэтому два <a href="#LaunchModes">режима +запуска</a>, которые отмечают, что операции всегда инициируют задачу, {@code "singleTask"} и +{@code "singleInstance"}, должны использоваться только в тех случаях, когда операция содержит +{@link android.content.Intent#ACTION_MAIN} +и фильтр {@link android.content.Intent#CATEGORY_LAUNCHER}. Представьте, например, что может произойти, +если фильтр отсутствует: намерение запускает операцию {@code "singleTask"}, которая инициирует +новую задачу, и пользователь некоторое время работает в этой задаче. Затем пользователь нажимает кнопку +<em>Домой</em>. Задача переводится в фоновый режим и не отображается. Теперь у пользователя нет возможности вернуться +к задаче, так как она отсутствует в средстве запуска приложения.</p> + +<p>Для таких случаев, когда вы не хотите, чтобы пользователь мог вернуться к операции, установите для атрибута +<a href="{@docRoot}guide/topics/manifest/activity-element.html#finish">{@code finishOnTaskLaunch}</a> +элемента +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> +значение {@code "true"} (см. раздел <a href="#Clearing">Очистка стека</a>).</p> + +<p>Дополнительную информацию о представлении задач и операций и управлении ими +на экране обзора см. в разделе <a href="{@docRoot}guide/components/recents.html"> +Экран обзора</a>.</p> + +<!-- +<h2>Beginner's Path</h2> + +<p>For more information about how to use intents to +activate other application components and publish the intents to which your components +respond, continue with the <b><a +href="{@docRoot}guide/components/intents-filters.html">Intents and Intent +Filters</a></b> document.</p> +--> |