
Полная версия:
Программирование приложений для мобильных устройств под управлением Android. Часть 1
• конфигурирование элементов пользовательского интерфейса и view.
Давайте рассмотрим метод onCreate приложения «MapLocation». @Override – переопределяет activity.onCreate. В методе onCreate вы видите четыре функции, о которых мы говорили.

Во-первых, есть вызов super.onCreate. Затем есть вызов setContentView с ID файла лейаута R.layout.main в качестве параметра. После этого Activity сохраняет ссылки на view редактирования текста и на кнопку, которые появляются в пользовательском интерфейсе. И, наконец, onCreate присоединяет «слушателя» кнопки button.setOnClickListener. Android вызывает этот код «слушателя» каждый раз, когда пользователь нажимает на кнопку.

оnRestart – следующий метод жизненного цикла. Он вызывается, если Activity, которая была остановлена, будет запущена снова. И вы можете использовать onRestart, чтобы выполнить некоторые действия, которые необходимы, перед тем, как Activity будет запущена снова.
onStart вызывается перед тем, как Activity станет видимой. Некоторые типичные применения для этого метода – это запрос обновлений датчика положения или загрузка или сброс персистентного состояния приложения.
onResume вызывается перед тем, как Activity начнёт взаимодействовать с пользователем, и уже видима. Действия, которые вы совершаете в этом методе, должны быть запущены на переднем плане приложения. Например, стартовые анимации или воспроизведение фоновой звуковой дорожки.
onPause вызывается пред тем, как Activity потеряет фокус. В этом методе вы можете завершить действия, совершающиеся на переднем плане, например, уничтожить анимации. Кроме этого вы должны сохранить любые изменения, которые совершил пользователь только что.
onStop вызывается, когда Activity больше не видима пользователю. В этой точке предполагается, что Activity может быть опять запущена позже. Так, некоторые типичные действия, которые надо сделать здесь – это кэширование состояния Activity, которое вы захотите восстановить, когда Activity позже перезапустится по вызову onStart. И помните: onStop не всегда вызывается, когда Activity завершается. Например, его нельзя вызвать, если Android уничтожает процесс приложения из-за нехватки памяти. Поэтому вместо сохранения персистентных данных в этом методе, лучше сделайте это в onPause.
onDestroy вызывается перед тем, как Activity будет уничтожена. Некоторые типичные действия, которые надо сделать в этом методе – высвободить ресурсы, занимаемых этой Activity. Например, закрыть частные потоки, которые были запущены этой Activity. И снова помните, что onDestroy не будет вызван, например, если Android уничтожит приложение из-за нехватки памяти на устройстве.
Теперь, когда мы увидели жизненный цикл Activity в действии, давайте рассмотрим как одна Activity может программно запускать другую Activity. Чтобы запустить Activity программно, вы сначала создаете объект Intent (намерение), который определяет Activity, которую вы хотите запустить. И затем вы передаете этот созданный Intent тем методам, которые запускают Activity – startActivity или startActivityForResult. startActivity запустит желаемую Activity, скрывая текущую Activity с переднего плана. startActivityForResult, тоже запустит желаемую Activity, но сделает это с ожиданием того, что запущенная Activity вернет результат для вызывающей Activity.
Давайте рассмотрим как приложение «Map Location» запускает Activity «Google Maps». Каждый раз, когда пользователь кликает кнопкой, вызывается метод onClick. В строковую переменную address помещается географический адрес, считанный из текстового поля addrText. Далее создается Intent geoIntent, содержащий в себе заданный географический адрес, который затем и передается методу startActivity для запуска приложения «Карты» с географической картой, центрированной на указанном адресе.
Но вместо того, чтобы запросить у пользователя ввод адреса через клавиатуру, мы можем позволить ему выбрать контакт из приложения «Контакты» и использовать адресные данные этого контакта как географический адрес, который открыть на карте. Для этого рассмотрим приложение «Map location from contacts».

