Настоящая статья содержит обзор особенностей, которые java-разработчику следует иметь ввиду при написании приложений для платформы Android. Первая часть посвящена ответам на вопросы, которые обычно возникают у программистов, не знакомых с android, когда им требуется написать или принять решение о написании программы для этой платформы. Вторая часть имеет более практическую направленность и содержит ряд советов, приемов, шаблонов, следование которым позволит быстро написать тот или иной тип приложения.
Платформа Android — это основанная на ядре Linux адаптированная для работы на смартфонах система. Java — это основной язык для разработки приложений на этой платформе. Имеется также возможность написания приложений на различных скриптовых языках. Разработка приложения для android целиком на C/C++, по крайней мере официально, не поддерживается (хотя есть программы, которые преобразуют C/C++ код в java код). Когда возникает необходимость использования native кода, пишутся native библиотеки, которые затем инсталлируются на устройстве и вызываются из java кода.
В отличии от Java ME, Android не вводит понятий профайла и его расширений. Вопрос о работоспособности того или иного API должен решаться на этапе инсталляции. Для этого в файле конфигурации приложения имеется специальный тег uses-feature. Приложение может получить доступ к таким подсистемам устройства, как видео камера, bluetooth, сеть, файловая система, телефон (звонки и sms) и др.. Версии платформы совместимы и приложения, написанные для младших версий, могут быть выполнены на устройствах с более старшими версиями. Аналогично J2ME, Android платформа может иметь дополнительное, специфичное для данного устройства API.
Вопросы безопасности в Android решаются отличным от принятого в java способом. На этапе инсталляции из файла конфигурации читается набор разрешений, которые необходимы приложению для работы, и пользователю, производящему установку, предлагается утвердить их. В дальнейшем при запуске и работе приложения дополнительные запросы не выдаются. При этом приложение может быть подписано ключом из собственного, возможно, само подписанного сертификата разработчика. Требование подписи сертификата «всем-известным» центром сертификации отсутствует. Подпись используется только для идентификации разработчика и его приложений и определения, какие приложения могут запускаться в одном процессе и может ли одно приложение вызывать компоненты другого.
Загрузка готовых приложений в устройство рекомендуется производить через сайт www.android.com/market . Однако, это не является обязательным и, сняв (или установив) галочку в соответствующем пункте меню устройства, можно загрузить приложение откуда угодно, например, с компьютера разработчика, используя Wi-Fi. Загрузка приложения, оформленного как файл с расширением apk, производится через браузер устройства, установка после загрузки стартует автоматически. Конечно, при этом, на компьютере, откуда производится загрузка, должен быть запущен http сервер и он должен быть доступен из интернета или локальной сети.
Если вы планируете распространять свое приложение через android market, вам обязательно следует изучить, как правильно его лицензировать, но этот вопрос находится за рамками этого обзора (смотри http://developer.android.com/guide/publishing/licensing.html).
В распоряжении разработчика имеется полный набор инструментов для разработки и отладки приложений. Если исключить периодические зависания эмулятора, этот набор можно было бы назвать отличным. В разделе http://developer.android.com/resources/faq/troubleshooting.html имеется ряд советов как справляться с этими зависаниями.
В заключение несколько определений и особенностей, которые отличают android приложения от традиционных java приложений (J2ME, SE, EE).
Определения
Не запущенное приложение — это инсталлированный на устройстве apk архив (подписанный jar файл). Этот архив включает в себя компоненты приложения — Activity, Service, Broadcast Receiver и Content provider.
Запущенное приложение это набор задач. Каждая задача (task) является стеком компонент. Предположим, что Activity_1 (главный экран) вызывает Activity_2 (редактирование параметров), которая в свою очередь Activity_3 (редактирование отдельного параметра). Тогда на момент работы Activity_3 задача будет представлять из себя стек из 3-х компонент. Если Activity_3 завершает работу (вызовом метода finish()), то стек сокращается до 2-х компонент. Важно, что компонента из одного архива может вызвать компоненту из другого и эта компонента может оказаться в той же задаче. С другой стороны, компонента может вызвать другую компоненту из своего архива и последняя создаст новую задачу. В каждый момент времени только одна задача имеет на экране активное окно и получает команды от пользователя.
Activity можно рассматривать как окно, через которое пользователь взаимодействует с приложением. В отличии от J2ME оно может занимать не весь экран устройства и это усложняет его жизненный цикл (подробнее смотри документацию). Если Activity становиться неактивным (пользователь переключается на другую задачу и эта Activity целиком(!) закрывается окном другой Activity — см. определение 2), то эта компонента выгружается в первую очередь при нехватке памяти. Нажатие клавиши «Назад» завершает текущую Activity и активирует предыдущую компоненту из стека задачи).
Service — это фоновый код, который может быть активирован из другой компоненты. Это не нить и не процесс. Просто код в памяти. Этот код выгружается, когда все фоновые Activity уже выгружены. Система прилагает максимальные усилия для сохранения этого кода в памяти.
Broadcast Receiver это код, который вызывается в ответ на некоторое внешнее событие, например, низкий заряд батарей устройства. Никакого предположения о его наличии в памяти между обработкой событий не делается.
Последний тип компонент Content provider здесь рассматриваться не будет.
Теперь об особенностях.
В android нет понятия «завершить работу приложения». Система держит приложение в памяти, пока эта память ей не потребуется, и выгружает его исходя из своих собственных соображений. Метод finish() класса Activity или stopService() класса Service завершают работу компоненты (активити или сервиса), но не завершают приложение. Так, если в этих компонентах были запущены фоновые нити, то они продолжают работу.
Для взаимодействия компоненты обмениваются событиями (Intent) и каждая компонента имеет процедуру обработки таких событий. Обработка всех событий происходит в одной нити (если специально не указано другое). Для выполнения долгих задач компонента должна запустить специальную нитку, чтобы не блокировать пользовательский интерфейс. Эта или другая компонента должна позаботиться о корректной остановке этой нити при выгрузке компоненты или приложения.
Каждая компонента имеет свой жизненный цикл. Наиболее сложный цикл имеется у Activity. Нужно корректно обрабатывать все изменения статуса компоненты и освобождать или запрашивать вновь нужные ресурсы, а также управлять запущенными нитями.
Компонента, получающая управление при запуске android приложения, определяется в файле конфигурации заданием специального фильтра для события запуска. Запуск приложения рассматривается, как обычная обработка события, сгенерированного системой.
Если приложению требуется доступ к компонентам устройства, следует указать запрос соответствующих разрешений в файле конфигурации.
Может показаться странным, но схемы для проверки содержимого файла шаблона (layout) в природе не существует. Однако адрес http://schemas.android.com/apk/res/android, тем не менее, должен быть указан для префикса android. Я не нашел в сети внятного ответа, почему это так и почему нельзя сгенерировать схему для каждой версии API. С другой стороны, плагин для Eclipse нормально выводит подсказки и подчеркивает ошибки в этих файлах, так что проблем с проверкой этих файлов на этапе набора текста не возникает.