diff options
Diffstat (limited to 'docs/html-intl/intl/ru')
13 files changed, 2474 insertions, 0 deletions
diff --git a/docs/html-intl/intl/ru/training/basics/activity-lifecycle/index.jd b/docs/html-intl/intl/ru/training/basics/activity-lifecycle/index.jd new file mode 100644 index 0000000..b8de11e --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/activity-lifecycle/index.jd @@ -0,0 +1,72 @@ +page.title=Управление жизненным циклом операций +page.tags=жизненный цикл операций +helpoutsWidget=true + +trainingnavtop=true +startpage=true + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + + +<h2>Необходимые знания и компоненты</h2> +<ul> + <li>Как создать проект для Android (см. <a href="{@docRoot}training/basics/firstapp/creating-project.html">Создание проекта для Android +</a>)</li> +</ul> + + +<h2>См. также:</h2> +<ul> + <li><a href="{@docRoot}guide/components/activities.html">Операции</a></li> +</ul> + + +<h2>Попробуйте сами</h2> + +<div class="download-box"> + <a href="http://developer.android.com/shareables/training/ActivityLifecycle.zip" class="button">Загрузить демонстрацию</a> + <p class="filename">ActivityLifecycle.zip</p> +</div> + +</div> +</div> + +<p>Во время навигации пользователя по вашему приложению экземпляры +{@link android.app.Activity} внутри приложения переключаются между разными состояниями их +жизненного цикла Например, при первом запуске +операции она получает высокий приоритет в системе и привлекает внимание +пользователя. Во время этого процесса система Android вызывает серию методов жизненного цикла +операции, позволяя настроить пользовательский интерфейс и другие компоненты. Если пользователь выполняет +действие, запускающее другую операцию, или переключается на другое приложение, система вызывает другой набор +методов жизненного цикла для операции, поскольку она переносится на фоновый уровень (операция больше не +отображается, но экземпляр и состояние остаются без изменений).</p> + +<p>В методах обратного вызова жизненного цикла можно декларировать поведение операции, когда +пользователь прекращает и снова запускает операцию. Например, если вы разрабатываете проигрыватель потокового видео, +то можете сделать так, чтобы при переключении пользователя на другое приложение видео ставилось на паузу, +а сетевое соединение разрывалось. После возврата пользователя проигрыватель может снова подключиться к сети, и пользователь сможет возобновить воспроизведение +видео с того же самого места.</p> + +<p>В этом учебном курсе разъясняются важные методы обратного вызова жизненного цикла, которые получает каждый экземпляр {@link +android.app.Activity}, и описывается как их использовать, чтобы операция выполнялась так, как этого ожидает +пользователь, и не потребляла системные ресурсы, когда они ей не нужны.</p> + +<h2>Уроки</h2> + +<dl> + <dt><b><a href="starting.html">Запуск операции</a></b></dt> + <dd>Из этого урока вы узнаете об основах жизненного цикла операций, способах запуска вашего приложения пользователями и вариантах +создания базовых операций.</dd> + <dt><b><a href="pausing.html">Приостановка и возобновление операции</a></b></dt> + <dd>Вы узнаете, что происходит во время приостановки операции (окно операции частично затемнено) и возобновления операции, +и что следует делать во время подобных изменений состояния.</dd> + <dt><b><a href="stopping.html">Остановка и перезапуск операции</a></b></dt> + <dd>В этом уроке рассказывается о том, что происходит, когда пользователь полностью прекращает операцию, а потом возвращается к ней.</dd> + <dt><b><a href="recreating.html">Повторное создание операции</a></b></dt> + <dd>Вы узнаете, что происходит при полном прекращении операции, и как можно восстановить ее состояние +в случае необходимости.</dd> +</dl> + diff --git a/docs/html-intl/intl/ru/training/basics/activity-lifecycle/pausing.jd b/docs/html-intl/intl/ru/training/basics/activity-lifecycle/pausing.jd new file mode 100644 index 0000000..c483780 --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/activity-lifecycle/pausing.jd @@ -0,0 +1,147 @@ +page.title=Приостановка и возобновление операции +page.tags=жизненный цикл операции +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> + <div id="tb"> + + <h2>Содержание этого урока</h2> + <ol> + <li><a href="#Pause">Приостановка операции</a></li> + <li><a href="#Resume">Возобновление операции</a></li> + </ol> + + <h2>См. также:</h2> + <ul> + <li><a href="{@docRoot}guide/components/activities.html">Операции</a> + </li> + </ul> + +<h2>Попробуйте сами</h2> + +<div class="download-box"> + <a href="http://developer.android.com/shareables/training/ActivityLifecycle.zip" class="button">Загрузить демонстрацию</a> + <p class="filename">ActivityLifecycle.zip</p> +</div> + + </div> +</div> + +<p>При обычном использовании приложения выполняемая на экране операция иногда закрывается другими +визуальными компонентами, в результате чего операция <em>приостанавливается</em>. Например, при открытии полупрозрачной +операции (например диалогового окна) предыдущая операция приостанавливается. Пока +операция остается частично видимой, но не остается в фокусе, она остается на паузе.</p> + +<p>Однако когда операция полностью закрывается и становится невидимой, она <em>останавливается</em> (об этом будет рассказано +на следующем уроке).</p> + +<p>Когда операция приостанавливается, система вызывает метод {@link +android.app.Activity#onPause onPause()} на {@link android.app.Activity}, что позволяет +остановить текущие действия, которые не должны продолжаться во время паузы (например воспроизведение видео), или сохранять +любую информацию, которая должна постоянно сохраняться в случае выхода пользователя из приложения. Если +пользователь возвращается к операции после паузы, система возобновляет ее и вызывает метод +{@link android.app.Activity#onResume onResume()}.</p> + +<p class="note"><strong>Примечание.</strong> Когда операция получает вызов {@link +android.app.Activity#onPause()}, это может указывать, что операция будет приостановлена совсем ненадолго +и пользователь сможет к ней вернуться. Однако обычно это является первым признаком +выхода пользователя из операции.</p> + +<img src="{@docRoot}images/training/basics/basic-lifecycle-paused.png" /> +<p class="img-caption"><strong>Рисунок 1</strong>. Когда полупрозрачная операция закрывает +вашу операцию, система вызывает {@link android.app.Activity#onPause onPause()} и операция +переходит в состояние паузы (1). Если пользователь возвращается к операции, находящейся в состоянии паузы, +система вызывает {@link android.app.Activity#onResume onResume()} (2).</p> + + +<h2 id="Pause">Приостановка операции</h2> + +<p>Когда система вызывает {@link android.app.Activity#onPause()} для операции, это +технически означает, что операция остается частично видимой. Однако чаще всего это означает, что +пользователь покидает операцию, и вскоре она войдет в состояние остановки. Обратный вызов + {@link android.app.Activity#onPause()} обычно следует использовать для следующих целей:</p> + +<ul> + <li>Остановка анимации или других текущих действий, которые могут потреблять ресурсы процессора.</li> + <li>Запись несохраненных изменений, если пользователи ожидают сохранения таких изменений +при выходе (например черновиков электронных писем).</li> + <li>Высвобождение ресурсов системы, например ресиверов, интерфейсов сенсоров (например +GPS) или других ресурсов, влияющих на время работы аккумулятора во время паузы и не +требующихся пользователю.</li> +</ul> + +<p>Например, если ваше приложение использует {@link android.hardware.Camera}, метод +{@link android.app.Activity#onPause()} подойдет для его освобождения.</p> + +<pre> +@Override +public void onPause() { + super.onPause(); // Always call the superclass method first + + // Release the Camera because we don't need it when paused + // and other activities might need to use it. + if (mCamera != null) { + mCamera.release() + mCamera = null; + } +} +</pre> + +<p>Обычно <strong>не</strong> следует использовать {@link android.app.Activity#onPause()} для сохранения +пользовательских изменений (например введенной в форму личной информации) в постоянном хранилище. Изменения +пользователя нужно сохранять в постоянном хранилище {@link android.app.Activity#onPause()} +только в тех случаях, когда пользователи ожидают автоматического сохранения изменений (например при составлении черновика электронного письма). +Однако в состоянии {@link +android.app.Activity#onPause()} следует избегать операций с высокой нагрузкой на процессор, например выполнения записи в базу данных, поскольку это может замедлить видимый +переход к следующей операции (вместо этого следует выполнять операции завершения работы с высокой нагрузкой при +{@link android.app.Activity#onStop onStop()}).</p> + +<p>Количество операций, выполняемых с помощью метода {@link android.app.Activity#onPause +onPause()}, должно быть не очень большим, чтобы пользователь мог быстро переключаться на следующую +задачу при фактической остановке вашей операции.</p> + +<p class="note"><strong>Примечание.</strong> При приостановке операции экземпляр {@link +android.app.Activity} остается в памяти и заново вызывается при возобновлении операции. +Не нужно заново инициализировать компоненты, созданные с использованием любого из методов обратного вызова для возобновления +операции.</p> + + + +<h2 id="Resume">Возобновление операции</h2> + +<p>Когда пользователь возобновляет операцию после паузы, система вызывает метод {@link +android.app.Activity#onResume()}.</p> + +<p>Учтите, что система вызывает этот метод каждый раз, когда ваша операция выполняется на экране, +в том числе и при первом ее запуске. В связи с этим, нужно реализовать метод {@link +android.app.Activity#onResume()} для инициализации компонентов, освобождаемых в состоянии {@link +android.app.Activity#onPause()}, и выполнения других операций инициализации, которые должны происходить каждый раз при возобновлении операции +(например при запуске анимированных элементов и компонентов, которые используются +только когда пользователь следит за операцией на экране).</p> + +<p>Следующий пример {@link android.app.Activity#onResume()} дополняет +пример {@link android.app.Activity#onPause()} выше. В нем инициализируется камера, +которая освобождается на время приостановки операции.</p> + +<pre> +@Override +public void onResume() { + super.onResume(); // Always call the superclass method first + + // Get the Camera instance as the activity achieves full user focus + if (mCamera == null) { + initializeCamera(); // Local method to handle camera init + } +} +</pre> + + + + + + + diff --git a/docs/html-intl/intl/ru/training/basics/activity-lifecycle/recreating.jd b/docs/html-intl/intl/ru/training/basics/activity-lifecycle/recreating.jd new file mode 100644 index 0000000..acb89fa --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/activity-lifecycle/recreating.jd @@ -0,0 +1,178 @@ +page.title=Воссоздание операции +page.tags=жизненный цикл операции +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> + <div id="tb"> + + <h2>Содержание этого урока</h2> + <ol> + <li><a href="#SaveState">Сохранение состояния операции</a></li> + <li><a href="#RestoreState">Восстановление состояния операции</a></li> + </ol> + + <h2>См. также:</h2> + <ul> + <li><a href="{@docRoot}training/basics/supporting-devices/screens.html">Поддержка +различных экранов</a></li> + <li><a href="{@docRoot}guide/topics/resources/runtime-changes.html">Обработка изменений в режиме выполнения</a></li> + <li><a href="{@docRoot}guide/components/activities.html">Операции</a> + </li> + </ul> + + </div> +</div> + +<p>Существуют ситуации, когда операция уничтожается в результате нормального поведения приложения. Например, это происходит, +когда пользователь нажимает кнопку <em>Назад</em> или когда операция подает сигнал о своем уничтожении +посредством вызова {@link android.app.Activity#finish()}. Система также может уничтожить операцию, +если она остановлена и не используется в течение длительного времени, или если для выполнения операции на экране требуется больше +системных ресурсов и системе нужно закрыть фоновые процессы для освобождения памяти.</p> + +<p>Если операция уничтожается при нажатии пользователем кнопки <em>Назад</em> или завершении +операции, система считает, что экземпляр {@link android.app.Activity} исчезает навсегда, +так как такое поведение указывает, что операция больше не нужна. Однако если система уничтожает +операцию в связи с системными ограничениями (а не в процессе обычной работы приложения), хотя фактический +{@link android.app.Activity} экземпляр исчезает, система помнит о его существовании, и если +пользователь вернется к нему, система создаст новый экземпляр действия, используя набор +сохраненных данных, описывающий состояние операции на момент ее уничтожения. Сохраненные данные, используемые +системой для восстановления предыдущего состояния, называются "состоянием экземпляра" и представляют собой набор +пар "ключ-значение", хранящийся в объекте {@link android.os.Bundle}.</p> + +<p class="caution"><strong>Внимание!</strong> Ваша операция будет уничтожаться и восстанавливаться каждый раз, +когда пользователь вращает экран. При изменении ориентации экрана система уничтожает и заново создает +активную операцию, поскольку конфигурация экрана меняется и операции может потребоваться +загрузка альтернативных ресурсов (например нового макета).</p> + +<p>По умолчанию система использует состояние экземпляра {@link android.os.Bundle} для сохранения информации +о каждом объекте {@link android.view.View} в макете операции (например, о текстовом значении, +введенном в объект {@link android.widget.EditText}). Таким образом, если экземпляр вашей операции уничтожается и +воссоздается заново, происходит восстановление предыдущего состояния макета, +и при этом вам не нужно добавлять в приложение дополнительный код. Однако операция +может содержать больше информации о состоянии, чем вы хотите восстановить, например переменные, +отслеживающие ход выполнения операции пользователем.</p> + +<p class="note"><strong>Примечание.</strong> Чтобы система Android могла восстановить состояние +представлений операции, <strong>каждое представление должно иметь уникальный идентификатор</strong>, предоставляемый атрибутом +<a href="{@docRoot}reference/android/view/View.html#attr_android:id">{@code +android:id}</a>.</p> + +<p>Для сохранения дополнительных данных о состоянии операции, необходимо +заменить метод обратного вызова {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()}. +Система вызывает этот метод, когда пользователь покидает операцию, +и передает ему объект {@link android.os.Bundle}, который будет сохранен в +случае, если операция будет неожиданно уничтожена. Если +системе нужно будет воссоздать экземпляр экземпляра операции, она передаст тот же объект {@link +android.os.Bundle} методам {@link android.app.Activity#onRestoreInstanceState +onRestoreInstanceState()} и {@link android.app.Activity#onCreate onCreate()}. +</p> + +<img src="{@docRoot}images/training/basics/basic-lifecycle-savestate.png" /> +<p class="img-caption"><strong>Рисунок 2</strong>. Когда система начинает останавливать операцию, она +вызывает {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} (1), чтобы вы могли указать +дополнительные данные состояния, которые нужно сохранить на случай необходимости воссоздания экземпляра {@link android.app.Activity}. +Если операция будет уничтожена, +и системе нужно будет воссоздать тот же экземпляр, она передаст данные +состояния, определенные в (1), методам {@link android.app.Activity#onCreate onCreate()} +(2) и {@link android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} +(3).</p> + + + +<h2 id="SaveState">Сохранение состояния операции</h2> + +<p>Когда начинается остановка операции, система вызывает метод {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()}, чтобы операция могла сохранить информацию о состоянии с помощью набора пар +"ключ-значение". По умолчанию при реализации этого метода сохраняется информация о состоянии иерархии +представления операции, например текст в виджете {@link android.widget.EditText} или положение экрана +для {@link android.widget.ListView}.</p> + +<p>Для сохранения дополнительной информации о состоянии операции +необходимо реализовать {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} и добавить +к объекту {@link android.os.Bundle} пары "ключ-значение". Например:</p> + +<pre> +static final String STATE_SCORE = "playerScore"; +static final String STATE_LEVEL = "playerLevel"; +... + +@Override +public void onSaveInstanceState(Bundle savedInstanceState) { + // Save the user's current game state + savedInstanceState.putInt(STATE_SCORE, mCurrentScore); + savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel); + + // Always call the superclass so it can save the view hierarchy state + super.onSaveInstanceState(savedInstanceState); +} +</pre> + +<p class="caution"><strong>Внимание!</strong> Реализацию суперкласса {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} следует вызывать во всех случаях, чтобы реализация +по умолчанию могла сохранить состояние новой иерархии.</p> + + + +<h2 id="RestoreState">Восстановление состояния операции</h2> + +<p>В случае воссоздания операции после предыдущего уничтожения сохраненное +состояние можно восстановить из {@link android.os.Bundle}, куда система +передает данные операции. Методы обратного вызова {@link android.app.Activity#onCreate onCreate()} и {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} получают один и +тот же {@link android.os.Bundle}, содержащий информацию о состоянии экземпляра.</p> + +<p>Поскольку метод {@link android.app.Activity#onCreate onCreate()} вызывается, если +система создает новый экземпляр операции или восстанавливает предыдущий экземпляр, перед попыткой чтения необходимо убедиться, +что {@link android.os.Bundle} имеет состояние null. В этом случае +система создает новый экземпляр операции +вместо восстановления ранее уничтоженного экземпляра.</p> + +<p>Приведем пример восстановления некоторых данных о состоянии в {@link android.app.Activity#onCreate +onCreate()}:</p> + +<pre> +@Override +protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); // Always call the superclass first + + // Check whether we're recreating a previously destroyed instance + if (savedInstanceState != null) { + // Restore value of members from saved state + mCurrentScore = savedInstanceState.getInt(STATE_SCORE); + mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL); + } else { + // Probably initialize members with default values for a new instance + } + ... +} +</pre> + +<p>Вместо восстановления состояния в {@link android.app.Activity#onCreate onCreate()} вы +можете реализовать метод {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()}, который система вызывает +после метода {@link android.app.Activity#onStart()}. Система вызывает {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} только при наличии сохраненного состояния +для восстановления, и поэтому вам не нужно проверять, имеет ли {@link android.os.Bundle} значение null:</p> + +<pre> +public void onRestoreInstanceState(Bundle savedInstanceState) { + // Always call the superclass so it can restore the view hierarchy + super.onRestoreInstanceState(savedInstanceState); + + // Restore state members from saved instance + mCurrentScore = savedInstanceState.getInt(STATE_SCORE); + mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL); +} +</pre> + +<p class="caution"><strong>Внимание!</strong> Реализацию суперкласса {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} следует вызывать во всех случаях, чтобы реализация +по умолчанию могла сохранить состояние новой иерархии.</p> + +<p>Более подробную информацию о воссоздании операции в связи +с перезапуском во время исполнения (например при повороте экрана) можно найти в разделе <a href="{@docRoot}guide/topics/resources/runtime-changes.html">Обработка изменений в режиме выполнения</a>.</p> + diff --git a/docs/html-intl/intl/ru/training/basics/activity-lifecycle/starting.jd b/docs/html-intl/intl/ru/training/basics/activity-lifecycle/starting.jd new file mode 100644 index 0000000..3a946e2 --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/activity-lifecycle/starting.jd @@ -0,0 +1,285 @@ +page.title=Запуск операции +page.tags=жизненный цикл операции +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + + +<div id="tb-wrapper"> + <div id="tb"> + + <h2>Содержание этого урока</h2> +<ol> + <li><a href="#lifecycle-states">Изучение обратных вызовов жизненного цикла</a></li> + <li><a href="#launching-activity">Указание операции, запускающей приложение</a></li> + <li><a href="#Create">Создание нового экземпляра</a></li> + <li><a href="#Destroy">Уничтожение операции</a></li> +</ol> + + <h2>См. также:</h2> + <ul> + <li><a href="{@docRoot}guide/components/activities.html">Операции</a></li> + </ul> + +<h2>Попробуйте сами</h2> + +<div class="download-box"> + <a href="http://developer.android.com/shareables/training/ActivityLifecycle.zip" class="button">Загрузить демонстрацию</a> + <p class="filename">ActivityLifecycle.zip</p> +</div> + + </div> +</div> + +<p>В отличие от других парадигм программирования, где приложения запускаются с использованием метода {@code main()}, система +Android запускает код в {@link android.app.Activity}экземпляре посредством активации определенных +методов обратного вызова, соответствующих определенным этапам его +жизненного цикла. Существует последовательность методов обратного вызова, которые запускают операцию и последовательность +методов обратного вызова, уничтожающих операцию.</p> + +<p>В этом уроке рассматриваются наиболее важные методы жизненного цикла и демонстрируется, как +обработать первый обратный вызов жизненного цикла, создающий новый экземпляр операции.</p> + + + +<h2 id="lifecycle-states">Изучение обратных вызовов жизненного цикла</h2> + +<p>В течение цикла существования операции система вызывает базовый набор методов жизненного цикла в +последовательности, сходной с многоступенчатой пирамидой. Таким образом, каждый этап +жизненного цикла операции представляет собой отдельную ступень пирамиды. Когда система создает новый экземпляр операции, +каждый метод обратного вызова перемещает состояние действия на одну ступень вверх. Вершина пирамиды представляет собой +точку, в которой операция выполняется в экранном режиме, и пользователь может с ней взаимодействовать.</p> + +<p>Когда пользователь начинает выходить из операции, система вызывает другие методы, которые перемещают состояние +операции вниз по пирамиде для уничтожения действия. В некоторых случаях действие +перемещает операцию вниз по пирамиде только частично и ждет (например когда пользователь переключается на другое приложение), +а затем операция может быть перемещена обратно вверх (если пользователь вернется к операции) и +возобновлена там, где пользователь вышел из нее.</p> + + +<img src="{@docRoot}images/training/basics/basic-lifecycle.png" /> +<p class="img-caption"><strong>Рисунок 1</strong>. Упрощенная иллюстрация жизненного цикла операции +в виде многоступенчатой пирамиды. На рисунке показано, что для каждого обратного вызова, +поднимающего операцию на одну ступень к состоянию возобновления на вершине пирамиды, +существует обратный вызов, опускающий операцию на одну ступень вниз. Возобновление операции также может производиться из состояний +паузы и остановки.</p> + + +<p>В зависимости от сложности операции, некоторые +методы жизненного цикла могут не требоваться. Однако очень важно понимать все методы и реализовать их так, чтобы +приложение работало так, как этого ожидают пользователи. Правильная реализация методов жизненного цикла операции +обеспечивает нормальную работу приложения в нескольких аспектах, в том числе:</p> +<ul> + <li>Не прекращает работу, если пользователь получает телефонный звонок или переключается на другое приложение +во время использования вашего приложения.</li> + <li>Не потребляет ценные системные ресурсы, когда пользователь не использует +его активно.</li> + <li>Сохраняет состояние приложения, если пользователь выходит из него и возвращается +позднее.</li> + <li>Не закрывается с ошибкой и не теряет данные пользователя при повороте экрана (смене ориентации между книжной +и альбомной.</li> +</ul> + +<!-- +<p class="table-caption"><strong>Table 1.</strong> Activity lifecycle state pairs and callback +methods.</p> +<table> + <tr> + <th scope="col">Lifecycle State</th> + <th scope="col">Startup Method</th> + <th scope="col">Teardown Method</th> + </tr> + <tr> + <td>Created / Destroyed</td> + <td>{@link android.app.Activity#onCreate onCreate()}</td> + <td>{@link android.app.Activity#onDestroy()}</td> + </tr> + <tr> + <td>Started / Stopped</td> + <td>{@link android.app.Activity#onStart()}</td> + <td>{@link android.app.Activity#onStop()}</td> + </tr> + <tr> + <td>Resumed / Resumed</td> + <td>{@link android.app.Activity#onResume()}</td> + <td>{@link android.app.Activity#onPause()}</td> + </tr> +</table> +--> + +<p>Как вы узнаете на следующих уроках, в некоторых ситуациях операция +переключается между разными состояниями, как показано на рисунке 1. Однако только три +из этих состояний могут быть статичными. Это означает, что операция может существовать относительно длительное +время только в одном из этих трех состояний.</p> +<dl> + <dt>Возобновление</dt> + <dd>В этом состоянии операция выполняется в экранном режиме, и пользователь может с ней взаимодействовать. +Иногда это состояние также называется рабочим состоянием.</dd> + <dt>Приостановка</dt> + <dd>В этом состоянии операция частично закрывается другой операцией — в экранном режиме +эта другая операция или является полупрозрачной, или не закрывает экран целиком. Приостановленная операция +не получает команд пользователя и не может выполнять код. + <dt>Остановка</dt> + <dd>В этом состоянии операция полностью скрыта и невидима для пользователя. Она считается находящейся в +фоновом режиме. В состоянии остановки сохраняется экземпляр операции и вся его информация +о состоянии, например, переменных, однако операция не может выполнять код.</dd> +</dl> + +<p>Другие состояния (создание и запуск) являются переходными, и система быстро переходит от них +к следующим состояниям посредством вызова следующего метода обратного вызова в жизненном цикле. Таким образом, после вызова +{@link android.app.Activity#onCreate onCreate()} система быстро вызывает {@link +android.app.Activity#onStart()}, а затем сразу же вызывает {@link +android.app.Activity#onResume()}.</p> + +<p>Так выглядит базовый жизненный цикл операции. Теперь рассмотрим определенные виды +поведения в жизненном цикле.</p> + + + +<h2 id="launching-activity">Указание операции, запускающей приложение</h2> + +<p>Когда пользователь выбирает значок приложения на главном экране, система вызывает метод {@link +android.app.Activity#onCreate onCreate()} для {@link android.app.Activity} в вашем приложении +в соответствии с тем, какую операцию вы задекларировали как операцию запуска (или основную операцию). Эта операция выступает +основной точкой входа в пользовательский интерфейс вашего приложения.</p> + +<p>Для определения основной операции вы можете использовать файл манифеста Android <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">{@code AndroidManifest.xml}</a>, +который находится в корневом каталоге вашего проекта.</p> + +<p>Основная операция приложения должна декларироваться в манифесте с помощью фильтра <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a>, включающего действие {@link +android.content.Intent#ACTION_MAIN MAIN} и категорию +{@link android.content.Intent#CATEGORY_LAUNCHER LAUNCHER}. Например:</p> + +<pre> +<activity android:name=".MainActivity" android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> +</activity> +</pre> + +<p class="note"><strong>Примечание.</strong> При создании нового проекта Android с помощью инструментов +Android SDK файлы проекта по умолчанию включают класс {@link android.app.Activity}, который декларируется в +манифесте с помощью этого фильтра.</p> + +<p>Если для одной из операций не декларировано действие {@link android.content.Intent#ACTION_MAIN MAIN} или категория +{@link android.content.Intent#CATEGORY_LAUNCHER LAUNCHER}, значок +приложения не будет отображатья в списке приложений на главном экране.</p> + + + +<h2 id="Create">Создание нового экземпляра</h2> + +<p>Большинство приложений содержат различные операции, позволяющие пользователю выполнять различные действия. +Как для основных операций, создаваемых при нажатии на значок приложения, так +и для других операций, которыми приложение реагирует на действия пользователя, система создает +каждый новый экземпляр {@link android.app.Activity} посредством вызова его метода {@link +android.app.Activity#onCreate onCreate()}.</p> + +<p>Вы должны реализовать метод {@link android.app.Activity#onCreate onCreate()} для выполнения базовой +логики запуска приложения, которое должно производиться только один раз для всего срока существования операции. Например, +ваша реализация {@link android.app.Activity#onCreate onCreate()} должна содержать определение пользовательского +интерфейса и возможно создавать экземпляры некоторых переменных уровня класса.</p> + +<p>Например, в следующем примере метода {@link android.app.Activity#onCreate onCreate()} +показан код, выполняющий фундаментальную настройку операции, включая +декларирование пользовательского интерфейса (определен в файле макета XML), определение составных переменных +и частичную настройку пользовательского интерфейса.</p> + +<pre> +TextView mTextView; // Member variable for text view in the layout + +@Override +public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Set the user interface layout for this Activity + // The layout file is defined in the project res/layout/main_activity.xml file + setContentView(R.layout.main_activity); + + // Initialize member TextView so we can manipulate it later + mTextView = (TextView) findViewById(R.id.text_message); + + // Make sure we're running on Honeycomb or higher to use ActionBar APIs + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + // For the main activity, make sure the app icon in the action bar + // does not behave as a button + ActionBar actionBar = getActionBar(); + actionBar.setHomeButtonEnabled(false); + } +} +</pre> + +<p class="caution"><strong>Внимание!</strong> Использование {@link android.os.Build.VERSION#SDK_INT} для +предотвращения запуска новых API-интерфейсов на старых системах поддерживается только в Android версии 2.0 (API-интерфейсы уровня +5) и более поздних версиях. В старых версиях возникнет ошибка времени исполнения.</p> + +<p>После завершения выполнения {@link android.app.Activity#onCreate onCreate()} система +быстро вызывает методы {@link android.app.Activity#onStart()} и {@link android.app.Activity#onResume()} по +очереди. Операция никогда не остается в состоянии создания или запуска. Технически +операция становится видимой для пользователя при вызове {@link android.app.Activity#onStart()}, однако затем сразу же происходит +{@link android.app.Activity#onResume()} и операция остается в состоянии возобновления, +пока что-то не произойдет, например пока не поступит телефонный звонок, пользователь не переключится +на другую операцию или экран устройства не выключится.</p> + +<p>На последующих уроках вы увидите, как можно с пользой использовать другие методы запуска, {@link +android.app.Activity#onStart()} и {@link android.app.Activity#onResume()}, в жизненном цикле операции +при возобновлении работы после паузы или остановки.</p> + +<p class="note"><strong>Примечание.</strong> Метод {@link android.app.Activity#onCreate onCreate()} +включает параметр <code>savedInstanceState</code>, о котором будет рассказано на +уроке <a href="recreating.html">Воссоздание операции</a>.</p> + + +<img src="{@docRoot}images/training/basics/basic-lifecycle-create.png" /> +<p class="img-caption"><strong>Рисунок 2.</strong> Еще один пример структуры жизненного +цикла операции, где основное внимание трем главным обратным вызовам, которые система выполняет по очереди при создании +нового экземпляра операции: {@link android.app.Activity#onCreate onCreate()}, {@link +android.app.Activity#onStart()} и {@link android.app.Activity#onResume()}. После завершения этой серии +обратных вызовов операция переходит в состояние возобновления, где пользователи могут +взаимодействовать с операцией до тех пор, пока не переключатся на другую операцию.</p> + + + + + + + +<h2 id="Destroy">Уничтожение операции</h2> + +<p>Первым обратным вызовом жизненного цикла операции является {@link android.app.Activity#onCreate +onCreate()}, а последним – {@link android.app.Activity#onDestroy}. Система вызывает +этот метод для операции, подавая окончательный сигнал +о том, что экземпляр операции полностью удаляется из системной памяти.</p> + +<p>Большинству приложений не требуется реализация этого метода, потому что ссылки локальных классов уничтожаются +вместе с операцией, а основные задачи по освобождению ресурсов операция выполняет в состояниях {@link +android.app.Activity#onPause} и {@link android.app.Activity#onStop}. Однако если ваша +операция содержит фоновые потоки, созданные во время выполнения {@link +android.app.Activity#onCreate onCreate()}, или в течение длительного времени использует другие ресурсы, могущие +вызывать утечку памяти при неправильном закрытии, их нужно уничтожить с помощью метода {@link +android.app.Activity#onDestroy}.</p> + +<pre> +@Override +public void onDestroy() { + super.onDestroy(); // Always call the superclass + + // Stop method tracing that the activity started during onCreate() + android.os.Debug.stopMethodTracing(); +} +</pre> + +<p class="note"><strong>Примечание.</strong> Система вызывает {@link android.app.Activity#onDestroy} +после вызова {@link android.app.Activity#onPause} и {@link +android.app.Activity#onStop} во всех случаях, кроме ситуации, когда вы вызываете {@link +android.app.Activity#finish()} из метода {@link android.app.Activity#onCreate onCreate()} +. В некоторых случаях, например когда ваша операция временно отвечает за принятие решения о запуске +другой операции, вы можете вызвать {@link android.app.Activity#finish()} из метода {@link +android.app.Activity#onCreate onCreate()} для уничтожения операции. В этом случае система +сразу же вызывает {@link android.app.Activity#onDestroy}, не вызывая другие методы жизненного +цикла.</p> diff --git a/docs/html-intl/intl/ru/training/basics/activity-lifecycle/stopping.jd b/docs/html-intl/intl/ru/training/basics/activity-lifecycle/stopping.jd new file mode 100644 index 0000000..27c771f --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/activity-lifecycle/stopping.jd @@ -0,0 +1,187 @@ +page.title=Остановка и перезапуск операции +page.tags=жизненный цикл операции +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> + <div id="tb"> + + <h2>Содержание этого урока</h2> + <ol> + <li><a href="#Stop">Остановка операции</a></li> + <li><a href="#Start">Запуск/перезапуск операции</a></li> + </ol> + + <h2>См. также:</h2> + <ul> + <li><a href="{@docRoot}guide/components/activities.html">Операции</a> + </li> + </ul> + +<h2>Попробуйте сами</h2> + +<div class="download-box"> + <a href="http://developer.android.com/shareables/training/ActivityLifecycle.zip" class="button">Загрузить демонстрацию</a> + <p class="filename">ActivityLifecycle.zip</p> +</div> + + </div> +</div> + +<p>Правильное выполнение остановки и перезапуска операции является важным шагом в жизненном +цикле операции, благодаря которому пользователи понимают, что приложение не потеряет их данные. +Ниже приведен пример нескольких основных ситуаций с остановкой и перезапуском операции.</p> + +<ul> + <li>Пользователь открывает окно "Последние приложения" и переключается из вашего приложения в другое приложение. +При этом останавливается операция вашего приложения, работающего на экране. Если пользователь возвращается в ваше +приложение, нажав значок запуска на главном экране или через окно "Последние приложения", данная операция возобновляется.</li> + <li>Пользователь выполняет в вашем приложении действие, запускающее новую операцию. Текущая операция +останавливается при создании второй операции. Если после этого пользователь нажимает кнопку <em>Назад</em> +, первая операция запускается заново.</li> + <li>Пользователь получает телефонный звонок или переключается на другое приложение во время использования вашего приложения на своем телефоне.</li> +</ul> + +<p>Класс {@link android.app.Activity} предоставляет два метода жизненного цикла, {@link +android.app.Activity#onStop()} и {@link android.app.Activity#onRestart()}, позволяющие явно +обрабатывать остановку и перезапуск операции. В отличие от состояния паузы, +означающем частичное уничтожение пользовательского интерфейса, в состоянии остановки пользовательский интерфейс больше не +отображается и пользователь переключается на отдельную операцию (или отдельное приложение).</p> + +<p class="note"><strong>Примечание.</strong> Поскольку система хранит ваш экземпляр {@link android.app.Activity} +в системной памяти при остановке, вам, возможно, вообще не потребуется реализация методов +{@link android.app.Activity#onStop()} и {@link android.app.Activity#onRestart()} (или даже {@link +android.app.Activity#onStart()}. Большинство операций относительно простые, и операция +остановится и перезапустится нормально, вам только может потребоваться {@link +android.app.Activity#onPause()} для приостановки текущих операций и отключения от системных ресурсов.</p> + +<img src="{@docRoot}images/training/basics/basic-lifecycle-stopped.png" /> +<p class="img-caption"><strong>Рисунок 1.</strong> Когда пользователь выйдет из операции, система +вызовет {@link android.app.Activity#onStop onStop()} для остановки операции (1). Если пользователь возвращается +при остановке операции, система вызывает {@link android.app.Activity#onRestart onRestart()} +(2), затем сразу же {@link android.app.Activity#onStart onStart()} (3) и {@link +android.app.Activity#onResume()} (4). Вне зависимости от причины остановки +операции, система всегда вызывает {@link android.app.Activity#onPause onPause()} перед вызовом {@link +android.app.Activity#onStop onStop()}.</p> + + + +<h2 id="Stop">Остановка операции</h2> + +<p>Когда операция получает вызов метода {@link android.app.Activity#onStop()}, +она становится невидимой и освобождает практически все ресурсы, которые не нужны ей, когда пользователь ее не +использует. После остановки операции система может уничтожить экземпляр, если ей потребуется +освободить системную память. В чрезвычайных ситуациях система может закрыть процесс приложения без +вызова последнего метода обратного вызова {@link android.app.Activity#onDestroy()} операции, и поэтому важно +использовать {@link android.app.Activity#onStop()} для высвобождения ресурсов, которые могут вызвать утечку памяти.</p> + +<p>Хотя метод {@link android.app.Activity#onPause onPause()} вызывается до +{@link android.app.Activity#onStop()}, вам следует использовать {@link android.app.Activity#onStop onStop()} +для выполнения более масштабных операций выключения с использованием процессорных ресурсов, например при записи информации в базу +данных.</p> + +<p>В качестве примера приведем реализацию {@link android.app.Activity#onStop onStop()}, +сохраняющую содержание черновой заметки в постоянное хранилище:</p> + +<!-- TODO: Find a better example for onStop, because this kind of thing should probably use a +separate thread but that's too complicated to show here. --> +<pre> +@Override +protected void onStop() { + super.onStop(); // Always call the superclass method first + + // Save the note's current draft, because the activity is stopping + // and we want to be sure the current note progress isn't lost. + ContentValues values = new ContentValues(); + values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText()); + values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle()); + + getContentResolver().update( + mUri, // The URI for the note to update. + values, // The map of column names and new values to apply to them. + null, // No SELECT criteria are used. + null // No WHERE columns are used. + ); +} +</pre> + +<p>При остановке операции объект {@link android.app.Activity} остается в памяти +и вызывается повторно при возобновлении операции. Не нужно заново инициализировать компоненты, +созданные с использованием любого из методов обратного вызова для возобновления операции. Система также +отслеживает текущее состояние каждого {@link android.view.View} в макете, и если +пользователь вводит текст в виджет {@link android.widget.EditText}, этот текст сохраняется, так что его не +нужно специально сохранять и восстанавливать.</p> + +<p class="note"><strong>Примечание.</strong> Даже если система уничтожит операцию в период остановки, +она сохранит состояние объектов {@link android.view.View} (например, текста в {@link +android.widget.EditText}) в {@link android.os.Bundle} (наборе пар "ключ-значение") и восстановит +их, если пользователь вернется в тот же экземпляр операции (на <a href="recreating.html">следующем уроке</a> мы более подробно поговорим об использовании {@link android.os.Bundle} для сохранения +других данных состояния в случае уничтожения и воссоздания вашей операции).</p> + + + +<h2 id="Start">Запуск/перезапуск операции</h2> + +<p>Когда ваша операция возвращается в экранный режим из состояния остановки, она получает вызов +{@link android.app.Activity#onRestart()}. Система также вызывает метод {@link +android.app.Activity#onStart()}, что происходит каждый раз, когда операция становится видимой +(при перезапуске или первоначальном создании). Однако метод {@link +android.app.Activity#onRestart()} вызывается только при возобновлении операции из состояния +остановки, так что его можно использовать для выполнения специальных задач восстановления, которые могут требоваться только если операция +была остановлена, но не была уничтожена.</p> + +<p>Приложениям редко требуется использовать {@link android.app.Activity#onRestart()} для восстановления +состояния операции, и поэтому для этого метода нет правил, относящихся +к обычным приложениям. Однако поскольку ваш метод {@link android.app.Activity#onStop()} +должен фактически освободить все ресурсы операции, их нужно снова выделить +при перезапуске операции. Однако их также нужно выделять при первом +создании операции (когда нет существующего экземпляра операции). По этому причине обычно +используют метод обратного вызова {@link android.app.Activity#onStart()} в дополнение +к методу {@link android.app.Activity#onStop()}, поскольку система вызывает {@link +android.app.Activity#onStart()} как при создании операции, так и при ее перезапуске +после остановки.</p> + +<p>Например, если пользователь долго не пользовался приложением, +а затем вернулся в него, метод {@link android.app.Activity#onStart()} хорошо помогает убедиться, что требуемые +системные функции включены:</p> + +<pre> +@Override +protected void onStart() { + super.onStart(); // Always call the superclass method first + + // The activity is either being restarted or started for the first time + // so this is where we should make sure that GPS is enabled + LocationManager locationManager = + (LocationManager) getSystemService(Context.LOCATION_SERVICE); + boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); + + if (!gpsEnabled) { + // Create a dialog here that requests the user to enable GPS, and use an intent + // with the android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS action + // to take the user to the Settings screen to enable GPS when they click "OK" + } +} + +@Override +protected void onRestart() { + super.onRestart(); // Always call the superclass method first + + // Activity being restarted from stopped state +} +</pre> + + + + +<p>Когда система уничтожает вашу операцию, она вызывает метод {@link android.app.Activity#onDestroy()} +для вашего {@link android.app.Activity}. Поскольку вы уже освобождаете большую часть +ресурсов с помощью {@link android.app.Activity#onStop()} к моменту вызова {@link +android.app.Activity#onDestroy()}, большинству приложений почти ничего не нужно делать. Этот метод представляет собой последний +шанс освободить ресурсы, могущие привести к утечке памяти, обеспечивая уверенность +в уничтожении дополнительных потоков и других долгосрочных действий, например, отслеживания +методов.</p> + diff --git a/docs/html-intl/intl/ru/training/basics/data-storage/databases.jd b/docs/html-intl/intl/ru/training/basics/data-storage/databases.jd new file mode 100644 index 0000000..418d288 --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/data-storage/databases.jd @@ -0,0 +1,317 @@ +page.title=Сохранение данных в базах данных SQL +page.tags=хранение данных +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>Содержание этого урока</h2> +<ol> + <li><a href="#DefineContract">Определение схемы и контракта</a></li> + <li><a href="#DbHelper">Создание базы данных с помощью SQL Helper</a></li> + <li><a href="#WriteDbRow">Размещение информации в базе данных</a></li> + <li><a href="#ReadDbRow">Чтение информации из базы данных</a></li> + <li><a href="#DeleteDbRow">Удаление информации из базы данных</a></li> + <li><a href="#UpdateDbRow">Обновление базы данных</a></li> +</ol> + +<h2>См. также:</h2> +<ul> + <li><a href="{@docRoot}guide/topics/data/data-storage.html#db">Использование баз данных</a></li> +</ul> + +<!-- +<h2>Try it out</h2> + +<div class="download-box"> + <a href="{@docRoot}shareables/training/Sample.zip" class="button">Download the sample</a> + <p class="filename">Sample.zip</p> +</div> +--> + +</div> +</div> + + +<p>Сохранение данных в базе идеально подходит для повторяющихся и структурированных данных, +таких как контактная информация. В этом учебном курсе предполагается, что вы +владеете общими знаниями о базах данных SQL, и он поможет вам начать работать с базами данных +SQLite на платформе Android. API-интерфейсы, необходимые для использования базы данных +на платформе Android, доступны в составе пакета {@link android.database.sqlite}.</p> + + +<h2 id="DefineContract">Определение схемы и контракта</h2> + +<p>Одним из основных элементов баз данных SQL является схема, которая представляет собой формальную +декларацию способа организации базы данных. Схема отражается в выражениях SQL, +используемых для создания базы данных. Для вас может оказаться полезным +создать сопутствующий класс (<em>класс-контракт</em>), явно указывающий структуру +схемы систематическим и самодокументирующим способом.</p> + +<p>Класс-контракт представляет собой контейнер, определяющий имена для URI-адресов, +таблиц и столбцов. Класс-контракт позволяет использовать одни и те же постоянные значения +во всех других классах этого же пакета. Таким образом, при изменении имени +столбца в одном месте это изменение применяется во всем коде.</p> + +<p>Для удобства организации класс-контракта стоит поместить +глобальные определения базы данных на корневой уровень класса. Затем нужно создать внутренний +класс для каждой таблицы, производящей нумерацию столбцов.</p> + +<p class="note"><strong>Примечание.</strong> За счет реализации интерфейса {@link +android.provider.BaseColumns} внутренний класс может наследовать поле первичного +ключа {@code _ID}, наличия которого ожидают от него некоторые +классы Android (например, адаптеры курсора). Это не является обязательным условием, однако может помочь обеспечить гармоничную работу +вашей базы данных в инфраструктуре Android.</p> + +<p>Например, в этом фрагменте кода определяются имя таблицы и имена столбцов для +одной таблицы:</p> + + +<pre> +public final class FeedReaderContract { + // To prevent someone from accidentally instantiating the contract class, + // give it an empty constructor. + public FeedReaderContract() {} + + /* Inner class that defines the table contents */ + public static abstract class FeedEntry implements BaseColumns { + public static final String TABLE_NAME = "entry"; + public static final String COLUMN_NAME_ENTRY_ID = "entryid"; + public static final String COLUMN_NAME_TITLE = "title"; + public static final String COLUMN_NAME_SUBTITLE = "subtitle"; + ... + } +} +</pre> + + + +<h2 id="DbHelper">Создание базы данных с помощью SQL Helper</h2> + +<p>После определения внешнего вида базы данных следует реализовать методы +создания и обслуживания базы данных и таблиц. Вот некоторые типичные +выражения для создания и удаления таблиц:</P> + +<pre> +private static final String TEXT_TYPE = " TEXT"; +private static final String COMMA_SEP = ","; +private static final String SQL_CREATE_ENTRIES = + "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" + + FeedEntry._ID + " INTEGER PRIMARY KEY," + + FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP + + FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP + + ... // Any other options for the CREATE command + " )"; + +private static final String SQL_DELETE_ENTRIES = + "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME; +</pre> + +<p>Как и при сохранении файлов во <a href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">внутренней +памяти</a> устройства, Android сохраняет вашу базу данных в закрытой области диска, связанной +с приложением Эти данные защищены, потому что эта область +по умолчанию недоступна другим приложениям.</p> + +<p>Полезный набор API-интерфейсов содержится в классе {@link +android.database.sqlite.SQLiteOpenHelper}. +Если вы используете этот класс для получения ссылок на свою базу данных, система +выполняет потенциально +долговременные операции создания и обновления базы данных только тогда, когда это +необходимо, а <em>не при запуске приложения</em>. Для этого нужно использовать вызов +{@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} или +{@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase}.</p> + +<p class="note"><strong>Примечание.</strong> Поскольку операции могут выполняться длительное время, +вызывайте {@link +android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} или {@link +android.database.sqlite.SQLiteOpenHelper#getReadableDatabase} в фоновом потоке, +например с {@link android.os.AsyncTask} или {@link android.app.IntentService}.</p> + +<p>Для использования {@link android.database.sqlite.SQLiteOpenHelper} создайте подкласс, заменяющий методы +вызова {@link +android.database.sqlite.SQLiteOpenHelper#onCreate onCreate()}, {@link +android.database.sqlite.SQLiteOpenHelper#onUpgrade onUpgrade()} и {@link +android.database.sqlite.SQLiteOpenHelper#onOpen onOpen()}. Также вы можете +использовать {@link android.database.sqlite.SQLiteOpenHelper#onDowngrade onDowngrade()}, +но это не требуется.</p> + +<p>Например, вот реализация {@link +android.database.sqlite.SQLiteOpenHelper}, при которой используются некоторые из приведенных выше команд:</p> + +<pre> +public class FeedReaderDbHelper extends SQLiteOpenHelper { + // If you change the database schema, you must increment the database version. + public static final int DATABASE_VERSION = 1; + public static final String DATABASE_NAME = "FeedReader.db"; + + public FeedReaderDbHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + public void onCreate(SQLiteDatabase db) { + db.execSQL(SQL_CREATE_ENTRIES); + } + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + // This database is only a cache for online data, so its upgrade policy is + // to simply to discard the data and start over + db.execSQL(SQL_DELETE_ENTRIES); + onCreate(db); + } + public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { + onUpgrade(db, oldVersion, newVersion); + } +} +</pre> + +<p>Для получения доступа к базе данных создайте экземпляр подкласса {@link +android.database.sqlite.SQLiteOpenHelper}:</p> + +<pre> +FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext()); +</pre> + + + + +<h2 id="WriteDbRow">Размещение информации в базе данных</h2> + +<p>Добавьте данные в базу данных, передав объект {@link android.content.ContentValues} +в метод {@link android.database.sqlite.SQLiteDatabase#insert insert()}:</p> + +<pre> +// Gets the data repository in write mode +SQLiteDatabase db = mDbHelper.getWritableDatabase(); + +// Create a new map of values, where column names are the keys +ContentValues values = new ContentValues(); +values.put(FeedEntry.COLUMN_NAME_ENTRY_ID, id); +values.put(FeedEntry.COLUMN_NAME_TITLE, title); +values.put(FeedEntry.COLUMN_NAME_CONTENT, content); + +// Insert the new row, returning the primary key value of the new row +long newRowId; +newRowId = db.insert( + FeedEntry.TABLE_NAME, + FeedEntry.COLUMN_NAME_NULLABLE, + values); +</pre> + +<p>Первый аргумент {@link android.database.sqlite.SQLiteDatabase#insert insert()} +представляет собой просто название таблицы. Второй аргумент указывает +имя столбца, в который инфраструктура вставляет значение NULL, если +{@link android.content.ContentValues} является пустым (если вместо этого указать {@code "null"}, +то инфраструктура не будет вставлять строку в случае отсутствия значений).</p> + + + + +<h2 id="ReadDbRow">Чтение информации из базы данных</h2> + +<p>Для чтения из базы данных используйте метод {@link android.database.sqlite.SQLiteDatabase#query query()} +с передачей критериев выделения и желаемых столбцов. +Метод сочетает элементы {@link android.database.sqlite.SQLiteDatabase#insert insert()} +и {@link android.database.sqlite.SQLiteDatabase#update update()}, за исключением того, что список столбцов +определяет данные, которые вы хотите получить, а не данные для вставки. Результаты запроса +возвращаются в объекте {@link android.database.Cursor}.</p> + +<pre> +SQLiteDatabase db = mDbHelper.getReadableDatabase(); + +// Define a <em>projection</em> that specifies which columns from the database +// you will actually use after this query. +String[] projection = { + FeedEntry._ID, + FeedEntry.COLUMN_NAME_TITLE, + FeedEntry.COLUMN_NAME_UPDATED, + ... + }; + +// How you want the results sorted in the resulting Cursor +String sortOrder = + FeedEntry.COLUMN_NAME_UPDATED + " DESC"; + +Cursor c = db.query( + FeedEntry.TABLE_NAME, // The table to query + projection, // The columns to return + selection, // The columns for the WHERE clause + selectionArgs, // The values for the WHERE clause + null, // don't group the rows + null, // don't filter by row groups + sortOrder // The sort order + ); +</pre> + +<p>Чтобы посмотреть на строку в месте курсора, используйте один из методов перемещения +{@link android.database.Cursor}, которые всегда нужно вызывать перед считыванием значений. Обычно следует начинать +с вызова {@link android.database.Cursor#moveToFirst}, который помещает "позицию чтения" +на первую запись в результатах. Для каждой строки значение столбца можно прочитать, вызвав один из методов +{@link android.database.Cursor} get, например {@link android.database.Cursor#getString +getString()} или {@link android.database.Cursor#getLong getLong()}. Для каждого из методов get +вы должны передать указатель желаемого столбца, который может вызвать +{@link android.database.Cursor#getColumnIndex getColumnIndex()} или +{@link android.database.Cursor#getColumnIndexOrThrow getColumnIndexOrThrow()}. +Например:</p> + +<pre> +cursor.moveToFirst(); +long itemId = cursor.getLong( + cursor.getColumnIndexOrThrow(FeedEntry._ID) +); +</pre> + + + + +<h2 id="DeleteDbRow">Удаление информации из базы данных</h2> + +<p>Для удаления строк из таблицы нужно указать критерии выделения, +идентифицирующие строки. API-интерфейс базы данных обеспечивает механизм для создания критериев +выделения, предоставляющий защиту от внедрения SQL-кода. Механизм делит спецификацию выбора +на предложение выбора и аргументы выбора. Предложение +определяет столбцы для рассмотрения, а также позволяет объединять операции тестирования +столбцов. Аргументы представляют собой значения для тестирования, которые привязаны к пункту. +Поскольку результат обрабатывается не как обычные выражения SQL, +он защищен от внедрения SQL-кода.</p> + +<pre> +// Define 'where' part of query. +String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?"; +// Specify arguments in placeholder order. +String[] selectionArgs = { String.valueOf(rowId) }; +// Issue SQL statement. +db.delete(table_name, selection, selectionArgs); +</pre> + + + +<h2 id="UpdateDbRow">Обновление базы данных</h2> + +<p>При необходимости изменить набор значений базы данных используйте метод {@link +android.database.sqlite.SQLiteDatabase#update update()}.</p> + +<p>Обновление таблицы сочетает значения синтаксиса {@link +android.database.sqlite.SQLiteDatabase#insert insert()} и синтаксиса {@code where} +для {@link android.database.sqlite.SQLiteDatabase#delete delete()}.</p> + +<pre> +SQLiteDatabase db = mDbHelper.getReadableDatabase(); + +// New value for one column +ContentValues values = new ContentValues(); +values.put(FeedEntry.COLUMN_NAME_TITLE, title); + +// Which row to update, based on the ID +String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?"; +String[] selectionArgs = { String.valueOf(rowId) }; + +int count = db.update( + FeedReaderDbHelper.FeedEntry.TABLE_NAME, + values, + selection, + selectionArgs); +</pre> + diff --git a/docs/html-intl/intl/ru/training/basics/data-storage/files.jd b/docs/html-intl/intl/ru/training/basics/data-storage/files.jd new file mode 100644 index 0000000..2afecea --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/data-storage/files.jd @@ -0,0 +1,379 @@ +page.title=Сохранение файлов +page.tags=хранение данных +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>Содержание этого урока</h2> +<ol> + <li><a href="#InternalVsExternalStorage">Выбор внутреннего или внешнего хранилища</a></li> + <li><a href="#GetWritePermission">Получение разрешений на доступ к внешнему хранилищу</a></li> + <li><a href="#WriteInternalStorage">Сохранение файла во внутреннем хранилище</a></li> + <li><a href="#WriteExternalStorage">Сохранение файла во внешнем хранилище</a></li> + <li><a href="#GetFreeSpace">Запрос свободного пространства</a></li> + <li><a href="#DeleteFile">Удаление файла</a></li> +</ol> + +<h2>См. также:</h2> +<ul> + <li><a href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">Использование внутреннего +хранилища</a></li> + <li><a href="{@docRoot}guide/topics/data/data-storage.html#filesExternal">Использование внешнего +хранилища</a></li> +</ul> + +</div> +</div> + +<p>Операционная система Android использует файловую систему, +похожую на дисковые файловые системы других платформ. В этом уроке рассказывается, +как работать с файловой системой Android для чтения и записи файлов в {@link java.io.File} +API-интерфейсах.</p> + +<p>Объект {@link java.io.File} подходит для чтения или записи больших объемов данных в порядке +от начала к концу без пропусков. Например, этот способ оптимален для изображений или +любых других файлов, передаваемых по сети.</p> + +<p>В этом уроке вы узнаете, как выполнять в приложении базовые задачи, связанные с файлами. +Для прохождения урока вы должны быть знакомы с основами файловой системы Linux +и стандартными файловыми API-интерфейсами ввода/вывода в {@link java.io}.</p> + + +<h2 id="InternalVsExternalStorage">Выбор внутреннего или внешнего хранилища</h2> + +<p>Все устройства Android имеют две области хранения файлов: внутренняя память и внешние хранилища. Эти области +появились в первые годы существования Android, когда на большинстве устройств имелись встроенная память +(внутреннее хранилище) и карты памяти (например micro SD, внешнее хранилище). +Некоторые устройства делят встроенную память на внутренний и внешний разделы, +так что даже без съемных носителей в системе две области хранения файлов, +и API-интерфейс работает одинаково вне зависимости от типа внешнего хранилища. +Ниже подробно описаны обе области хранения файлов.</p> + +<div class="col-5" style="margin-left:0"> +<p><b>Внутренняя память</b></p> +<ul> +<li>Всегда доступна.</li> +<li>Сохраненные здесь файлы по умолчанию доступны только вашему приложению.</li> +<li>При удалении пользователем вашего приложения система Android удаляет из внутренней памяти +все файлы этого приложения.</li> +</ul> +<p>Внутренняя память лучше всего подходит для ситуаций, когда вы хотите быть уверены, что ни пользователь, ни другие приложения не смогут получить +доступ к вашим файлам.</p> +</div> + +<div class="col-7" style="margin-right:0"> +<p><b>Внешнее хранилище</b></p> +<ul> +<li>Доступно не всегда, потому что пользователь может в любое время подключать и отключать такие +хранилища, например, USB-накопители.</li> +<li>Такие хранилища доступны для чтения везде, +поэтому вы не контролируете чтение сохраненных в них данных.</li> +<li>При удалении пользователем вашего приложения система Android удаляет из внешних хранилищ +файлы этого приложения, только если они сохраняются в директории из {@link android.content.Context#getExternalFilesDir +getExternalFilesDir()}.</li> +</ul> +<p>Внешнее хранилище лучше всего +подходит для файлов без ограничений доступа и для файлов, которые вы хотите сделать +доступными другим приложениям или пользователю через компьютер.</p> +</div> + + +<p class="note" style="clear:both"> +<strong>Совет.</strong> Хотя по умолчанию приложения устанавливаются во внутреннюю память, +вы можете указать в манифесте атрибут <a href="{@docRoot}guide/topics/manifest/manifest-element.html#install">{@code +android:installLocation}</a>, чтобы приложение можно +было установить во внешнее хранилище. Этот вариант будет полезен пользователям при большом размере файла APK, либо если у них есть доступ +к внешнему хранилищу, объем которого превышает внутреннюю память. Дополнительную информацию +см. в разделе <a href="{@docRoot}guide/topics/data/install-location.html">Место установки приложения</a>.</p> + + +<h2 id="GetWritePermission">Получение разрешений доступа к внешнему хранилищу</h2> + +<p>Для записи во внешнее хранилище следует указать запрос +разрешения {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} в <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">файле манифеста</a>:</p> + +<pre> +<manifest ...> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + ... +</manifest> +</pre> + +<div class="caution"><p><strong>Внимание!</strong> +В настоящее время все приложения могут считывать данные из внешних +хранилищ без специального разрешения. Однако в новой версии эта ситуация будет изменена. Если вашему приложению потребуется +считать данные из внешнего хранилища (но не записать туда данные), вам необходимо будет декларировать разрешение {@link +android.Manifest.permission#READ_EXTERNAL_STORAGE}. Чтобы обеспечить дальнейшую работу вашего приложения +ожидаемым образом, вы должны сразу декларировать это разрешение до того, как изменения вступят в силу.</p> +<pre> +<manifest ...> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + ... +</manifest> +</pre> +<p>Однако если ваше приложение использует разрешение {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}, +оно косвенно получает разрешение на чтение данных из внешнего хранилища.</p> +</div> + +<p>Для сохранения файлов во внутреннем хранилище +не требуется никаких разрешений. У вашего приложения всегда будет разрешение на чтение и запись +файлов в его каталог внутренней памяти.</p> + + + + + +<h2 id="WriteInternalStorage">Сохранение файла во внутренней памяти</h2> + +<p>При сохранении файла во внутреннюю память вы можете получить соответствующую директорию в виде +{@link java.io.File}, вызвав один из двух методов:</p> + +<dl> + <dt>{@link android.content.Context#getFilesDir}</dt> + <dd>Возвращает {@link java.io.File}, соответствующий внутренней директории приложения.</dd> + <dt>{@link android.content.Context#getCacheDir}</dt> + <dd>Возвращает {@link java.io.File}, соответствующий внутренней директории файлов временной +кэш-памяти приложения. Обязательно удаляйте каждый файл, когда он перестанет +быть нужным, и устанавливайте разумное ограничение размера памяти, используемой в каждый момент времени, +например, 1 МБ. Если системе будет не хватать места в хранилище, она может удалять файлы +из кэш-памяти без уведомления.</dd> +</dl> + +<p>Для создания файла в одной из этих директорий можно использовать конструктор {@link +java.io.File#File(File,String) File()}, который передает элемент {@link java.io.File}, предоставляемый одним из вышеприведенных +методов, с помощью которого указывается директория во внутренней памяти. Например:</p> + +<pre> +File file = new File(context.getFilesDir(), filename); +</pre> + +<p>Кроме того, можно вызвать метод {@link +android.content.Context#openFileOutput openFileOutput()} для получения объекта {@link java.io.FileOutputStream} +, производящего запись в файл во внутренней памяти. Вот пример +записи текста в файл:</p> + +<pre> +String filename = "myfile"; +String string = "Hello world!"; +FileOutputStream outputStream; + +try { + outputStream = openFileOutput(filename, Context.MODE_PRIVATE); + outputStream.write(string.getBytes()); + outputStream.close(); +} catch (Exception e) { + e.printStackTrace(); +} +</pre> + +<p>Если вам потребуется кэшировать какие-то файлы, используйте {@link +java.io.File#createTempFile createTempFile()}. Например, следующий метод извлекает +имя файла из {@link java.net.URL} и создает файл с этим именем +в каталоге внутренней кэш-памяти вашего приложения:</p> + +<pre> +public File getTempFile(Context context, String url) { + File file; + try { + String fileName = Uri.parse(url).getLastPathSegment(); + file = File.createTempFile(fileName, null, context.getCacheDir()); + catch (IOException e) { + // Error while creating file + } + return file; +} +</pre> + +<p class="note"><strong>Примечание.</strong> +Каталог вашего приложения во внутренней памяти указывается +с использованием имени пакета приложения в определенном месте файловой системы Android. +Технически другое приложение может прочитать ваши файлы во внутренней памяти, если вы установите для файлов +режим Readable (доступно для чтения). Однако для этого другому приложению должны быть известны имя пакета вашего +приложения и имена файлов. Другие приложения не могут просматривать внутренние каталоги вашего приложения и не имеют разрешений на +чтение или запись, если вы специально не установите для своих файлов режим Readable (доступно для чтения) или Writable (доступно для записи). Следовательно, пока +вы будете использовать режим {@link android.content.Context#MODE_PRIVATE} для своих файлов во внутренней памяти, +они будут недоступны другим приложениям.</p> + + + + + +<h2 id="WriteExternalStorage">Сохранение файла во внешнем хранилище</h2> + +<p>Поскольку внешнее хранилище может быть недоступно— например, если пользователь установил +хранилище в гнездо на компьютере или извлек из устройства SD-карту, — перед доступом к тому всегда следует +проверять его доступность. Состояние внешнего +хранилища можно узнать, если вызвать {@link android.os.Environment#getExternalStorageState}. Если возвращаемое состояние +равнозначно {@link android.os.Environment#MEDIA_MOUNTED}, вы сможете выполнять с файлами операции чтения и +записи. Например, следующие методы будут полезными для определения доступности +хранилища данных:</p> + +<pre> +/* Checks if external storage is available for read and write */ +public boolean isExternalStorageWritable() { + String state = Environment.getExternalStorageState(); + if (Environment.MEDIA_MOUNTED.equals(state)) { + return true; + } + return false; +} + +/* Checks if external storage is available to at least read */ +public boolean isExternalStorageReadable() { + String state = Environment.getExternalStorageState(); + if (Environment.MEDIA_MOUNTED.equals(state) || + Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { + return true; + } + return false; +} +</pre> + +<p>Хотя внешнее хранилище может быть изменено пользователем и другими приложениями, существует две +категории файлов, которые в нем можно сохранять:</p> + +<dl> + <dt>Общедоступные файлы</dt> + <dd>Файлы, которые +должны быть доступны другим приложениям и пользователю. Когда пользователь удаляет ваше приложение, +эти файлы должны оставаться доступны пользователю. + <p>Например, в эту категорию входят снимки, сделанные с помощью вашего приложения, а также другие загружаемые файлы.</p> + </dd> + <dt>Личные файлы</dt> + <dd>Это файлы, принадлежащие вашему приложению. Они должны удаляться при удалении +вашего приложения пользователем. Хотя технически эти файлы доступны для пользователя и других приложений, поскольку находятся +во внешнем хранилище, они не имеют никакой ценности для пользователей +вне вашего приложения. Когда пользователь удаляет ваше приложение, система удаляет +все файлы из каталога закрытых файлов вашего приложения во внешнем хранилище. + <p>Например, к этой категории относятся дополнительные ресурсы, загруженные приложением, и временные мультимедийные файлы.</p> + </dd> +</dl> + +<p>Если вы хотите сохранить публичные файлы во внешнем хранилище, используйте методы +{@link android.os.Environment#getExternalStoragePublicDirectory +getExternalStoragePublicDirectory()} для получения {@link java.io.File}, отражающего +соответствующий каталог во внешнем хранилище. Этот метод принимает аргумент, указывающий +тип сохраняемого файла и позволяющий включить его в логическую структуру +с другими публичными файлами, например, {@link android.os.Environment#DIRECTORY_MUSIC} или {@link +android.os.Environment#DIRECTORY_PICTURES}. Например:</p> + +<pre> +public File getAlbumStorageDir(String albumName) { + // Get the directory for the user's public pictures directory. + File file = new File(Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_PICTURES), albumName); + if (!file.mkdirs()) { + Log.e(LOG_TAG, "Directory not created"); + } + return file; +} +</pre> + + +<p>Если вы хотите сохранить личные файлы вашего приложения, вы можете получить +соответствующий каталог посредством, вызвав метод {@link +android.content.Context#getExternalFilesDir getExternalFilesDir()} и предоставив ему имя с указанием +желаемого типа каталога. Каждый каталог, создаваемый таким способом, добавляется в родительский +каталог, в котором объединены все файлы вашего приложения во внешнем хранилище. При удалении вашего приложения +пользователем система удаляет этот каталог.</p> + +<p>Например, следующий метод позволит вам создать каталог для персонального фотоальбома:</p> + +<pre> +public File getAlbumStorageDir(Context context, String albumName) { + // Get the directory for the app's private pictures directory. + File file = new File(context.getExternalFilesDir( + Environment.DIRECTORY_PICTURES), albumName); + if (!file.mkdirs()) { + Log.e(LOG_TAG, "Directory not created"); + } + return file; +} +</pre> + +<p>Если ни одно из готовых имен подкаталогов не подходит для ваших файлов, вы можете вызвать {@link +android.content.Context#getExternalFilesDir getExternalFilesDir()} и передать аргумент {@code null}. При этом +будет возвращен корневой каталог закрытого каталога вашего приложения во внешнем хранилище.</p> + +<p>Следует помнить, что {@link android.content.Context#getExternalFilesDir getExternalFilesDir()} +создает каталог внутри каталога, который удаляется при удалении вашего приложения пользователем. +Если сохраняемые вами файлы должны оставаться доступными после удаления вашего +приложения пользователем — например, если ваше приложение работает с камерой, а пользователь хочет сохранить снимки, — вам +следует использовать {@link android.os.Environment#getExternalStoragePublicDirectory +getExternalStoragePublicDirectory()}.</p> + + +<p>Вне зависимости от того, используете ли вы {@link +android.os.Environment#getExternalStoragePublicDirectory +getExternalStoragePublicDirectory()} для общих файлов или + link android.content.Context#getExternalFilesDir +getExternalFilesDir()} для собственных файлов приложения, вы должны использовать имена каталогов, предоставляемые +постоянными значениями API-интерфейсов, например, +{@link android.os.Environment#DIRECTORY_PICTURES}. Эти имена каталогов обеспечивают +правильную обработку файлов системой. Например, сохраненные в {@link +android.os.Environment#DIRECTORY_RINGTONES} файлы относятся медиа-сканером системы в категорию рингтонов, +а не музыки.</p> + + + + +<h2 id="GetFreeSpace">Запрос доступного пространства</h2> + +<p>Если вам заранее известен объем сохраняемых данных, вы можете +определить наличие достаточного пространства без исключения {@link +java.io.IOException}, вызвав метод {@link java.io.File#getFreeSpace} или {@link +java.io.File#getTotalSpace}. Эти методы позволяют узнать текущее доступное пространство и +общее пространство в хранилище. Эта информация также позволять +избежать переполнения объема хранилища сверх определенного уровня.</p> + +<p>Однако система не гарантирует возможность записи такого же количества байт, как указано +в {@link java.io.File#getFreeSpace}. Если выводимое число на +несколько мегабайт превышает размер данных, которые вы хотите сохранить, или если файловая система заполнена +менее, чем на 90%, дальше можно действовать спокойно. +В противном случае запись в хранилище осуществлять нежелательно.</p> + +<p class="note"><strong>Примечание.</strong> Вам необязательно проверять объем доступного места +перед сохранением файла. Вы можете попробовать сразу записать файл, а затем +определить событие {@link java.io.IOException}, если оно возникнет. Это может потребоваться, +если вы точно не знаете, сколько нужно свободного места. Например, если вы +меняете кодировку файла перед сохранением при конвертации изображения PNG в формат +JPEG, вы не будете знать размер файла заранее.</p> + + + + +<h2 id="DeleteFile">Удаление файла</h2> + +<p>Ненужные файлы всегда следует удалять. Наиболее простой способ удалить +файл – вызвать в открытом файле ссылку {@link java.io.File#delete} на сам этот файл.</p> + +<pre> +myFile.delete(); +</pre> + +<p>Если файл сохранен во внутреннем хранилище, вы также можете запросить {@link android.content.Context}, чтобы найти и +удалить файл посредством вызова {@link android.content.Context#deleteFile deleteFile()}:</p> + +<pre> +myContext.deleteFile(fileName); +</pre> + +<div class="note"> +<p><strong>Примечание.</strong> При удалении пользователем вашего приложения система Android удаляет +следующие элементы:</p> +<ul> +<li>Все файлы, сохраненные во внутреннем хранилище</li> +<li>Все файлы, сохраненные во внешнем хранилище с использованием {@link +android.content.Context#getExternalFilesDir getExternalFilesDir()}.</li> +</ul> +<p>Однако вам следует регулярно вручную очищать кэш-память, чтобы удалить файлы, созданные с помощью +{@link android.content.Context#getCacheDir()}, а также удалять любые +другие ненужные файлы.</p> +</div> + diff --git a/docs/html-intl/intl/ru/training/basics/data-storage/index.jd b/docs/html-intl/intl/ru/training/basics/data-storage/index.jd new file mode 100644 index 0000000..064239b --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/data-storage/index.jd @@ -0,0 +1,57 @@ +page.title=Сохранение данных +page.tags=хранение данных,файлы,sql,база данных,настройки +helpoutsWidget=true + +trainingnavtop=true +startpage=true + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>Необходимые знания и компоненты</h2> +<ul> + <li>Android 1.6 (уровень API 4) или более поздняя версия</li> + <li>Знакомство с коллекциями значений ключей Map</li> + <li>Знакомство с файловым API-интерфейсом ввода/вывода Java</li> + <li>Знакомство с базами данных SQL</li> +</ul> + +<h2>См. также:</h2> +<ul> + <li><a href="{@docRoot}guide/topics/data/data-storage.html">Варианты хранения</a></li> +</ul> + +</div> +</div> + +<p>Большинству приложений Android необходимо сохранять данные (даже если это требуется только для сохранения информации о состоянии приложения) +во время использования {@link android.app.Activity#onPause onPause()}, чтобы текущий прогресс пользователя не был утрачен. Большинству +приложений, за исключением самых простых, также требуется сохранять настройки пользователя, а некоторым приложениям также требуется управление большими +объемами информации в файлах и базах данных. В этом учебном курсе вы познакомитесь +с основными вариантами хранения данных в Android, включая следующие:</p> + +<ul> + <li>Сохранение пар "ключ-значение" для простых типов данных в общем +файле настроек.</li> + <li>Сохранение произвольных файлов в файловой системе Android</li> + <li>Использование баз данных под управлением SQLite</li> +</ul> + + +<h2>Уроки</h2> + +<dl> + <dt><b><a href="shared-preferences.html">Сохранение наборов "ключ-значение"</a></b></dt> + <dd>В этом уроке вы узнаете, как использовать общий файл настроек для хранения небольших объемов информации +в парах "ключ-значение".</dd> + + <dt><b><a href="files.html">Сохранение файлов</a></b></dt> + <dd>Узнайте, как сохранять простые файлы, например, для хранения длинных последовательностей данных, которые +обычно читаются по порядку.</dd> + + <dt><b><a href="databases.html">Сохранение данных в базах данных SQL</a></b></dt> + <dd>Этот урок содержит информацию об использовании баз данных SQLite для чтения и записи структурированных данных.</dd> + +</dl> diff --git a/docs/html-intl/intl/ru/training/basics/data-storage/shared-preferences.jd b/docs/html-intl/intl/ru/training/basics/data-storage/shared-preferences.jd new file mode 100644 index 0000000..61a0037 --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/data-storage/shared-preferences.jd @@ -0,0 +1,120 @@ +page.title=Сохранение наборов "ключ-значение" +page.tags=хранение данных +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>Содержание этого урока</h2> +<ol> + <li><a href="#GetSharedPreferences">Получение средства обработки SharedPreferences</a></li> + <li><a href="#WriteSharedPreference">Запись в общие настройки</a></li> + <li><a href="#ReadSharedPreference">Чтение общих настроек</a></li> +</ol> + +<h2>См. также:</h2> +<ul> + <li><a href="{@docRoot}guide/topics/data/data-storage.html#pref">Использование общих настроек</a></li> +</ul> + +</div> +</div> + + +<p>Если вы хотите сохранить относительно небольшой набор пар "ключ-значение", +используйте {@link android.content.SharedPreferences} API-интерфейсы. +Объект {@link android.content.SharedPreferences} указывает на файл, +содержащий пары "ключ-значение", и предоставляет простые методы для чтения и записи. Управление каждым +файлом {@link android.content.SharedPreferences} осуществляется +с помощью инфраструктуры и может быть частным или общим.</p> + +<p>На этом уроке вы узнаете, как использовать API-интерфейсы {@link android.content.SharedPreferences} для сохранения и +получения простых значений.</p> + +<p class="note"><strong>Примечание.</strong> API-интерфейсы {@link android.content.SharedPreferences} предназначены +только для чтения и записи пар "ключ-значение", и их не следует путать с API-интерфейсами +{@link android.preference.Preference}, которые помогают создать пользовательский интерфейс +для настроек приложения (хотя они используют {@link android.content.SharedPreferences} в качестве своей +реализации для сохранения настроек приложения). Информацию об использовании API-интерфейсов {@link +android.preference.Preference} см. в руководстве <a href="{@docRoot}guide/topics/ui/settings.html">Настройки</a>.</p> + +<h2 id="GetSharedPreferences">Получение средства обработки SharedPreferences</h2> + +<p>Чтобы создать новый файл общих настроек или получить доступ к существующему, +нужно вызвать один из двух методов:</p> +<ul> + <li>{@link android.content.Context#getSharedPreferences(String,int) +getSharedPreferences()} — Используйте этот метод, если вам потребуется несколько общих файлов настроек, идентифицируемых +по имени, указанном в первом параметре. Этот метод можно вызвать из любого +{@link android.content.Context} в вашем приложении.</li> + <li>{@link android.app.Activity#getPreferences(int) getPreferences()} — Используйте метод из +{@link android.app.Activity}, если вам нужен +только один файл общих настроек для операции. Поскольку при этом получается файл общих +настроек по умолчанию, относящийся к операции, вам не нужно указывать имя.</li> +</ul> + +<p>Например, следующий код выполняется в {@link android.app.Fragment}. +Он получает доступ к файлу общих настроек, +идентифицируемому по строке ресурсов {@code R.string.preference_file_key} и открывает его, используя +закрытый режим так, что данный файл доступен только вашему приложению.</p> + +<pre> +Context context = getActivity(); +SharedPreferences sharedPref = context.getSharedPreferences( + getString(R.string.preference_file_key), Context.MODE_PRIVATE); +</pre> + +<p>Файлу общих настроек следует присваивать имя, которое ваше приложение может уникально +идентифицировать, например {@code "com.example.myapp.PREFERENCE_FILE_KEY"}.</p> + +<p>Если для вашей операции нужен только один файл общих настроек, вы можете использовать метод +{@link android.app.Activity#getPreferences(int) getPreferences()}:</p> + +<pre> +SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE); +</pre> + +<p class="caution"><strong>Внимание!</strong> Если вы создадите общий файл настроек +с {@link android.content.Context#MODE_WORLD_READABLE} или {@link +android.content.Context#MODE_WORLD_WRITEABLE}, ваши данные будут доступны всем другим приложениям, которым известен идентификатор +файла.</p> + + +<h2 id="WriteSharedPreference">Записать в общие настройки</h2> + +<p>Для записи в файл общих настроек создайте объект {@link +android.content.SharedPreferences.Editor} посредством вызова {@link +android.content.SharedPreferences#edit} в {@link android.content.SharedPreferences}.</p> + +<p>Передайте ключи и значения, которые хотите записать, с помощью таких методов, как {@link +android.content.SharedPreferences.Editor#putInt putInt()} и {@link +android.content.SharedPreferences.Editor#putString putString()}. Затем вызовите {@link +android.content.SharedPreferences.Editor#commit} для сохранения изменений. Например:</p> + +<pre> +SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE); +SharedPreferences.Editor editor = sharedPref.edit(); +editor.putInt(getString(R.string.saved_high_score), newHighScore); +editor.commit(); +</pre> + + +<h2 id="ReadSharedPreference">Чтение общих настроек</h2> + +<p>Для получения значений из файла общих настроек следует вызвать такие, методы как {@link +android.content.SharedPreferences#getInt getInt()} и {@link +android.content.SharedPreferences#getString getString()}, предоставляя ключ для нужного +вам значения, а также при желании значение по умолчанию, которое будет выводиться при отсутствии +ключа. Например:</p> + +<pre> +SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE); +int defaultValue = getResources().getInteger(R.string.saved_high_score_default); +long highScore = sharedPref.getInt(getString(R.string.saved_high_score), defaultValue); +</pre> + diff --git a/docs/html-intl/intl/ru/training/basics/intents/filters.jd b/docs/html-intl/intl/ru/training/basics/intents/filters.jd new file mode 100644 index 0000000..0f5bb31 --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/intents/filters.jd @@ -0,0 +1,236 @@ +page.title=Разрешение другим приложениям на запуск вашей операции +page.tags=намерения +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> + <div id="tb"> + +<h2>Содержание этого урока</h2> +<ol> + <li><a href="#AddIntentFilter">Добавление фильтра Intent</a></li> + <li><a href="#HandleIntent">Обработка объекта Intent в операции</a></li> + <li><a href="#ReturnResult">Возврат результата</a></li> +</ol> + +<h2>См. также:</h2> +<ul> + <li><a href="{@docRoot}training/sharing/index.html">Общий доступ к простым данным</a></li> + <li><a href="{@docRoot}training/secure-file-sharing/index.html">Общий доступ к файлам</a> +</ul> + </div> +</div> + +<p>Предыдущие два урока были посвящены одной теме – запуску операции вашего приложения +в другом приложении. Однако если ваше приложение может выполнять операции, полезные другим приложениям, +его нужно подготовить так, чтобы оно реагировало на запросы других приложений. Представьте, что вы разработали +приложение для социальных сетей, позволяющее отправлять сообщения и фотографии друзьям пользователя. В этом случае в ваших интересах +будет поддерживать объекты Intent (намерения) {@link android.content.Intent#ACTION_SEND}, чтобы пользователи могли +поделиться контентом из другого приложения и запускать ваше приложение для выполнения требуемого действия.</p> + +<p>Чтобы разрешить другим приложениям запускать ваши операции, вам нужно добавить в ваш файл манифеста элемент <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>.</p> + +<p>Если ваше приложение установлено на устройстве, система идентифицирует ваши +фильтры Intent и добавляет информацию во внутренний каталог намерений, поддерживаемый всеми установленными приложениями. +Когда приложение осуществляет вызов {@link android.app.Activity#startActivity +startActivity()} или {@link android.app.Activity#startActivityForResult startActivityForResult()} +с неявными объектами Intent, система определяет, какая операция (или какие операции) может отреагировать на объект +Intent.</p> + + + +<h2 id="AddIntentFilter">Добавление фильтра Intent</h2> + +<p>Чтобы правильно определить, какие объекты Intent может обрабатывать ваша операция, каждый добавляемый вами фильтр Intent +должен быть максимально определенным с точки зрения действий и данных, принимаемых +операцией.</p> + +<p>Система может отправить указанный {@link android.content.Intent} в операцию, если у нее имеется +фильтр Intent, соответствующий следующим критериям объекта {@link android.content.Intent}:</p> + +<dl> + <dt>Действие</dt> + <dd>Строка, называющая действие, которое необходимо выполнить. Обычно это одно из определяемых платформой значений, +например, {@link android.content.Intent#ACTION_SEND} или {@link android.content.Intent#ACTION_VIEW}. + <p>Это следует указать в фильтре Intent с элементом <a href="{@docRoot}guide/topics/manifest/action-element.html">{@code <action>}</a>. +Указанное в этом элементе значение должно представлять собой полное имя строки действия, а не +постоянное значение API-интерфейса (см. примеры ниже).</p></dd> + + <dt>Данные</dt> + <dd>Описание данных, связанных с объектом Intent. + <p>Указывается в фильтре Intent с элементом <a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a>. Используя один +или несколько атрибутов в этом элементе, вы можете указать только тип MIME, только префикс URI, +только схему URI или из сочетание, а также другие индикаторы типа +принимаемых данных.</p> + <p class="note"><strong>Примечание.</strong> Если вам не нужно декларировать специфику данных +{@link android.net.Uri}(например, когда операция использует другие виды дополнительных данных вместо +URI), вы должны указать только атрибут {@code android:mimeType} для декларирования типа +данных, с которыми работает ваша операция, например, {@code text/plain} или {@code image/jpeg}.</p> +</dd> + <dt>Категория</dt> + <dd>Дополнительный способ описания характеристик операции, обрабатывающей объект Intent который обычно связан +с жестом пользователя или местом запуска. Система поддерживает несколько разных категорий, +но большинство из них используется редко. Однако по умолчанию все неявные объекты Intent определяются с +{@link android.content.Intent#CATEGORY_DEFAULT}. + <p>Это указывается в фильтре Intent с элементом <a href="{@docRoot}guide/topics/manifest/category-element.html">{@code <category>}</a> +.</p></dd> +</dl> + +<p>В своем фильтре Intent вы можете декларировать критерии, принимаемые вашей операцией. +Для этого нужно декларировать каждый из них с соответствующими элементами XML, вложенными в элемент <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a> +.</p> + +<p>Рассмотрим в качестве примера операцию с фильтром Intent, обрабатывающим объект {@link +android.content.Intent#ACTION_SEND}, для текстовых или графических данных.</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"/> + <data android:mimeType="image/*"/> + </intent-filter> +</activity> +</pre> + +<p>Каждый входящий объект Intent указывает только одно действие и только один тип данных, однако допускается декларирование нескольких +экземпляров элементов <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> в каждом +<a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a>.</p> + +<p>Если поведение любых двух пар действий и данных является взаимоисключающим, +необходимо создать отдельные фильтры Intent с указанием допустимых действий +в сочетании с соответствующими типами данных.</p> + +<p>Допустим, ваша операция обрабатывает текст и изображения для объектов Intent {@link +android.content.Intent#ACTION_SEND} и {@link +android.content.Intent#ACTION_SENDTO}. В этом случае вам необходимо определить два отдельных фильтра Intent +для двух действий, потому что объект Intent {@link +android.content.Intent#ACTION_SENDTO} должен использовать данные {@link android.net.Uri} для указания +адреса получателя с использованием схемы URI {@code send} или {@code sendto}. Например:</p> + +<pre> +<activity android:name="ShareActivity"> + <!-- filter for sending text; accepts SENDTO action with sms URI schemes --> + <intent-filter> + <action android:name="android.intent.action.SENDTO"/> + <category android:name="android.intent.category.DEFAULT"/> + <data android:scheme="sms" /> + <data android:scheme="smsto" /> + </intent-filter> + <!-- filter for sending text or images; accepts SEND action and text or image data --> + <intent-filter> + <action android:name="android.intent.action.SEND"/> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="image/*"/> + <data android:mimeType="text/plain"/> + </intent-filter> +</activity> +</pre> + +<p class="note"><strong>Примечание.</strong> Для получения неявных объектов Intent необходимо включить категорию +{@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> + +<p>Более подробную информацию об отправке и получении объектов {@link android.content.Intent#ACTION_SEND} +Intent для социальных сетей и обмена данными см. в уроке <a href="{@docRoot}training/sharing/receive.html">Получение простых данных от других приложений</a>.</p> + + +<h2 id="HandleIntent">Обработка объекта Intent в операции</h2> + +<p>Чтобы решить, какое действие выполнить в операции, можно прочитать объект {@link +android.content.Intent}, использованный для ее запуска.</p> + +<p>При запуске операции нужно вызвать {@link android.app.Activity#getIntent()} для получения +{@link android.content.Intent}, запустившего операцию. Это можно сделать в любое время в течение +жизненного цикла операции, но обычно это делается при ранних обратных вызовах, например, +{@link android.app.Activity#onCreate onCreate()} или {@link android.app.Activity#onStart()}.</p> + +<p>Например:</p> + +<pre> +@Override +protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.main); + + // Get the intent that started this activity + Intent intent = getIntent(); + Uri data = intent.getData(); + + // Figure out what to do based on the intent type + if (intent.getType().indexOf("image/") != -1) { + // Handle intents with image data ... + } else if (intent.getType().equals("text/plain")) { + // Handle intents with text ... + } +} +</pre> + + +<h2 id="ReturnResult">Возврат результата</h2> + +<p>Если вы хотите возвратить результат операции, которая вызвала вашу операцию, просто вызовите {@link +android.app.Activity#setResult(int,Intent) setResult()}, чтобы указать код результата и объект {@link +android.content.Intent} результата. Когда ваша операция завершается, и пользователь должен вернуться к первоначальной +операции, вызовите {@link android.app.Activity#finish()}, чтобы закрыть (и уничтожить) вашу операцию. Например: +</p> + +<pre> +// Create intent to deliver some kind of result data +Intent result = new Intent("com.example.RESULT_ACTION", Uri.parse("content://result_uri"); +setResult(Activity.RESULT_OK, result); +finish(); +</pre> + +<p>Вместе с результатом всегда нужно указывать код результата. Обычно этот код выглядит так: {@link +android.app.Activity#RESULT_OK} или {@link android.app.Activity#RESULT_CANCELED}. После этого при +необходимости можно указать дополнительные данные с помощью {@link android.content.Intent}.</p> + +<p class="note"><strong>Примечание.</strong> По умолчанию используется результат {@link +android.app.Activity#RESULT_CANCELED}. Таким образом, если пользователь нажимает кнопку <em>Назад</em> +до завершения действия и определения результата, первоначальная операция получает +результат "Отменено".</p> + +<p>Если вам просто нужно возвратить целое число, указывающее один из нескольких вариантов результатов, вы можете установить +для кода результата любое значение больше 0. Если вы используете код результата для передачи целого числа и вам +не нужно включать {@link android.content.Intent}, можете вызвать метод {@link +android.app.Activity#setResult(int) setResult()} и передать только код результата. Например:</p> + +<pre> +setResult(RESULT_COLOR_RED); +finish(); +</pre> + +<p>В этом случае может быть мало возможных результатов, и поэтому код результата представляет собой определяемое +локально целое число (больше 0). Это хорошо работает, когда вы возвращаете результат операции +в собственном приложении, поскольку получающая результат операция может ссылаться на общедоступное +постоянное значение для определения значения кода результата.</p> + +<p class="note"><strong>Примечание.</strong> Не нужно проверять, запущена ли ваша операция, +используя метод {@link +android.app.Activity#startActivity startActivity()} или {@link +android.app.Activity#startActivityForResult startActivityForResult()}. Просто вызовите метод {@link +android.app.Activity#setResult(int,Intent) setResult()}, если запустивший вашу операцию объект Intent может +ожидать результат. Если исходная операция вызвала метод {@link +android.app.Activity#startActivityForResult startActivityForResult()}, система передаст +ей результат, который вы передаете {@link android.app.Activity#setResult(int,Intent) setResult()}. В противном случае +результат будет проигнорирован.</p> + + + + + + diff --git a/docs/html-intl/intl/ru/training/basics/intents/index.jd b/docs/html-intl/intl/ru/training/basics/intents/index.jd new file mode 100644 index 0000000..cfb606d --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/intents/index.jd @@ -0,0 +1,62 @@ +page.title=Взаимодействие с другими приложениями +page.tags=объекты Intent,операция +helpoutsWidget=true + +trainingnavtop=true +startpage=true + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>Необходимые знания и компоненты</h2> +<ul> + <li>Базовые знания о жизненном цикле операции (см. раздел <a href="{@docRoot}training/basics/activity-lifecycle/index.html">Управление жизненным циклом +операции</a>)</li> +</ul> + + +<h2>См. также:</h2> +<ul> + <li><a href="{@docRoot}training/sharing/index.html">Общий доступ к простым данным</a></li> + <li><a href="{@docRoot}training/secure-file-sharing/index.html">Общий доступ к файлам</a> + <li><a href="http://android-developers.blogspot.com/2009/11/integrating-application-with-intents.html"> +Интеграция приложений с объектами Intent (сообщение в блоге)</a></li> + <li><a href="{@docRoot}guide/components/intents-filters.html">Объекты Intent и фильтры +объектов Intent</a></li> +</ul> + +</div> +</div> + +<p>В приложениях Android обычно имеется несколько <a href="{@docRoot}guide/components/activities.html">операций</a>. Каждая операция отображает +пользовательский интерфейс, позволяющий пользователю выполнить определенную задачу (например, посмотреть карту или сделать снимок). +Чтобы обеспечить переход пользователя от одной операции к другой, приложение должно использовать объект {@link +android.content.Intent} для определения "намерения" приложения что-то сделать. При передаче системе +{@link android.content.Intent} с помощью такого метода как {@link +android.app.Activity#startActivity startActivity()} система использует {@link +android.content.Intent} для идентификации и запуска соответствующего компонента приложения. Использование объектов Intent даже +позволяет приложению запускать операции, содержащиеся в отдельном приложении.</p> + +<p>{@link android.content.Intent} может <em>явно</em> запускать определенный компонент +(определенный экземпляр {@link android.app.Activity}) или <em>косвенно</em> запускать любой +компонент, способный выполнить желаемую операцию (например, "сделать снимок").</p> + +<p>В этом учебном курсе рассказывается о том, как использовать {@link android.content.Intent} для простого +взаимодействия с другими приложениями, например для запуска другого приложения, получения результата от этого приложения и обеспечения +способности приложения реагировать на объекты Intent из других приложений.</p> + +<h2>Уроки</h2> + +<dl> + <dt><b><a href="sending.html">Направление пользователя в другое приложение</a></b></dt> + <dd>Вы узнаете, как создавать неявные объекты Intent для запуска других приложений, способных выполнить +операцию.</dd> + <dt><b><a href="result.html">Получение результата операции</a></b></dt> + <dd>В этом уроке демонстрируется, как запустить другую операцию и получить ее результат.</dd> + <dt><b><a href="filters.html">Разрешение другим приложениям на запуск операции</a></b></dt> + <dd>В этом уроке демонстрируется, как разрешить другим приложениям использовать операции вашего приложения за счет определения +фильтров объектов Intent, которые декларируют неявные намерения, принимаемые вашим приложением.</dd> +</dl> + diff --git a/docs/html-intl/intl/ru/training/basics/intents/result.jd b/docs/html-intl/intl/ru/training/basics/intents/result.jd new file mode 100644 index 0000000..8ab03d4 --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/intents/result.jd @@ -0,0 +1,178 @@ +page.title=Получение результата операции +page.tags=объекты Intent +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> + <div id="tb"> + +<h2>Содержание этого урока</h2> +<ol> + <li><a href="#StartActivity">Запуск операции</a></li> + <li><a href="#ReceiveResult">Получение результата</a></li> +</ol> + +<h2>См. также:</h2> +<ul> + <li><a href="{@docRoot}training/sharing/index.html">Общий доступ к простым данным</a></li> + <li><a href="{@docRoot}training/secure-file-sharing/index.html">Общий доступ к файлам</a> +</ul> + + </div> +</div> + +<p>Запуск другой операции не обязательно должен быть односторонним действием. Вы можете запустить другую операцию и +получить от нее результат. Для получения результата нужно вызвать метод {@link android.app.Activity#startActivityForResult +startActivityForResult()} (вместо {@link android.app.Activity#startActivity +startActivity()}).</p> + +<p>Например, ваше приложение может запустить приложение для камеры и получить в качестве результата фотографию. Также вы +можете запустить приложение "Люди", чтобы пользователь выбрал в нем +контакт, и получить контактные данные в качестве результата.</p> + +<p>Разумеется, отвечающая операция должна быть способной возвратить результат. При возврате +результат отправляется как другой объект {@link android.content.Intent}. Ваша операция получает +его в обратном вызове {@link android.app.Activity#onActivityResult onActivityResult()}.</p> + +<p class="note"><strong>Примечание.</strong> Вы можете использовать явные и неявные результаты при вызове +{@link android.app.Activity#startActivityForResult startActivityForResult()}. При запуске собственной +операции для получения результата вы должны использовать явные результаты, чтобы получить +именно ожидаемый результат.</p> + + +<h2 id="StartActivity">Запуск операции</h2> + +<p>В объекте {@link android.content.Intent}, используемом для запуска операции +для получения результата, нет ничего особенного, однако нужно передать дополнительный целочисленный аргумент методы {@link +android.app.Activity#startActivityForResult startActivityForResult()}.</p> + +<p>Этот целочисленный аргумент представляет собой "код запроса", определяющий ваш запрос. Когда вы получите +результат {@link android.content.Intent}, обратный вызов использует тот же самый код результата, +чтобы ваше приложение могло правильно определить результат и понять, как его нужно обработать.</p> + +<p>Например, вот так можно запустить операцию, позволяющую пользователю выбрать контакт:</p> + +<pre> +static final int PICK_CONTACT_REQUEST = 1; // The request code +... +private void pickContact() { + Intent pickContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts")); + pickContactIntent.setType(Phone.CONTENT_TYPE); // Show user only contacts w/ phone numbers + startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST); +} +</pre> + + +<h2 id="ReceiveResult">Получение результата</h2> + +<p>Когда пользователь завершит последующую операцию и вернется, система вызовет метод вашей операции +{@link android.app.Activity#onActivityResult onActivityResult()}. Этот метод содержит три +аргумента:</p> + +<ul> + <li>Код запроса, переданный вами {@link +android.app.Activity#startActivityForResult startActivityForResult()}.</li> + <li>Полученный в качестве результата код, указанный второй операцией. Или {@link +android.app.Activity#RESULT_OK}, если операция была успешной, или {@link +android.app.Activity#RESULT_CANCELED}, если пользователь отказался или операцию не удалось выполнить по какой-либо +причине.</li> + <li>{@link android.content.Intent}, переносящий полученные данные.</li> +</ul> + +<p>Например, результаты для намерения "Выбрать контакт" могут обрабатываться следующим образом:</p> + +<pre> +@Override +protected void onActivityResult(int requestCode, int resultCode, Intent data) { + // Check which request we're responding to + if (requestCode == PICK_CONTACT_REQUEST) { + // Make sure the request was successful + if (resultCode == RESULT_OK) { + // The user picked a contact. + // The Intent's data Uri identifies which contact was selected. + + // Do something with the contact here (bigger example below) + } + } +} +</pre> + +<p>В этом примере результаты, {@link android.content.Intent} возвращаемые приложениями +Android Контакты или Люди, предоставляют контент {@link android.net.Uri}, который идентифицирует +выбранный пользователем контакт.</p> + +<p>Для успешной обработки результатов необходимо понимать, каким будет формат этих результатов +{@link android.content.Intent}. Это просто, если результат возвращается одной из ваших +собственных операций. Приложения, входящие в состав платформы Android, имеют собственные прикладные интерфейсы, так что +вы можете рассчитывать на получение определенных результатов. Например, приложение "Люди" (приложение "Контакты" в старых +версиях) всегда возвращает результат с URI контента, идентифицирующий выбранный контакт, а приложение +"Камера" возвращает {@link android.graphics.Bitmap} в дополнительном {@code "data"} (см. урок +<a href="{@docRoot}training/camera/index.html">Съемка фотографий</a>).</p> + + +<h4>Бонус: Чтение контактных данных</h4> + +<p>Приведенный выше код, показывающий как получить результаты из приложения "Люди", не описывает +детально чтение данных результатов, потому что для этого нужно более +подробно рассказать о <a href="{@docRoot}guide/topics/providers/content-providers.html">поставщиках +контента</a>. Если вам все-таки интересно, вот еще код, показывающий как запрашивать +данные результатов для получения номера телефона выбранного контакта:</p> + +<pre> +@Override +protected void onActivityResult(int requestCode, int resultCode, Intent data) { + // Check which request it is that we're responding to + if (requestCode == PICK_CONTACT_REQUEST) { + // Make sure the request was successful + if (resultCode == RESULT_OK) { + // Get the URI that points to the selected contact + Uri contactUri = data.getData(); + // We only need the NUMBER column, because there will be only one row in the result + String[] projection = {Phone.NUMBER}; + + // Perform the query on the contact to get the NUMBER column + // We don't need a selection or sort order (there's only one result for the given URI) + // CAUTION: The query() method should be called from a separate thread to avoid blocking + // your app's UI thread. (For simplicity of the sample, this code doesn't do that.) + // Consider using {@link android.content.CursorLoader} to perform the query. + Cursor cursor = getContentResolver() + .query(contactUri, projection, null, null, null); + cursor.moveToFirst(); + + // Retrieve the phone number from the NUMBER column + int column = cursor.getColumnIndex(Phone.NUMBER); + String number = cursor.getString(column); + + // Do something with the phone number... + } + } +} +</pre> + +<p class="note"><strong>Примечание.</strong> До выхода версии Android 2.3 (API-интерфейс уровня 9) для выполнения +запроса {@link android.provider.ContactsContract.Contacts Contacts Provider} (как +показанный выше) ваше приложение должно было декларировать разрешение {@link +android.Manifest.permission#READ_CONTACTS} (см. <a href="{@docRoot}guide/topics/security/security.html">Безопасность и разрешения</a>). Однако, +начиная с версии Android 2.3, приложение "Контакты/Люди" дает вашему приложению временное +разрешение на чтение данных Поставщика контактов при выводе результата. Это временное разрешение +действует только в отношении конкретного запрошенного контакта, так что нельзя запрашивать другой контакт, +кроме указанного объектом Intent {@link android.net.Uri}, если вы не хотите декларировать разрешение {@link +android.Manifest.permission#READ_CONTACTS}.</p> + + + + + + + + + + + + + + + diff --git a/docs/html-intl/intl/ru/training/basics/intents/sending.jd b/docs/html-intl/intl/ru/training/basics/intents/sending.jd new file mode 100644 index 0000000..da147ee --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/intents/sending.jd @@ -0,0 +1,256 @@ +page.title=Направление пользователя в другое приложение +page.tags=объекты Intent +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + + +<div id="tb-wrapper"> + <div id="tb"> + +<h2>Содержание этого урока</h2> +<ol> + <li><a href="#Build">Создание явного объекта Intent</a></li> + <li><a href="#Verify">Проверка наличия приложения для получения объекта Intent</a></li> + <li><a href="#StartActivity">Запуск операции с объектом Intent</a></li> + <li><a href="#AppChooser">Отображение блока выбора приложения</a></li> +</ol> + +<h2>См. также:</h2> +<ul> + <li><a href="{@docRoot}training/sharing/index.html">Общий доступ к простым данным</a></li> +</ul> + + </div> +</div> + +<p>Одна из наиболее важных возможностей системы Android – способность приложений направлять пользователя в другое приложение +в зависимости от желаемого действия. Например, если +ваше приложение содержит адрес компании, который нужно показать на карте, вам не нужно встраивать в приложение операцию +вывода карты. Вместо этого можно создать запрос на просмотр адреса +с помощью {@link android.content.Intent}. В этом случае система Android запускает приложение, +которое показывает адрес на карте.</p> + +<p>Как объяснялось в первом уроке <a href="{@docRoot}training/basics/firstapp/index.html">Создание +первого приложения</a>, объекты Intent нужно использовать для навигации между операциями в собственном приложении. Обычно +для этого используются <em>явные объекты Intent</em>, определяющие точное имя класса +компонента, который вы хотите запустить. Однако если вы хотите, чтобы действие (например +просмотр карты) выполнялось отдельным приложением, следует использовать <em>неявный объект Intent</em>.</p> + +<p>На этом уроке вы узнаете, как создать неявный объект Intent для определенного действия и использовать его +для запуска операции, выполняющей действие в другом приложении.</p> + + + +<h2 id="Build">Создание неявного объекта Intent</h2> + +<p>Неявные объекты Intent не декларируют имя класса запускаемого компонента, а декларируют +выполняемое действие. Действие указывает задачу, которую вы хотите выполнить, например <em>просмотр</em>, +<em>правка</em>,<em> отправка</em> или <em>получение</em> чего-либо. Объекты Intent часто также содержат данные, ассоциируемые +с действием, например адрес для просмотра или электронное сообщение для отправки. +В зависимости от того, какой объект Intent вы хотите создать, данные могут относиться к типу {@link android.net.Uri}, +одному из нескольких других типов данных, либо объекту могут вообще не требоваться данные.</p> + +<p>Если ваши данные относятся к типу {@link android.net.Uri}, вы можете использовать простой конструктор {@link +android.content.Intent#Intent(String,Uri) Intent()} для определения действия и +данных.</p> + +<p>Приведем пример создания объекта Intent для запуска телефонного звонка, в котором данные {@link +android.net.Uri} используются для указания телефонного номера:</p> + +<pre> +Uri number = Uri.parse("tel:5551234"); +Intent callIntent = new Intent(Intent.ACTION_DIAL, number); +</pre> + +<p>Когда ваше приложение активирует этот объект Intent посредством вызова {@link android.app.Activity#startActivity +startActivity()}, приложение "Телефон" инициирует звонок на указанный номер телефона.</p> + +<p>Вот еще несколько объектов Intent в сочетании с действиями и {@link android.net.Uri} парами +данных:</p> + +<ul> + <li>Просмотр карты: +<pre> +// Map point based on address +Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California"); +// Or map point based on latitude/longitude +// Uri location = Uri.parse("geo:37.422219,-122.08364?z=14"); // z param is zoom level +Intent mapIntent = new Intent(Intent.ACTION_VIEW, location); +</pre> + </li> + <li>Просмотр веб-страницы: +<pre> +Uri webpage = Uri.parse("http://www.android.com"); +Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage); +</pre> + </li> +</ul> + +<p>Другие виды неявных объектов Intent требуют дополнительные данные, предоставляющие разные типы данных, +например строки. Вы можете добавить один или несколько элементов дополнительных данных с помощью разных методов {@link +android.content.Intent#putExtra(String,String) putExtra()}.</p> + +<p>Система по умолчанию определяет соответствующий тип MIME, который требует объект Intent на базе включенных данных +{@link android.net.Uri}. Если не включать {@link android.net.Uri} в объект +Intent, обычно нужно использовать {@link android.content.Intent#setType setType()} для указания типа +данных, связанного с объектом Intent. Установка типа MIME также определяет, какие виды +действий должен получать объект Intent.</p> + +<p>Вот некоторые объекты Intent, добавляющие дополнительные данные для указания желаемого действия:</p> + +<ul> + <li>Отправка электронного письма с вложением: +<pre> +Intent emailIntent = new Intent(Intent.ACTION_SEND); +// The intent does not have a URI, so declare the "text/plain" MIME type +emailIntent.setType(HTTP.PLAIN_TEXT_TYPE); +emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"jon@example.com"}); // recipients +emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Email subject"); +emailIntent.putExtra(Intent.EXTRA_TEXT, "Email message text"); +emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://path/to/email/attachment")); +// You can also attach multiple items by passing an ArrayList of Uris +</pre> + </li> + <li>Создание события в календаре: +<pre> +Intent calendarIntent = new Intent(Intent.ACTION_INSERT, Events.CONTENT_URI); +Calendar beginTime = Calendar.getInstance().set(2012, 0, 19, 7, 30); +Calendar endTime = Calendar.getInstance().set(2012, 0, 19, 10, 30); +calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis()); +calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis()); +calendarIntent.putExtra(Events.TITLE, "Ninja class"); +calendarIntent.putExtra(Events.EVENT_LOCATION, "Secret dojo"); +</pre> +<p class="note"><strong>Примечание.</strong> Этот объект Intent для создания события в календаре поддерживается только +API-интерфейсами уровня 14 и выше.</p> + </li> +</ul> + +<p class="note"><strong>Примечание.</strong> Очень важно определять объекты {@link +android.content.Intent} как можно более конкретно. Например, если вы хотите вывести изображение +с помощью объекта Intent {@link android.content.Intent#ACTION_VIEW}, вам следует указать тип MIME +{@code image/*}. Это не даст объекту Intent запускать приложения для просмотра других типов +данных (например картографические приложения).</p> + + + +<h2 id="Verify">Проверка наличия приложения для получения объекта Intent</h2> + +<p>Хотя платформа Android гарантирует выполнение объектов Intent во +встроенных приложениях (таких как "Телефон", "Электронная почта" или "Календарь"), перед активацией объекта Intent всегда следует добавлять шаг +проверки.</p> + +<p class="caution"><strong>Внимание!</strong> Если вы активируете объект Intent, а на устройстве не +будет приложения, способного его обработать, ваше приложение закроется с ошибкой.</p> + +<p>Чтобы убедиться в наличии операции, реагирующей на объект Intent, вызовите метод {@link +android.content.pm.PackageManager#queryIntentActivities queryIntentActivities()} для получения списка +операций, способных обработать ваш {@link android.content.Intent}. Если полученный в результате список {@link +java.util.List} не пустой, вы можете безопасно использовать данный объект Intent. Например:</p> + +<pre> +PackageManager packageManager = {@link android.content.Context#getPackageManager()}; +List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, + PackageManager.MATCH_DEFAULT_ONLY); +boolean isIntentSafe = activities.size() > 0; +</pre> + +<p>Если<code>isIntentSafe</code> имеет значение <code>true</code>, то хотя бы одно приложение отреагирует на объект +Intent. Если же он имеет значение <code>false</code>, то на устройстве нет приложений для обработки данного объекта Intent.</p> + +<p class="note"><strong>Примечание.</strong> Такую проверку следует выполнять при первом +запуске операции на случай, если понадобится отключить функцию, обрабатывающую объект Intent, до того, как пользователь попытается использовать +ее. Если вам известно определенное приложение, которое может обработать данный объект Intent, вы можете указать ссылку, +по которой пользователь может загрузить приложение (посмотрите, как <a href="{@docRoot}distribute/tools/promote/linking.html">добавить ссылку на свой продукт в Google +Play</a>).</p> + + +<h2 id="StartActivity">Запуск операции с объектом Intent</h2> + +<div class="figure" style="width:200px;margin-top:-10px"> + <img src="{@docRoot}images/training/basics/intents-choice.png" alt="" /> + <p class="img-caption"><strong>Рисунок 1.</strong> Пример диалогового окна выбора, +которое отображается, если объект Intent могут обработать разные приложения.</p> +</div> + +<p>После создания {@link android.content.Intent} и установки дополнительной информации вызовите {@link +android.app.Activity#startActivity startActivity()} для отправки в систему. Если система +идентифицирует несколько операций, способных обработать объект Intent, она выводит для пользователя диалоговое окно +выбора приложения, как показано на рисунке 1. Если объект Intent может быть обработан +только одной операцией, система сразу же запускает ее.</p> + +<pre> +startActivity(intent); +</pre> + +<p>Вот полный пример, показывающий, как создать объект Intent для просмотра карты, убедиться в наличии +приложения для его обработки и запустить это приложение:</p> + +<pre> +// Build the intent +Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California"); +Intent mapIntent = new Intent(Intent.ACTION_VIEW, location); + +// Verify it resolves +PackageManager packageManager = {@link android.content.Context#getPackageManager()}; +List<ResolveInfo> activities = packageManager.queryIntentActivities(mapIntent, 0); +boolean isIntentSafe = activities.size() > 0; + +// Start an activity if it's safe +if (isIntentSafe) { + startActivity(mapIntent); +} +</pre> + + + +<h2 id="AppChooser">Отображение блока выбора приложения</h2> + +<div class="figure" style="width:200px;margin-top:-10px"> + <img src="{@docRoot}images/training/basics/intent-chooser.png" alt="" /> + <p class="img-caption"><strong>Рисунок 2</strong>. Диалог выбора.</p> +</div> + +<p>Обратите внимание, что при запуске операции посредством передачи {@link android.content.Intent} в {@link +android.app.Activity#startActivity startActivity()} и наличии нескольких приложений, реагирующих на +объект Intent, пользователь может выбрать, какое из этих приложений использовать по умолчанию (для этого нужно установить флажок в нижней +части диалогового окна; см. рисунок 1). Это удобно при выполнении действия, для которого +пользователь обычно хочет всегда использовать одно и то же приложение, например при открытии веб-страницы (пользователи +обычно используют один и тот же браузер) или создании снимка (пользователи обычно предпочитают одно и то же приложение камеры).</p> + +<p>Однако бывает так, что выполняемое действие может быть обработано несколькими приложениями, и пользователь +каждый раз может использовать разные приложения — например, действие "Поделиться", для которого пользователи могут использовать разные +приложения, — и в этом случае ваше приложение должно отображать диалоговое окно выбора приложения, +как показано на рисунке 2. В диалоговом окне +выбора приложения пользователь должен при каждом запуске выбирать, какое приложение использовать для действия (пользователь не +может выбрать приложение по умолчанию).</p> + +<p>Чтобы отобразить блок выбора приложения, создайте {@link android.content.Intent} с помощью {@link +android.content.Intent#createChooser createChooser()} и передайте его {@link +android.app.Activity#startActivity startActivity()}. Например:</p> + +<pre> +Intent intent = 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 chooser +Intent chooser = Intent.createChooser(intent, title); + +// Verify the intent will resolve to at least one activity +if (intent.resolveActivity(getPackageManager()) != null) { + startActivity(chooser); +} +</pre> + +<p>В результате отобразится диалоговое окно со списком приложений, которые могут отреагировать на объект Intent, переданный методу {@link +android.content.Intent#createChooser createChooser()} и используют указанный текст в качестве +заголовка диалога.</p> + + + |