Отличие от «Map Location» находится в коде OnClickListener, в частности, этот код создает несколько иной Intent, а затем передает его в качестве параметра методу StartActivityForResult. Вторым параметром является requestCode (код запроса), который пригодится позже, когда результат вернется обратно в запускающую Activity.
Как только новая Activity запущена, она ответственна за результат, который должен быть возвращен назад вызывающей Activity. Это делается вызовом Activity.SetResult, передвая в нем код результата (resultCode). Эти коды результата включают некоторые встроенные коды, такие как Result_Canceled (отмененный результат), который указывает, что пользователь принял решение не завершать действие, а отменил его, например, нажав кнопку «Назад», и Result_OK, указывающий, что Activity фактически завершена штатно. Разработчики могут также добавить пользовательские коды результата после встроенного кода результата (Result_First_User).
Перед тем, как Activity завершится, она должна выдать свой результат, вызвав SetResult, и этот результат будет в конечном счете передан назад в вызвавшую Activity и использован в методе onActivityResult.

Как вы видите, его параметры включают начальный код запроса (requestCode), который был передан в StartActivityForResult, код результата (resultCode), используемый запущенной Activity, когда она вызвала SetResult, и Intent, если он был передан в SetResult. onActivityResult обычно начинается с проверки кода результата и кода запроса, чтобы определить, что сделать с полученным результатом.
Следующим шагом необходимо получить данные контакта и проанализировать их, чтобы извлечь почтовый адрес контакта. Как только почтовый адрес был извлечен, «Map location from contacts» обрабатывает данные так, чтобы они были в формате, который ожидают карты Google (строковая переменная formattedAddress), помещает эти данные в Intent и затем вызывает startActivity, передавая ей этот Intent.

Далее рассмотрим обработку изменения конфигурации. Конфигурация устройства относится к характеристикам устройства и приложения, связанным с ресурсами приложения, такими как: используемые языки, размер экрана, доступность клавиатуры и ориентация экрана устройства. Эти характеристики могут измениться во время выполнения приложения и, когда это происходит, Android уничтожает текущую Activity и затем перезапускает ее с ресурсами, соответствующими измененной конфигурации.
Предположим, например, что изменяется ориентация устройства. Если вы поворачиваете устройство из портретного режима в альбомный и назад в портретный, это заставит систему уничтожить текущую Activity и перезапустить ее дважды. Чтобы оптимизировать скорость изменения конфигурации, Android позволяет вам сделать две вещи. Первое, когда происходит изменение конфигурации, вы можете создать и сохранить произвольный объект Java, тем самым сохранив в кэше важную информацию о состоянии. И второе, вы можете вручную обработать изменение конфигурации, избежав завершения и перезапуска Activity в целом.
Итак, чтобы ускорить реконфигурирование, вы можете сохранить данные в объекте Java. И один способ сделать это – переопределить метод onRetainNonConfigurationInstance и вызвать его между onStop и onDestroy. Объект, который вы создаете и возвращаете в onRetainNonConfigurationInstance, может быть получен путём вызова экземпляра getLastNonConfigurationInstance, когда Activity воссоздана. Это обычно делается во время вызова onCreate, когда вы воссоздаете Activity.
И вторая вещь, которую вы можете сделать, чтобы ускорить реконфигурирование, это избежать уничтожения и перезапуска Activity для определенных изменений конфигурации. Чтобы сделать это, вы объявляете в файле androidmanifest.xml те определенные изменения, которые ваша Activity обработает. Например, этот отрывок xml-файла указывает, что MyActivity вручную обработает изменения в ориентации устройства, размере экрана и доступности клавиатуры.

Если вы обрабатываете изменения конфигурации вручную, тогда, как только произойдут изменения конфигурации во время работы приложения, ваша Activity примет вызов onConfigurationChanged, и в качестве параметра этот метод получит объект конфигурации, который детализирует новую конфигурацию. Затем ваш код сможет считать этот объект и внести любые изменения, если необходимо.
Класс Intent
Класс Intent (намерение) – структура данных, которая служит двум целям. Первая – это определить операцию, которую вы хотите выполнить, и вторая – это представить событие, произошедшее в системе, о котором вы хотите уведомить другие компоненты. Мы поговорим о второй из них позже, когда будем рассматривать Broadcast receivers.
Вы можете представить себе Intent как предоставление своего рода языка для определения операций, которые вы хотите выполнить (ваше намерение). В сущности Intent даёт вам простой способ сказать такие вещи как: «я хочу выбрать контакт», «я хочу сделать фотографию», «я хочу набрать телефонный номер» или «я хочу вывести на экран карту». Intent обычно создаётся одной Activity, которой необходимо, чтобы некоторая операция была выполнена, и затем Android использует этот Intent чтобы запустить другую Activity, которая может выполнить желаемую работу.
Теперь о видах информации, которая может быть определена в Intent. Например: action (действие), data (данные), category (категория) и прочие. Action – строковая последовательность, которая представляет или называет операцию, которая будет выполнена. Например, action_dial означает, что я хочу набрать телефонный номер. Action_edit – я хочу вывести на экран некоторые данные для пользователя, чтобы отредактировать. Action_sync – синхронизировать некоторые данные на устройстве с данными на сервере. Action_main – запустить Activity как начальную Activity приложения.
Вы можете установить Action несколькими способами. Например, вы можете передать Action-строку в качестве параметра конструктору Intent. Как альтернатива, вы можете создать пустой Intent и затем вызвать метод setAction в нем, передав Action-строку в качестве параметра.

У Intent также есть поле данных (data), которое представляет данные, связанные с намерением. Эти данные отформатированы как Универсальный идентификатор ресурса или URI. Один пример данных Intent – geo URI, который означает данные географической карты.

Другой пример – URI, указывающий телефонный номер, который вы хотите набрать. Обратите внимание на то, что последовательности, которые представляют базовые данные, сначала передаются методу uri.parse, который получает эти последовательности и затем возвращает объект URI.

Вы можете установить данные Intent различными способами. Например, вы можете передать его непосредственно конструктору Intent, или вы можете создать пустой Intent и затем использовать метод setData, чтобы установить данные для этого Intent.
Категория (category) представляет дополнительную информацию о видах компонентов, которые может или должен обработать этот Intent. Некоторые примеры: category_browsable означает, что Activity может быть вызвана браузером через ссылку URI. Другой пример – category_launcher означает, что целевая Activity может быть начальной Activity задачи (task) и описана в средстве запуска приложения верхнего уровня.
У Intent также есть поле Type (тип), которое определяет MIME-тип данных. Некоторые примеры MIME-типов включают различные форматы изображений, такие как png или jpg. Есть также различные текстовые форматы, такие как HTML или txt. Если вы не определите MIME-тип, Android попытается указать его для вас. Вы можете установить MIME-тип при помощи метода setType, передав его в строковой последовательности, которая представляет желаемый MIME-тип. Вы можете также установить и данные, и тип вместе, вызвав метод setDataAndType.

Поле Component идентифицирует целевую Activity. Вы можете установить это поле, если знаете точно ту Activity, которая должна всегда получать этот Intent. Вы можете установить Component, используя один из конструкторов Intent, передав его в объекте контекста и объекте класса, который представляет Activity, которая должна выполнить желаемую операцию.

Вы можете также создать пустой Intent и затем использовать один из методов setComponent(), setClass() или setClassName(), чтобы установить целевую Activity.

Поле Extras. Extras хранят дополнительную информацию, связанную с Intent. Extras – это набор пар ключ-значение. Таким образом, целевая Activity должна знать и имя, и тип любых дополнительных данных (extras), которые она намеревается использовать. Например, класс Intent определяет дополнительные данные (extras) EXTRA_EMAIL, которые используются, чтобы передать список получателей при отправке электронного письма.


Как здесь видно, этот код создает Intent с действием (action) ACTION_SEND. Таким образом, мы хотим послать некоторое электронное письмо. А также добавляет Extras к этому Intent (EXTRA_EMAIL).
Есть несколько различных методов для установки Extras, и форма этих методов будет зависеть от типа данных, которые вы хотите там хранить. Так, например, есть один метод для хранения строковой последовательности, ещё один метод – для хранения массива чисел, и т. д.

Поле флагов (flags). Флаги представляют информацию о том, как Intent должен быть обработан. Некоторые встроенные примеры включают flag_activity_no_history. Что означает, что, когда Activity запущено на основе этого Intent, оно не должно быть помещено в стек.

Другой флаг – FLAG_DEBUG_LOG_RESOLUTION, который говорит Android распечатывать дополнительную информацию о журналировании (log), когда этот Intent обрабатывается, и это – важный инструмент, чтобы его использовать, если ваш Intent не запускает ту Activity, которую вы хотите.
Давайте поговорим о том, как использовать Intent, чтобы запустить Activity. Когда вы используете один из методов startActivity или startActivityForResult, Android должен выяснить, какую конкретно Activity он собирается запустить. И есть два пути, которыми Android это делает.
Первый путь состоит в том, что, если вы явно указали целевую Activity, когда создали Intent (explicit intent), то Android может просто найти и запустить эту определенную Activity.
Второй путь используется, только тогда, когда вы явно не указали целевую Activity, и состоит в том, что Android может неявно определить ее на основе Intent (implicit intent), который использовался, и на основе свойств Activity (или приложений), которые установлены на устройстве.
Давайте рассмотрим приложение, которое явно запускает другую Activity. Это приложение называется «Hello World With Login». Оно состоит из двух Activity. Одна Activity называется LoginActivity, а другая – HelloAndroid. Когда это приложение запущено, первым запускается LoginActivity, оно предоставляет текстовое поле для ввода имени пользователя и поле для ввода пароля. Есть также кнопка Login, которую пользователь нажимает после того как введет имя пользователя и пароль. И затем LoginActivity проверяет введенные данные – пароль и имя пользователя, и если примет их, запустит Activity HelloAndroid, которая выведет на экран слова “HelloAndroid”.

Давайте рассмотрим код, чтобы увидеть как это реализовано. Это класс LoginActivity. Здесь реализован onClickListener («слушатель» кнопки), он присоединен к кнопке Login. Код захватывает имя пользователя и пароль, которое пользователь только что ввел, и затем передает их методу checkPassword. Если checkPassword возвратит true, то код будет выполняться дальше.

Сначала создается Intent (helloAndroidIntent). Вызов конструктора получает два параметра – контекст и объект класса Activity, которая будет запущена. В этом случае LoginScreen.this используется в качестве контекста.
Контекст (Context) – это интерфейс, который предоставляет доступ к базовым функциям приложения. Так как Activity – это подкласс контекста, то можно использовать. this для этого параметра. Второй параметр – объект класса, в этом случае – HelloAndroid.class. Этот параметр указывает Activity, которая будет запущена вызовом startActivity.
Второй способ запустить Activity – неявная активация. В этой ситуации вы не сообщаете Android, какую Activity запустить, система должна решить это самостоятельно. Android пытается найти соответствие между Intent, который был передан в startActivity или startActivityForResult с возможностями других Activity (или приложений), которые находятся на устройстве. И этот процесс называют intent resolution.
Intent resolution полагается на два вида информации. Во-первых, Activity создает Intent, описывающий действие, которое должно быть выполнено. Во-вторых, у некоторых Activity в Android определены Intent-фильтры (IntentFilters), определяющие виды операций, которые они могут обработать. Эта информация обычно помещается в файл androidmanifest.xml или в приложения, которым эти Activity принадлежат.
Во время выполнения, когда вызван метод startActivity, Android попытается найти соответствие между Intent и известными Intent-фильтрами. Но он будет использовать только часть информации, содержащейся в Intent. В частности он будет смотреть на поле Action (действие), на Data (данные), включая URI и mime-тип и на Категорию (Category).
Рассмотрим то, как приложения определяют Intent-фильтры в своих файлах androidmanifest.xml. Вот отрывок xml, показывающий тег Activity, этот тег содержит в себе тег Intent-фильтра с соответствующим действием, которое он поддерживает. Например, если Activity может набирать номер телефона, то она должна содержать Intent-фильтр со стандартным intent.action.dial.

Если ваша Activity обрабатывает определенный тип данных, она может добавить эту информацию к ее Intent-фильтру, например, Activity может определить mime-тип данных, которые она обрабатывает. Вы можете также определить схему, хост или номер порта данных UI, которые она обрабатывает. В дальнейшем можно будет ограничить формат данных URI, определив путь, шаблон или префикс пути.
Если Activity хочет объявить, что она может, например, показывать карты, тогда она могла бы содержать Intent-фильтр, который говорит Android, что она может обработать данные URI со схемой geo.

Точно так же Activity могут определить категории для Intent, которые они обрабатывают.
Теперь мы можем предположить или представить на основе тех приложений «Map location», которые мы видели прежде, что «Google Maps» может обработать Intent, которые содержат action_view и которые содержат поле данных со схемой geo. Файл androidmanifest.xml для приложения карт Google выглядит примерно следующим образом. Имеется Activity с Intent-фильтром. И этот фильтр перечислен в Action intent.action.view. И у него есть данные со схемой geo. Фактически это то, что приложение «Map location» передали в стартовую Activity Google-карт.

Activity также перечисляет две категории. Одна – category.browsable означает, что Activity может реагировать на ссылки браузера. Другая – cateogry.default. Оказывается, что в большинстве случаев Activity, которые хотят принимать implicit intents, должны включать category.defualt в свои Intent-фильтры.
Наконец, когда больше чем одна Activity может принять определенный Intent, Android должен будет приостановить исполнение для принятия решения. Один вариант – спросить пользователя, какую Activity (или приложение) он хочет использовать. И второй вариант – это использовать возможность того, что Activity могут определить приоритет, который Android примет во внимание при выборе между двумя различными Activity, которые могут обработать данный Intent. Значения приоритета должны быть между минус 1000 и плюс 1000, более высокие значения имеют более высокий приоритет.
Permissions
Android использует Permissions (полномочия, разрешения), чтобы защитить ресурсы, данные и операции. Приложения могут определять и устанавливать свои собственные полномочия, чтобы ограничить доступ к их ресурсам и информации о пользователе. Например, чтобы ограничить, кто может получить доступ к базе данных приложения, платным услугам, ограничить какие компоненты могут вызвать дорогостоящие операции, такие как исходящие сообщения SMS или MMS, ограничить доступ к системным ресурсам, например, чтобы определить какие компоненты могут использовать камеру устройства для съемки фото или записи видео.
В Android полномочия (Permissions) представлены как строковые последовательности. Приложения включают эти строки в файл androidmanifest.xml. Они могут сделать это, чтобы объявить полномочия, которые они используют сами, и полномочия, которые они требуют от других компонентов, которые хотят использовать их.
Сначала, если приложение должно использовать разрешение (permission), оно объявляет это разрешение тегом uses-permission в его файле androidmanifest.xml. И когда это приложение будет устанавливаться на устройство, пользователь должен будет согласиться с этим разрешением, иначе произойдут ошибки или отказы доступа.
Рассмотрим кусок файла androidmanifest.xml для гипотетического приложения.

Как видите, для этого приложения нужны полномочия получить доступ к камере устройства, доступ к Интернету и доступ к информации о точном местоположении с приемника GPS.