Подробный гайд по разработке Android-приложений с помощью Clean Architecture


Оглавление (нажмите, чтобы открыть):

Введение в разработку Andro >

Данный туториал поможет вам разобраться в очень полезном подходе по разработке приложений Clean Architecture.

С того времени, как я начал разработку Android-приложений, у меня сложилось впечатление, что разработку приложений можно было сделать лучше. За свою карьеру я сталкивался с множеством плохих решений, включая и свои собственные. Однако важно учиться на своих ошибках, чтобы не совершать их в дальнейшем. Я долго искал оптимальный подход к разработке и наконец наткнулся на . После того, как я применил данный подход к Android-приложениям я решил, что это заслуживает внимания.

Целью статьи является предоставление пошаговой инструкции разработки Android-приложений с применением подхода Clean Architecture. Суть моего подхода заключается в том, что я на довольно успешных примерах покажу вам все достоинства Clean Architecture.

Что такое Clean Architecture?

Я не собираюсь вдаваться в подробности, потому что есть статьи, в которых это объясняется лучше, чем смогу сделать я. Однако в следующем абзаце рассматривается ключевой вопрос, который вам необходимо знать, чтобы понять, как устроен подход Clean Architecture.

Как правило, в Clean Architecture код разделен на несколько уровней, по структуре схожей со структурой обычного лука, с одним правилом зависимости : внутренний уровень не должен зависеть от каких-либо внешних уровней. Это означает, что зависимости должны указываться внутри каждого уровня, чтобы не было зависимостей между уровнями (слоями) .

Clean Architecture, делает ваш код:
  • Независящим от фреймворков;
  • Тестируемым;
  • Независящим от UI;
  • Независящим от Базы данных;
  • Независимым от какого-либо внешнего воздействия.

Я надеюсь, что вам станет понятно, как каждый из этих пунктов достигается, за счет приведенных ниже примеров. Для более детального объяснения данного подхода я настоятельно рекомендую ознакомиться с этой статьей и данным видео .

Что это значит для Android?

Как правило, ваше приложение имеет произвольное количество уровней (слоев), однако если вам не нужна бизнес-логика Enterprise, то скорее всего у вас будет только 3 уровня:

  • Внешний: Уровень реализации;
  • Средний: Уровень интерфейса;
  • Внутренний: Уровень бизнес-логики.

Уровень реализации – это место где описывается основная структура приложения. Сюда входит любое содержимое Android такое, как: создание операций и фрагментов, отправка намерений, и другой структурный код наподобие сетевого кода и кода базы данных.

Целью уровня интерфейса является обеспечение взаимодействия/коммуникации между уровнем реализации и уровнем бизнес-логики.

Самым важным уровнем считается уровень бизнес логики . Данный уровень — это то, где вы фактически решаете поставленную задачу, собственно ради которой и создавалось приложение. Уровень бизнес-логики не содержит какого-либо структурного кода, и вы должны уметь запускать его без эмулятора. Таким образом, если вы будете придерживаться подобного подхода при построении бизнес-логики, то получится уровень легко тестируемый, разрабатываемый и его будет легко поддерживать. Пожалуй, это самая большая выгода при использовании Clean Architecture.

Каждый уровень, расположенный выше основного уровня, отвечает за преобразование моделей в модели нижнего уровня, перед тем как нижний уровень сможет их использовать. Нижний уровень не имеет ссылки на класс, который принадлежит внешнему уровню. Несмотря на это, внешний уровень может использовать и ссылочные модели внутреннего уровня. Опять же таки, это возможно благодаря нашему правилу зависимости . Это приводит к большему ресурсопотреблению, но оно является необходимым для того, чтобы убедиться, что наш код не привязан к какому-либо из уровней.

Почему преобразование является обязательным?

К примеру, ваши модели бизнес-логики могут оказаться некорректными для непосредственного отображения их пользователю. Возможно, вам необходимо отображать сочетание нескольких моделей бизнес-логики. По этой причине, я рекомендую создать класс ViewModel, который позволит упростить отображение моделей в интерфейсе пользователя. После чего вы просто используете класс преобразователя внешнего уровня для преобразования ваших бизнес-моделей в соответствующие ViewModel.

Еще одним примером может быть следующее: давайте скажем, что объект Cursor , принадлежит ContentProvider во внешнем уровне базы данных. Значит что внешний уровень, в первую очередь преобразует его в бизнес-модель внутреннего уровня, а затем уже отдаст его на обработку соответствующему уровню бизнес-логики.

Внизу статьи я добавлю больше ресурсов для изучения данного вопроса. Сейчас же мы уже знаем об основных принципах подхода Clean Architecture, а значит давайте «замараем» руки кодом. Далее я покажу вам как создать рабочий функционал с использованием Clean Architecture.

Как начать создание Чистых приложений?

Специально для вас я создал шаблонный проект , в котором уже есть все что вам нужно. Своего рода это стартовый набор для тех, кто хочет начать придерживаться Clean -подхода . Этот стартовый набор предназначен для скорейшего создания приложений с помощью уже встроенных, самых распространённых инструментов. Вы можете скачать этот набор абсолютно бесплатно , затем модифицировать его под свои нужды и создавать свои приложения.

Первые шаги по написанию новых прецедентов

Этот раздел будет объяснять весь необходимый вам код, для создания своих прецедентов с помощью подхода Clean Architecture, так скажем поверх шаблона, приведенного в предыдущем разделе. Прецедент – это просто некоторый изолированный функционал приложения. Прецедент может быть запущен или не может быть запущен пользователем (например, по нажатию пользователя).

Во-первых, давайте объясним структуру и терминологию этого подхода. И да, это просто то, как я создавал свои приложения, то есть это не является чем-то незыблемым, и вы можете организовывать свои приложения по-другому, как вам хочется.

Структура

Общая структура Android-приложения выглядит, как показано ниже:

  • Пакеты внешнего уровня: Интерфейс пользователя, хранилище, сеть и т.д.;
  • Пакеты среднего уровня: Представители , конвертеры;
  • Пакеты внутреннего уровня: Interactors, модели, репозитории, исполнители.

Внешний уровень

Как уже было сказано, это то, где описываются детали структуры.

Интерфейс пользователя (UI ) – Это то, куда вы помещаете все ваши Операции, Фрагменты, Адаптеры и любой другой Android-код, связанный с интерфейсом пользователя.

Хранилище – Отдельный код для базы данных, который реализует интерфейс наших Интеракторов, используемых для доступа к базе данных и для хранения данных. Например, сюда включается Поставщик контента или ORM-ы такие, как DBFlow .

Исполнитель (executor ) – данный пакет содержит код для запуска Interactor-классов в фоновом режиме с помощью рабочего потока-исполнителя. Чаще всего вам не придется изменять этот пакет.

Простой пример

В этом примере, нашим прецедентом будет: «Приветствие пользователя сообщением, когда приложение запущено и данное сообщение помещено в базу данных.» Данный пример будет наглядной демонстрацией того, как создать следующие пакеты, необходимые для корректной работы нашего прецедента:

Первые два пункта относятся к внешнему уровню, в то время как последний относится к внутреннему/основному уровню.

Пакет представления ответственен за все, что связано с отображением вещей на экране, он содержит весь стек шаблона проектирования MVP . Это означает, что он содержит в себе как UI, так и Presenter-пакеты, даже если они относятся к разным уровням.

Отлично – меньше слов, больше кода!

Создание нового Interactor-а (внутренний/основной уровень)

На самом деле, вы можете начать разработку своего приложения с любого уровня представленной архитектуру, но я рекомендую начать именно с основного уровня бизнес-логики. Вы можете написать весь необходимы для этого код, протестировать его и убедиться, что он работает даже без создания операции.

Итак, давайте начнем создание Interactor-а. Interactor – это то место, где располагается основная логика работы нашего прецедента. Все Interactor -ы запускаются в фоновом потоке, поэтому не должно быть никакого воздействия на производительность интерфейса пользователя . Давайте создадим новый Interactor с приятным названием «WelcomingInteractor ».

public interface WelcomingInteractor extends Interactor <

void onMessageRetrieved (String message ) ;

void onRetrievalFailed (String error ) ;

Callback отвечает за общение с интерфейсом пользователя (UI) в основном потоке, мы помещаем его в интерфейс Interactor-а, поэтому нет необходимости в подобном названии «WelcomingInteractorCallback», чтобы отличать его от других callback-ов. Теперь реализуем логику получения сообщения. Давайте скажем, что у нас есть интерфейс MessageRepository , в котором будет наше сообщение приветствия.

public interface MessageRepository

public interface MessageRepository <

public class WelcomingInteractorImpl extends AbstractInteractor implements WelcomingInteractor <

private void notifyError () <

public void run () <

mCallback . onRetrievalFailed ( «Nothing to welcome you with:(» ) ;

private void postMessage (final String msg ) <

mMainThread . post (new Runnable () <

public void run () <

mCallback . onMessageRetrieved (msg ) ;

public void run () <

Final String message = mMessageRepository . getWelcomeMessage () ;

// проверяем, получили ли мы сообщение

if (message == null || message . length () == 0 ) <

// уведомляем об ошибке основной поток

// мы получили наше сообщение, уведомляем об этом UI в основном потоке

Что же, взглянем на зависимости, создаваемые нашим Interactor:Этот фрагмент кода, пытается получить сообщение, затем переслать его или же отправить сообщение об ошибке интерфейсу пользователя, чтобы он отобразил сообщение или ошибку. Для этого мы уведомляем интерфейс пользователя с помощью нашего callback-а, который по факту и будет Presenter-ом. Собственно, в этом и заключается суть всей нашей бизнес-логики . Все что нам остается – это построить структурные зависимости.

import com.kodelabs.boilerplate.domain.executor.Executor; import com.kodelabs.boilerplate.domain.executor.MainThread; import com.kodelabs.boilerplate.domain.interactors.WelcomingInteractor; import com.kodelabs.boilerplate.domain.interactors.base.AbstractInteractor; import com.kodelabs.boilerplate.domain.repository.MessageRepository;

import com . kodelabs . boilerplate . domain . executor . Executor ;

import com . kodelabs . boilerplate . domain . executor . MainThread ;

import com . kodelabs . boilerplate . domain . interactors . WelcomingInteractor ;

import com . kodelabs . boilerplate . domain . interactors . base . AbstractInteractor ;

import com . kodelabs . boilerplate . domain . repository . MessageRepository ;

Как вы можете заметить, здесь нет ни одного упоминания о каком-либо Android -коде . Это и есть главное преимущество данного подхода. Также вы можете увидеть, что пункт: «Независимость от фреймворков» все также соблюдается. Кроме того, нам не нужно отдельно определять интерфейс пользователя или базу данных, мы просто вызываем методы интерфейса, которые кто-то, где-то на внешнем уровне реализует. Следовательно, мы независим от UI и независим от Базы данных .

Тестирование нашего Interactor-а

На данный момент мы можем запустить и начать тестирование нашего Interactor -а без запуска эмулятора . Поэтому давайте напишем простой Junit-тест, чтобы убедиться, что все работает:

. @Test public vo ; when(mMessageRepository.getWelcomeMessage()) .thenReturn(msg); WelcomingInteractorImpl interactor = new WelcomingInteractorImpl(mExecutor, mMainThread, mMockedCallback, mMessageRepository); interactor.run(); Mockito.verify(mMessageRepository).getWelcomeMessage(); Mockito.verifyNoMoreInteractions(mMessageRepository); Mockito.verify(mMockedCallback).onMessageRetrieved(msg); >

public void testWelcomeMessageFound () throws Exception <

String msg = «Welcome, friend!» ;

when (mMessageRepository . getWelcomeMessage () )

WelcomingInteractorImpl interactor = new WelcomingInteractorImpl (

Mockito . verify (mMessageRepository ) . getWelcomeMessage () ;

Mockito . verifyNoMoreInteractions (mMessageRepository ) ;

Mockito . verify (mMockedCallback ) . onMessageRetrieved (msg ) ;

И вновь, этот Interactor даже не подозревает, что будет находиться внутри Android-приложения. Это доказывает, что наша бизнес-логика является тестируемой , а это был наш пункт номер два.

Создание уровня представления

Код представления относится ко внешнему уровню подхода Clean Architecture. Уровень представления состоит из структурно зависимого кода, который отвечает за отображение интерфейса пользователя, собственно, пользователю. Мы будем использовать класс MainActivity для отображения нашего приветствующего сообщения пользователю, когда приложение возобновляет свою работу.

Давайте начнем с создания интерфейса нашего Presenter и Отображения (View ). Единственное, что должно делать наше отображение – это отображать приветствующее сообщение:

public interface MainPresenter extends BasePresenter <

interface View extends BaseView <

void displayWelcomeMessage (String msg ) ;

Итак, как и где мы запускаем наш Interactor, когда приложение возобновляет работу? Все, что не имеет строгой привязки к отображению, должно помещаться в класс Presenter. Это помогает достичь принципа Разделения ответственности и предотвратить классы Операций от чрезмерного увеличения размера кода. Сюда включается весь код, который работает с Interactor-ми.

В нашем классе MainActivity мы переопределяем метод onResume() :

Все Presenter-объекты реализуют метод resume() , при наследовании BasePresenter .

Примечание : Самые внимательные читатели могли заметить, что я добавил Android-методы жизненного цикла в интерфейс BasePresenter в качестве вспомогательных методов, хотя сам Presenter находится на более низком уровне. Наш Presenter должен знать все на уровне UI, к примеру, что что-то на этом уровне имеет жизненный цикл. Тем не менее, здесь я не указываю конкретное событие , так как каждый UI для конкретного пользователя может отрабатывать разные события, в зависимости от действий пользователя. Представьте, я назвал его onUIShow() вместо onResume() . Теперь все хорошо, верно? ��

Мы запускаем Interactor внутри класса MainPresenter в методе resume() :

WelcomingInteractor interactor = new WelcomingInteractorImpl (

Метод execute () просто выполняет метод run () объекта WelcomingInteractorImpl в фоновом потоке. Метод run () вы можете увидеть в разделе Создание нового Interactor .

Вы также могли заметить, что поведение Interactor-а схоже с поведением класса AsyncTask . Так как вы предоставляете все необходимое для его запуска и выполнения. Тут вы можете спросить, а почему мы не используем AsyncTask ? Да потому что это Android-код, и вам нужен будет эмулятор для его запуска и тестирования.

Мы предоставляем несколько вещей нашему Interactor-у:

  • Экземпляр ThreadExecutor , который отвечает за выполенение Interactor-а в фоновом потоке. Я чаще всего создаю его как singleton. Этот класс также располагается внутри domain-пакета и нет необходимости реализовывать его во внешнем уровне;
  • Экземпляр MainThreadImpl , который отвечает за отправку запущенных потоков Interactor-а в главный поток приложения. Основные потоки имеют доступ к использованию определённого структурного кода и поэтому мы должны реализовывать их во внешнем уровне;
  • Также вы могли обратить внимание на то, что мы предоставляем this нашему Interactor-у. MainPresenter – это callback-объект, который используется Interactor-ом для уведомления UI о каких-либо событиях;
  • Кроме того, мы предоставляем экземпляр WelcomeMessageRepository , который отвечает за реализацию интерфейса MessageRepository , который в свою очередь использует Interactor. WelcomeMessageRepository будет рассмотрен позже, в разделе Создание уровня хранения .

Примечание : Поскольку существует множество вещей, которые необходимо связывать каждый раз с Interactor-ом, то будет полезен следующий фреймворк для внедрения зависимостей: Dagger 2 (и подобные ему). Но я его использую здесь не для того чтобы что-то упростить. Свою структуру вы вольны сами выбирать, и то какие фреймворки использовать также ваше право.

Что же касается this , то MainPresenter класса MainActivity действительно реализует callback-интерфейс:

public class MainPresenterImpl extends AbstractPresenter implements MainPresenter, WelcomingInteractor.Callback <

public class MainPresenterImpl extends AbstractPresenter implements MainPresenter , WelcomingInteractor . Callback <

@Override public void onMessageRetrieved(String message) < mView.hideProgress(); mView.displayWelcomeMessage(message); >@Override public void onRetrievalFailed(String error)

В этой статье мы рассмотрим архитектуру Android-приложений.

Откровенно говоря, официальную Google по этой теме я считаю не очень полезной. Детально отвечая на вопрос «как», она совсем не объясняет «что» и «почему». Итак, вот моя версия, и, я надеюсь, она внесёт некоторую ясность. Да, кстати, я полностью одобряю чтение статей Google, поскольку они содержат полезную информацию, повторять которую я не собираюсь.

Архитектура ОС Android — немного истории

Разработка ОС Android была начата в 2003 молодой компанией Android Inc. В 2005 году эта компания была куплена Google. Я считаю, что главные особенности архитектуры Android были определены именно в этот период. Это заслуга не только Android Inc; архитектурные концепции и финансовые ресурсы Google оказали решающее влияние на архитектуру Android. Далее я приведу несколько примеров.

Если вы помните, 2003-2005 года были ознаменованы повышенным вниманием к AJAX приложениям. Я думаю, это оказало основополагающее влияние на архитектуру Android: во многих аспектах она ближе к архитектуре типичного AJAX приложения, нежели к десктопному GUI приложению, написанному на Java, C#, C++, VB и тп.

Не знаю, почему так произошло. Моя догадка — это придумал кто-то из Google в тот период, когда насыщенные интернет-приложения (Rich Internet Applications, RIA) в духе Google Docs или Gmail считались решением всех проблем. По-моему, эту идею нельзя назвать ни плохой, ни хорошей. Просто помните, что Android-приложения очень сильно отличаются от десктопных.

Влияние архитектурной философии Eclipse заметно в выборе принципа реализации GUI, который больше похоже на SWT, нежели на Swing.

В стандартах оформления кода Android присутствует «венгерская нотация», рождённая в стенах MS. Можно предположить, что тот, кто писал эти стандарты, ранее занимался разработкой под Windows.

Архитектурные уровни Android

Уровень Linux

Грубо говоря, у вас два пути: реализовывать собственные идеи, начав с нуля или же использовать существующую ОС и адаптировать её под свои устройства.

Реализация с нуля всегда звучит захватывающе для программистов. В эти моменты мы все верим в то, что в этот раз мы всё сделаем лучше, чем делают другие, и даже лучше, чем мы сами делали ранее.

Тем не менее, это не всегда практично. Например, использование ядра Linux заметно уменьшило стоимость разработки (возможно где-то и без того чрезмерно большую). Согласитесь, если кто-то решит создать нечто, напоминающее ядро Linux в его сегодняшнем состоянии, ему потребуется несколько миллионов долларов.

Если вы руководите Android Inc, то у вас по определению не может быть столько денег. Если вы руководите Google, то у вас такие деньги найдутся, но вы, скорее всего, подумаете дважды, прежде чем потратить их на создание собственной ОС. Так же вы потратите несколько лет, прежде чем достигните сегодняшнего состояния Linux; несколько лет задержки могут стать слишком большим опозданием при выходе на рынок.

В подобной ситуации компания Apple решила построить Mac OS на основе Free BSD. Android Inc приняла решение использовать Linux как основу для Android. Исходники как Free BSD, так и Linux, находятся в свободном доступе и предоставляют собой хорошую основу для любых разработок, будь то Apple или Google.

Но в то время запустить стандартный Linux на мобильном устройстве было невозможно (сейчас это уже не так). Устройства имели слишком мало оперативной и энергонезависимой памяти. Процессоры были значительно медленнее по сравнению с процессорами компьютеров, где обычно используется Linux. Как результат, разработчики Android решили минимизировать системные требования Linux.

Если рассматривать Linux на высоком уровне, то это комбинация ядра (без которого нельзя обойтись) и множества других, необязательных частей. Можно даже запустить одно ядро, без чего бы то ни было ещё. Так, Google вынуждена в любом случае использовать ядро Linux как часть ОС Android. Кроме того, были рассмотрены необязательные части и из них выбрано самое необходимое. Например, были добавлены сетевой фаервол IPTables и оболочка Ash. Любопытно, что добавили именно Ash, а не Bash, не смотря на то, что последний на порядок мощнее; вероятно, это решение было основано на том, что Ash менее требователен к ресурсам.

Разработчики Android модифицировали ядро Linux, добавив поддержку железа, используемого в мобильных устройствах и, чаще всего, недоступного на компьютерах.

Выбор Linux в качестве основы оказал огромное влияние на все аспекты ОС Android. Сборка Android, по сути, есть вариация процесса сборки Linux. Код Android находится под управлением git (инструмент, разработанный для управления кодом Linux). И так далее.

Пускай это всё и интересно, но вы, скорее всего, никогда не коснётесь всех этих специфических моментов до тех пор, пока ваша цель просто разработать приложения под Android. Исключение может составить разве что обзор файловой системы с помощью команд ash. Главное, что вы должны знать, разрабатывая приложения под Android — это уровень инфраструктуры приложения.

Вы можете спросить, как же быть, если необходимо разработать нативное приложение для Android? Google настоятельно не рекомендует делать этого. Технически, конечно, это возможно, но в дальнейшем у вас не будет возможности распространять это приложение нормальным способом. Так что подумайте дважды, прежде чем начать нативную разработку под Android, если конечно, вы не работает над Android Open Source Project (AOSP), т.е. собственно ОС Android.

Уровень инфраструктуры приложения

Apple решила использовать Objective-C как язык программирования и среду выполнения приложения iOS. Objective-C выглядит более или менее естественным выбором для ОС, в основе которой лежит Free BSD. Можно рассматривать Objective-C как обычный C++ с кастомным препроцессором, который добавляет некоторые специфические лингвистические конструкции. Почему же нельзя использовать стандартный C++, на котором написана Free BSD? Мне кажется причина в том, что Apple старается всё делать в своём, «эппловском» стиле.

Основная идея в том, что приложения iOS написаны более или менее на том же языке, что и стоящая за ними ОС.

Android-приложения сильно отличаются в этом смысле. Они написаны на Java, а это совсем другая технология, нежели C++ (хотя синтаксис и унаследован от C++).

Я думаю, основная причина состоит в необходимости одному и тому же приложению работать на различном аппаратном обеспечении. Эта проблема имеет место лишь для ОС Android; у ребят из Apple такой проблемы нет. iOS работает только на оборудовании собственного производства, и Apple полностью контролирует весь процесс. Для Android же всё наоборот: Google не контролирует производителей аппаратных средств. Например, ОС Android работает на процессорах с архитектурой x86, ARM и Atom (в комментах подсказывают, что x86 включает в себя Atom, и Android работает на x86, ARM, PPC и MIPS — примечание переводчика ). На бинарном уровне эти архитектуры несовместимы.

Если бы архитекторы ОС Android выбрали тот же путь, что и архитекторы из Apple, разработчики приложений под Android были бы вынуждены распространять несколько версий одного и того же приложения одновременно. Это стало бы серьёзной проблемой, которая могла бы привести к краху всего проекта Android.


Для того, чтобы одно и то же приложение могло работать на разном аппаратном обеспечении, компания Google использовала контейнер-ориентированную архитектуру (container-based architecture). В такой архитектуре двоичный код выполняется программным контейнером и изолируется от деталей конкретного аппаратного обеспечения. Примеры всем знакомы — Java и C#. В обоих языках двоичный код не зависит от специфики аппаратного обеспечения и выполняется виртуальной машиной.

Конечно, есть и другой способ достигнуть независимости от аппаратного обеспечения на уровне двоичного кода. Как один из вариантов, можно использовать эмулятор аппаратного обеспечения, так же известный как QEMU . Он позволяет эмулировать, например, устройство с процессором ARM на платформе x86 и так далее. Google могла бы использовать C++ как язык для разработки приложений внутри эмуляторов. Действительно, Google использует такой подход в своих эмуляторах Android, которые построены на основе QEMU.

Очень хорошо, что они не пошли по такому пути, поскольку тогда кому-то пришлось бы запускать ОС на эмуляторе, требующем намного больше ресурсов, и, как итог, скорость работы снизилась бы. Для достижения наилучшего быстродействия эмуляция была оставлена только там, где этого нельзя было избежать, в нашем случае — в Android-приложениях.

Как бы то ни было, компания Google пришла к решению использовать Java как основной язык разработки приложений и среды их выполнения.

Я думаю, это было критически важное архитектурное решение, которое поставило Android в стороне от остальных мобильных ОС на основе Linux, представленных в настоящее время. Насколько мне известно, ни у одной из них нет совместимости двоичного кода на уровне приложений. Возьмём для примера MeeGo . Она использует C++ и фреймворк Qt ; не смотря на то, что Qt кроссплатформенный, необходимость делать разные сборки для разных платформ не исчезает.

Выбрав Java, нужно было решить, какую виртуальную машину (JVM) использовать. Ввиду ограниченности ресурсов использование стандартной JVM было затруднено. Единственным возможным выбором было использование Java ME JVM, разработанной для мобильных устройств. Однако счастье Google было бы неполным без разработки собственной виртуальной машины, и появилась Dalvik VM .

Dalvik VM отличается от других виртуальных Java-машин следующим:

  • Она использует специальный формат DEX для хранения двоичных кодов, в противовес форматам JAR и Pack200, которые являются стандартом для других виртуальных Java-машинах. Компания Google заявила, что бинарники DEX меньше, чем JAR. Я думаю, с тем же успехом они могли бы использовать Pack200, но они решили пойти своим путём.
  • Dalvik VM оптимизирована для выполнения нескольких процессов одновременно.
  • Dalvik VM использует архитектуру, основанную на регистрах против стековой архитектуры в других JVM, что приводит к увеличению скорости выполнения и уменьшению размеров бинарников.
  • Она использует собственный набор инструкций (а не стандартный байткод JVM)
  • Возможен запуск (если необходимо) нескольких независимых Android-приложений в одном процессе
  • Выполнение приложения может охватывать несколько процессов Dalvik VM «естественным образом» (позже мы обсудим, что это значит). Для поддержи этого добавлено:
    • Специальный механизм сериализации объектов, основанный на классах Parcel и Parcelable. Функционально преследуются те же цели, что и Java Serializable, но в результате данные имеют меньший объём и потенциально более терпимы к версионным изменениям классов.
    • Особый способ для выполнения вызовов между процессами (inter process calls, IPC), основный на Android Interface Definition Language (AIDL).
  • До Android 2.2 Dalvik VM не поддерживала JIT-компиляцию, что было серьёзным ударом по производительности. Начиная с версии 2.2, скорость выполнения часто используемых приложений

Разрабатывать под Android я начал относительно недавно (3 месяца назад) и первое с чем столкнулся — очень малое количество «русскоязычного» материала по этой теме, особенно, касательно таких вопросов как продумывание интерфейса или использование встроенных фич среды Android. Все что я находил, в большинстве своем, сводилось или к переводу статей с https://developer.android.com или примерами решающими конкретную задачу.

Скажу сразу, что на истину в последней инстанции я не претендую, и лишь хочу донести до начинающих Android разработчиков материал, который, возможно, поможет им в их дальнейшей деятельности.

Я предполагаю выдать несколько статей, целью которых, будет являться раскрыть суть проектирования и разработки приложений под платформу Android.

Конкретно в этой статье пойдет речь о программном обеспечении, необходимом для начала разработки под Android, литературе и немного о структуре приложения.

Быстрый старт

Для начала разработки вам необходимо 2 вещи: Android SDK (можно скачать с https://developer.android.com/intl/zh-TW/sdk/index.html) и, собственно, среда разработки.

SDK устанавливается через интернет (сам установочный модуль весит немного, на момент когда начинал я — около 10 Мб). Запустив установочный модуль (SDK Setup.exe) сразу запустится проверка репозитория на наличие обновлений API и прочих приблуд, по умолчанию ваше SDK пустое, и по любому для начала работы надо будет что-то из предложенного выбора обновлений скачать. Внимание! Возможно возникнет проблема соединением с репозиторием, для того чтобы её решить нужно перейти в Settings и поставить галочку напротив «Force https: \\ что-то там еще. » и попробовать соединится с репозиторием снова.

Размер загружаемого контента будет зависить от вашего выбора (в текущем варианте около 1 Гб). Для того чтобы ускорить загрузку, можно ограничить свой выбор 1 — 2 модулями (например SDK Platform Android API 1.5 и 2.0). В целом, все зависит от того под какую версию Android-а вы планируете разрабатывать. Наиболее популярные на данный момент версии API: 1.5, 1.6, 2.0. Лично я качал все.

Внимание! Учтите тот факт, что это вам не iPhone! Платформа постоянно растет и развивается и то что хорошо компилилось на Android API 1.5, под 2.0 вам выдаст предупреждение о том, что вы пользуетесь устаревшими библиотеками или методами (эх, Java ;-)). Плюс, в новых версиях API доступны методы недоступные в более ранних, для того чтобы наглядно увидеть какие библиотеки поддерживает данное API, можно на https://developer.android.com/intl/zh-TW/reference/android/app/Activity.html в правом верхнем углу поставить галочку напротив надписи «Filter by API level» и указать интересующую вас версию API (по состоянию на март 2010 года — максимальная 7-ка).

Увидеть загруженные модули можно перейдя по вкладке «Installed Packages». Обновить и удалить их можно там же.

Итак, SDK вы загрузили. теперь требуется создать эмуляторы для отладки ваших приложений. Для того чтобы создать эмулятор, необходимо в том же модуле загрузки, перейти по вкладке Virtual Device. В открывшемся окне перед вами появится таблица (сначала пустая) со списком созданных эмуляторов. Функционал окна позволяет вам создавать, удалять, «ремонтировать» (данную функцию никогда не использовал), просматривать информацию и запускать эмуляторы. Нажав на кнопку «New» вы попадете в мастер создания эмуляторов.

В мастере, вам необходимо указать имя эмулятора, целевую платформу (выпадающий список Target), объем SD карты памяти (обратите внимание, по умолчанию, объём в Мб), скин (тип и разрешение экрана устройства), а так же поддерживаемое «железо»: поддержка камеры, работы батареи, GPS и т.д. (нажав на кнопку «New» вы попадает в окно выбора свойства эмулятора и его значения).

Для завершения создания эмулятора нужно нажать на «Create AVD». Внимание! После создания эмулятора невозможно поменять его свойства. Если вам необходим эмулятор с другими свойствами — то прийдется создавать новый. После создания эмулятора, считайте, что пол пути для «быстрого» старта уже пройдено.

Теперь переходим к среде разработки. Выбор тут у нас невелик: или ставим плагин под Eclipse/NetBeans (https://developer.android.com/intl/zh-TW/sdk/eclipse-adt.html и https://kenai.com/projects/nbandroid/ , соответственно) или качаем уже собранную IDE (например, MOTODEV Studio for Android 1.1 с сайта https://developer.motorola.com/docstools/motodevstudio/). Впринципе, установив плагин под Eclipse в конечном итоге вы и получите, почти, MOTODEV Studio for Android 1.1, но вам нужна эта возня. Что же касается плагина под NetBeans, то я не знаю в каком он состоянии сейчас, но та версия с которой мне приходилось работать не имела визуального редактора UI, что, мягко говоря, тормозило работу.

Лично мой выбор пал на второй вариант (MOTODEV Studio рулит;-)). Всю необходимую информацию по установке и настройке среды разработки, вы найдете без проблем погуглив с пол часика.

Чтиво

Наличие русскоязычной литературы по разработке под Android, лично я, пока не наблюдал. Поэтому сразу перейдем к англоязычному контенту. Итак, для меня стали Библией Android: Apress Beginning Android и Apress Pro Android.

В этих книгах достаточно популярно и доступно описывается что, зачем и почему, а так же приводятся достаточно понятные примеры. Стоит отметить, что обе книги в качестве базы рассматривают платформу Android 1.5, версии книг под 2.0 (Apress Beginning Android 2 и Apress Pro Android 2, соответственно) хоть и присутствуют на сайте издателя, для загрузки мне еще не попадались.

Много справочного материала (правда с частично неработающими примерами;-)) есть на основном сайте проекта (https://developer.android.com), полезным будет не только почитать реферы и DevGuide но и посмотреть видео уроки.

Структура

О том как создавать HelloWorld приложение вам поведают и без меня, поэтому, сразу перейдем к структуре. В принципе, все что я сейчас поведаю вы можете почитать и из выше предложенного вам чтива, но я постараюсь вам сэкономить немножко времени.

Итак, условно, приложение под Android состоит из 3-х блоков:

  1. Манифест (AndroidManifest.xml) — дескриптор файла приложения, обязательный элемент, в котором определены такие страшные штуки как: activities, content providers, services, intent receivers (о них пойдет речь в следующих статьях). А так же в манифесте вы можете описывать «разрешения» (permissions) необходимые для работы вашего приложения. Более подробно манифест я планирую описать в следующих статьях.
  2. Папка «src» — папка содержащая весь исходный код программы, является обязательной.
  3. Папка «res» — самая вкусная папка, в ней содержаться все «ресурсы» приложения;-) Вы пока еще об этом не знаете, но она сильно вам облегчит жизнь, более того, я бы сказал, что самое ВСЕ разработчика для Android. Наличие данной папки для проекта обязательно.

Кроме того, корневая папка приложения может еще содержать в себе папку «assets», данная папка не обязательна и может содержать в себе различные вспомогательные ресурсы (другие папки и файлы).

И еще одна тонкость. В корень приложения можно забросить папку «libs», в которую, в последующем можно будет добавить нативные С/С++ библиотеки (о них мы тоже как-нибудь поговорим, но позже).

В процессе сборки приложения, появится папка «gen» с вложенным пакетом и файлом R.xml — это тоже очень полезный файл, в котором содержаться дисрипторы ресурсов, генерится средой разработки и лезть туда руками крайне не рекомендуется.

Ресурсы (кроме графических) как и манифест представленны в виде XML файлов. И те кто что-то понимает в Java EE сразу для себя найдут много сходного, и будут правы. Структуру XML для различных типов ресурсов я так же планирую расписать в следующих статьях, а пока лишь распишу вкратце содержимое папки «res»:

  1. drawable -папка, которая содержит файлы с графическим контентом а так же xml-предписания работы с ними, не обязательна.
  2. anim — папка, в которой содержаться xml-файлы с описанием анимации.
  3. layout — папка, содержит xml описание слоем для реализации UI.
  4. values — папка — контейнер для таких xml-файлов как: strings, styles, colors, dimens, arrays (контент обозначенных файлов соответствует их названию).
  5. xml — папка содержащая различные xml-файлы вспомогательного характера.
  6. raw — папка для хранения не XML данных используемых в приложении.

Казалось бы, судя по структуре все и так понятно. Но уверяю вас — это только вершина айсберга. Путем нехитрых манипуляций с этими папками и их контентом можно достаточно легко реализовывать такие вещи как интернационализация и локализация вашего приложения, а так же реализовать, примерно, до 80% работы с вашим UI.

Подводя итоги

В ходе написания статьи, я понял что меня «понесло»..и писать статей мне еще придется целую кучу, дабы излить все то, что «накипело» по разработке для Android.

Надеюсь, кому-то она покажется полезной, а я, в свою очередь, постараюсь как можно быстрее выдать продолжение.

Изначально разрабатывалась компанией Android Inc., которую затем (июль, 2005) купила Google. Впоследствии (ноябрь, 2007) Google инициировала создание бизнес-альянса Open Handset Alliance (в его состав вошли Google, HTC, Intel, Motorola, Nvidia и другие компании), который и занимается сейчас поддержкой и дальнейшим развитием платформы.

С момента выхода первой версии (сентябрь, 2008) произошло несколько обновлений системы. Эти обновления, как правило, касаются исправления обнаруженных ошибок и добавления нового функционала в систему. Каждая версия системы получает собственное кодовое имя на тему десерта.

1.2. Инструментарий разработчика

Перед тем, как приступить к созданию Android -приложений, необходимо выбрать подходящий инструментарий разработки.

Как правило, разработка Android -приложений осуществляется на языке Java . Поэтому, в первую очередь , необходимо установить Java Development Kit ( JDK ). Но для начала следует разобраться, что представляет из себя Java .

Java – это объектно-ориентированный язык программирования . Программы на Java транслируются в байт-код, выполняемый виртуальной машиной Java , которая обрабатывает байтовый код и передает инструкции оборудованию как интерпретатор . Достоинство подобного способа выполнения программ заключается в полной независимости байт-кода от операционной системы и оборудования, что позволяет выполнять Java -приложения на любом устройстве, для которого существует соответствующая виртуальная машина . Другой важной особенностью Java является гибкая система безопасности благодаря тому, что исполнение программы полностью контролируется виртуальной машиной. Любые операции , которые превышают установленные полномочия программы (например, попытка несанкционированного доступа к данным или соединения с другим компьютером) вызывают немедленное прерывание . Следует заметить, что фактически, большинство архитектурных решений, принятых при создании Java , было продиктовано желанием предоставить синтаксис , схожий с С/C++. В Java используются практически идентичные соглашения для объявления переменных, передачи параметров и операторов. Поэтому те, кто уже имеет опыт программирования на C/C++, смогут быстро освоиться и начать писать Java -приложения.

JDK – это бесплатно распространяемый комплект разработчика приложений на языке Java , включающий в себя компилятор Java , стандартные библиотеки классов Java , примеры, документацию, различные утилиты и исполнительную систему Java Runtime Environment ( JRE ). В состав JDK не входит интегрированная среда разработки (Integrated Development Environment ). Поэтому после того, как будет установлен JDK , следует установить IDE .

Существует несколько популярных сред разработки, но в данном курсе мы остановим свой выбор на Eclipse IDE и соответствующем для нее плагине Android Development Tools ( ADT ).

Android Software Development Kit ( SDK ) содержит множество инструментов и утилит для создания и тестирования приложений. Например, с помощью SDK Manager можно установить Android API любой версии (Рис. 1.1), а также проверить репозиторий на наличие доступных, но еще не установленных пакетов и архивов.

Android Native Development Kit (NDK) позволяет осуществлять разработку Android -приложений на языке C/C++. Зачем это может потребоваться? Есть несколько причин. Например, необходимость использовать код, который уже написан для нативной платформы, или ускорение выполнения критических кусков кода.

1.3. Архитектура Android

Рассмотрим основные компоненты операционной системы Android (Рис. 1.2).

Applications. Android поставляется с набором основных приложений, включающим календарь, карты, браузер , менеджер контактов и другие. Все перечисленные приложения написаны на Java .

Application Framework. Предоставляя открытую платформу разработки, Android дает разработчикам возможность создавать гибкие и инновационные приложения. Разработчики могут использовать аппаратные возможности устройства, получать информацию о местоположении, выполнять задачи в фоновом режиме, устанавливать оповещения и многое другое. Разработчики имеют полный доступ к тем же API , что используются в основных приложениях.

Архитектура приложений разработана с целью упрощения повторного использования компонентов; любое приложение может «публиковать» свои возможности и любое другое приложение может затем использовать эти возможности (с учетом ограничений безопасности). Этот же механизм позволяет заменять стандартные компоненты на пользовательские.

Libraries. Android включает в себя набор C/C++ библиотек, используемых различными компонентами системы. Эти возможности доступны разработчикам в контексте применения Android Aplication Framework. Некоторые основные библиотеки, перечислены ниже:

  • Mедиа библиотеки – эти библиотеки предоставляют поддержку воспроизведения и записи многих популярных аудио, видео форматов и форматов изображений, в том числе MPEG4, MP3, AAC, AMR, JPG, PNG и других;
  • Surface Manager – управляет доступом к подсистеме отображения 2D и 3D графических слоев;
  • LibWebCore – современный веб-движок, на котором построен браузер Android;
  • SGL – основной графический движок 2D;
  • 3D библиотеки – реализованы на основе OpenGL; библиотеки используют либо аппаратное 3D-ускорение (при его наличии), либо включены программно;
  • FreeType – поддержка растровых и векторных шрифтов
  • SQLite – механизм базы данных, доступной для всех приложений.

Android Runtime. Android включает в себя набор основных библиотек, которые обеспечивают большинство функций, доступных в библиотеках Java . Каждое приложение Android работает в своем собственном процессе, со своим собственным экземпляром виртуальной машины Dalvik. Dalvik была написана так, что устройство может работать эффективно с несколькими виртуальными машинами одновременно.

Dalvik проектировалась специально под платформу Android . Виртуальная машина оптимизирована для низкого потребления памяти и работы на мобильном аппаратном обеспечении. Dalvik использует собственный байт-код. Android -приложения переводятся компилятором в специальный машинно-независимый низкоуровневый код. И именно Dalvik интерпретирует и выполняет такую программу при выполнении на платформе. Кроме того, с помощью специальной утилиты, входящей в состав Android SDK , Dalvik способна переводить байт-коды Java в коды собственного формата и также исполнять их в своей виртуальной среде.

Linux Kernel. Android основан на Linux версии 2.6 с основными системными службами – безопасность , управление памятью , управление процессами и модель драйверов. Разработчики Android модифицировали ядро Linux, добавив поддержку аппаратного обеспечения, используемого в мобильных устройствах и, чаще всего, недоступного на компьютерах.

1.4. Обзор Java-интерфейсов

Для прикладного программиста Android – это набор интерфейсов на языке Java . Рассмотрим, как он организован. В основе набора – пакеты, входящие в

Приложения для Android пишутся на языке программирования Java. Инструменты Android SDK (Software Development Kit – комплект разработки программного обеспечения) компилируют написанный вами код — и все требуемые файлы данных и ресурсов — в файл APK – программный пакет Android , который представляет собой файл архива с расширением.apk . В файле APK находится все, что требуется для работы Android-приложения, и он позволяет установить приложение на любом устройстве под управлением системы Android.

Каждое приложение Android, установленное на устройстве, работает в собственной «песочнице» (изолированной программной среде):

  • операционная система Android представляет собой многопользовательскую систему Linux, в которой каждое приложение является отдельным пользователем;
  • по умолчанию система назначает каждому приложению уникальный идентификатор пользователя Linux (этот идентификатор используется только системой и неизвестен приложению); система устанавливает полномочия для всех файлов в приложении, с тем чтобы доступ к ним был разрешен только пользователю с идентификатором, назначенным этому приложению;
  • у каждого процесса имеется собственная виртуальная машина (ВМ), так что код приложения выполняется изолированно от других приложений;
  • по умолчанию каждое приложение выполняется в собственном процессе Linux. Android запускает процесс, когда требуется выполнить какой-либо компонент приложения, а затем завершает процесс, когда он больше не нужен либо когда системе требуется освободить память для других приложений.

Таким образом система Android реализует принцип предоставления минимальных прав . То есть каждое приложение по умолчанию имеет доступ только к тем компонентам, которые ему необходимы для работы, и ни к каким другим. Благодаря этому формируется исключительно безопасная среда, в которой приложение не имеет доступа к недозволенным областям системы.

Однако у приложения есть варианты предоставления своих данных другим приложениям и доступа к системным службам:

  • двум приложениям можно назначить один идентификатор пользователя Linux. В этом случае каждый из них сможет обращаться к файлам другого приложения. Для экономии ресурсов системы также можно сделать так, чтобы приложения с одинаковым идентификатором пользователя выполнялись в одном процессе Linux и использовали одну ВМ (приложения также должны быть подписаны одним сертификатом);
  • приложение может запросить разрешение на доступ к данным устройства, например к контактам пользователя, SMS-сообщениям, подключаемой карте памяти (SD-карте), камере, Bluetooth и др. Все разрешения должны предоставляться приложению при его установке.

Это основные сведения о том, каким образом приложение Android существует в системе. В остальной части этого документа раскрываются следующие темы:

  • базовые компоненты, которые определяют приложение;
  • файл манифеста, в котором объявляются компоненты и функции устройства, необходимые для приложения;
  • ресурсы, которые существуют отдельно от кода приложения и позволяют приложению адаптировать свою работу к устройствам с различными конфигурациями.

Компоненты приложения

Компоненты приложения являются кирпичиками, из которых состоит приложение для Android. Каждый компонент представляет собой отдельную точку, через которую система может войти в приложение. Не все компоненты являются точками входа для пользователя, а некоторые из них зависят друг от друга. При этом каждый компонент является самостоятельной структурной единицей и играет определенную роль — каждый из них представляет собой уникальный элемент структуры, который определяет работу приложения в целом.

Компоненты приложения можно отнести к одному из четырех типов. Компоненты каждого типа предназначены для определенной цели, они имеют собственный жизненный цикл, который определяет способ создания и прекращения существования компонента.

Четыре типа компонентов:

Операции Операция (Activity) представляет собой один экран с пользовательским интерфейсом. Например, в приложении для работы с электронной почтой одна операция может служить для отображения списка новых сообщений, другая – для составления сообщения и третья операция – для чтения сообщений. Несмотря на то что операции совместно формируют связное взаимодействие пользователя с приложением по работе с электронной почтой, каждая из них не зависит от других операций. Любые из этих операций могут быть запущены другим приложением (если это позволяет приложение по работе с электронной почтой). Например, приложение для камеры может запустить операцию в приложении по работе с электронной почтой, которая составляет новое сообщение, чтобы пользователь мог отослать фотографию.

Операция относится к подклассу класса . Подробные сведения об этом можно найти в руководстве для разработчиков в статье .

Службы Служба (Service) представляет собой компонент, который работает в фоновом режиме и выполняет длительные операции, связанные с работой удаленных процессов. Служба не имеет пользовательского интерфейса. Например, она может воспроизводить музыку в фоновом режиме, пока пользователь работает в другом приложении, или же она может получать данные по сети, не блокируя взаимодействие пользователя с операцией. Служба может быть запущена другим компонентом, который затем будут взаимодействовать с ней, – например операцией.

Служба относится к подклассу класса . Подробные сведения об этом можно найти в руководстве для разработчиков в статье .

Поставщики контента Поставщик контента (Content provider) управляет общим набором данных приложения. Данные можно хранить в файловой системе, базе данных SQLite, в Интернете или любом другом постоянном месте хранения, к которому у вашего приложения имеется доступ. Посредством поставщика контента другие приложения могут запрашивать или даже изменять данные (если поставщик контента позволяет делать это). Например, в системе Android есть поставщик контента, который управляет информацией контактов пользователя. Любое приложение, получившее соответствующие разрешения, может запросить часть этого поставщика контента (например ), для чтения и записи сведений об определенном человеке.

Поставщики контента также используются для чтения и записи данных, доступ к которым внешним компонентам приложение не предоставляет. Например, в образце приложения с помощью поставщика контента выполняется сохранение заметок.

Поставщик контента относится к подклассу класса . Он должен реализовывать стандартный набор API-интерфейсов, с помощью которых другие приложения будут выполнять транзакции. Подробные сведения можно найти в руководстве для разработчиков в статье .

Приемники широковещательных сообщений Приемник широковещательных сообщений (Broadcast receiver) представляет собой компонент, который реагирует на объявления распространяемые по всей системе. Многие из этих объявлений рассылает система — например объявление о том, что экран выключился, аккумулятор разряжен или был сделан фотоснимок. Объявления также могут рассылаться приложениями, — например, чтобы сообщить другим приложениям о том, что какие-то данные были загружены на устройство и теперь готовы для использования. Несмотря на то что приемники широковещательных сообщений не имеют пользовательского интерфейса, они могут чтобы предупредить пользователя о событии «рассылка объявления». Однако чаще всего они являются просто «шлюзом» для других компонентов и предназначены для выполнения минимального объема работы. Например, они могут инициировать выполнение службой определенных действий при возникновении события.

Мастер Йода рекомендует:  Советы Google AdSense по подбору размера блока мобильной рекламы

Приемник широковещательных сообщений относится к подклассу класса , а каждое такое сообщение предоставляется как объект . Подробные сведения изложены в руководстве, посвященном классу .

Уникальной особенностью системы Android является то, что любое приложение может запустить компонент другого приложения. Например, если вы хотите дать пользователю возможность фотографировать, используя камеру устройства, то, поскольку наверняка имеется другое приложение, которое может выполнить это действие, вместо того чтобы разработать операцию фотографирования в своем приложении, вы можете вызвать такое приложение. Вам не нужно внедрять код из приложения для камеры или даже устанавливать на него ссылку. Вместо этого вы можете просто запустить операцию фотографирования из приложения для камеры. По завершении этой операции фотография будет возвращена в ваше приложение, и ее можно будет использовать. Для пользователя это будет выглядеть как одно приложение.

Когда система запускает компонент, она запускает процесс для этого приложения (если он еще не был запущен) и создает экземпляры классов, которые требуются этому компоненту. Например, если ваше приложение запустит операцию фотографирования в приложении для камеры, эта операция будет выполняться в процессе, который относится к этому стороннему приложению, а не в процессе вашего приложения. Поэтому, в отличие от приложений для большинства других систем, в приложениях для Android отсутствует единая точка входа (например, в них нет функции main()).

Поскольку система выполняет каждое приложение в отдельном процессе с такими правами доступа к файлам, которые ограничивают доступ в другие приложения, ваше приложение не может напрямую вызвать компонент из другого приложения. Это может сделать сама система Android. Поэтому, чтобы вызвать компонент в другом приложении, необходимо сообщить системе о своем намерении (Intent ) запустить определенный компонент. После этого система активирует для вас этот компонент.

Активация компонентов

Компоненты трех из четырех возможных типов — операции, службы и приемники широковещательных сообщений — активируются асинхронным сообщением, которое называется Intent (намерение). Объекты Intent связывают друг с другом отдельные компоненты во время выполнения, будь то это компоненты вашего или стороннего приложения (эти объекты Intent можно представить себе в виде мессенджеров, которые посылают другим компонентам запрос на выполнение действий).

Объект Intent создается с помощью объекта , который описывает запрос на активацию либо конкретного компонента, либо компонента конкретного типа — соответственно, намерение Intent может быть явным или неявным.

Для операций и служб Объект Intent определяет действие, которое требуется выполнить (например, просмотреть (view) или отправить (send) что-то), а также может указывать URI (Uniform Resource Identifier – унифицированный идентификатор ресурса) данных, с которыми это действие нужно выполнить (помимо прочих сведений, которые нужно знать запускаемому компоненту). Например, объект Intent может передавать запрос на выполнение операции «показать изображение» или «открыть веб-страницу». В некоторых ситуациях операцию можно запустить, чтобы получить результат. В этом случае операция возвращает результат также в виде объекта (например, можно отправить сообщение Intent, чтобы дать пользователю возможность выбрать контакт и вернуть его вам — в ответном сообщении Intent будет содержаться URI, указывающий на выбранный контакт).

Для приемников широковещательных сообщений Intent просто определяет передаваемое объявление (например, широковещательное сообщение о низком уровне заряда аккумулятора содержит только строку «аккумулятор разряжен»).

Компоненты четвертого типа – поставщики контента – сообщениями Intent не активируются. Они активируются по запросу от . Процедура определения контента (content resolver) обрабатывает все прямые транзакции с поставщиком контента, с тем чтобы этого не пришлось делать компоненту, который выполняет транзакции с поставщиком. Вместо этого он вызывает методы для объекта . Это формирует слой, абстрагирующий (в целях безопасности) поставщика контента от компонента, запрашивающего информацию.

Для активации компонентов каждого типа имеются отдельные методы:

  • Можно запустить операцию (или определить для нее какое-то новое действие), передав объект методу или (если требуется, чтобы операция вернула результат).
  • Можно запустить службу (либо выдать работающей службе новые инструкции), передав объект методу . Либо можно установить привязку к службе, передав объект методу .
  • Можно инициировать рассылку сообщений, передав объект таким методам, как , и .
  • Можно выполнить запрос к поставщику контента, вызвав метод для объекта .

Подробные сведения об использовании объектов Intent приведены в документе . Более подробная информация об активации определенных компонентов также приведена в следующих документах: , и .

Файл манифеста

Для запуска компонента приложения системе Android необходимо знать, что компонент существует. Для этого она читает файл AndroidManifest.xml приложения (файл манифеста). В этом файле, который должен находиться в корневой папке приложения, должны быть объявлены все компоненты приложения.

Помимо объявления компонентов приложения, манифест служит и для других целей, среди которых:

  • указание всех полномочий пользователя, которые требуются приложению, например разрешения на доступ в Интернет или на чтение контактов пользователя;
  • объявление минимального , требуемого приложению, с учетом того, какие API-интерфейсы оно использует;
  • объявление аппаратных и программных функций, которые нужны приложению или используются им, например камеры, службы Bluetooth или сенсорного экрана;
  • указание библиотек API, с которыми необходимо связать приложение (отличные от API-интерфейсов платформы Android), например библиотеки Google Maps ;
  • и многое другое.

Объявление компонентов

Основная задача манифеста – это информировать систему о компонентах приложения. Например, файл манифеста может объявлять операцию следующим образом:

Подробные сведения о структуризации файла манифеста для приложения см. в документе .

Объявление возможностей компонентов

Как уже говорилось в разделе , с помощью объекта можно запускать операции, службы и приемники широковещательных сообщений. Для этого в объекте Intent следует явно указать имя целевого компонента (с помощью имени класса компонента). Однако в полной мере возможности объектов Intent раскрываются при использовании концепции неявных Intent . В неявном сообщении Intent просто описывается тип действия, которое требуется выполнить (а также, хотя это и не обязательно, данные, в отношении которых вы бы хотели выполнить это действие). Системе же предоставляется возможности найти на устройстве компонент, который может выполнить это действие, и запустить его. При наличии нескольких компонентов, которые могут выполнить действие, описанное в сообщении Intent, пользователь выбирает, какой из них будет использоваться.

Система определяет компоненты, которые могут ответить на сообщение Intent, путем сравнения полученного сообщения Intent с фильтрами объектов Intent, указанными в файле манифеста других приложений, имеющихся на устройстве.

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

Например, если вы создали приложение для работы с электронной почтой с операцией составления нового сообщения, вы можете объявить фильтр для ответа на сообщения Intent типа «send» (для отправки нового сообщения электронной почты) следующим образом:

Затем, если другое приложение создаст объект Intent с действием и передаст его в , система сможет запустить вашу операцию, дав пользователю возможность написать и отправить сообщение электронной почты.

Подробные сведения о создании фильтров объектов Intent приведены в документе .

Объявление требований приложения

Существует огромное количество устройств, работающих под управлением Android, и не все они имеют одинаковые функциональные возможности. Чтобы ваше приложение не могло быть установлено на устройствах, в которых отсутствуют функции, необходимые приложению, важно четко определить профиль для типов устройств, поддерживаемых вашим приложением, указав требования к аппаратному и программному обеспечению в файле манифеста. Эти объявления по большей части носят информационный характер, система их не читает. Однако их читают внешние службы, например Google Play, с целью обеспечения фильтрации для пользователей, которые ищут приложения для своих устройств.

Например, если вашему приложению требуется камера и оно использует API-интерфейсы из Android 2.1 ( 7), эти параметры следует объявить в файле манифеста в качестве требований следующим образом:

Теперь ваше приложение нельзя будет установить из Google Play на устройствах, в которых нет камеры, а также на устройствах, работающих под управлением Android версии ниже 2.1.

Однако можно также объявить, что приложение использует камеру, но для его работы она не является непременно необходимой . В этом случае в приложении атрибуту необходимо задать значение «false» , а во время работы оно должно проверять, имеется ли на устройстве камера, и при необходимости отключать свои функции, которые используют камеру.

Более подробные сведения об управлении совместимостью своего приложения с различными устройствами приведены в документе .

Ресурсы приложения

Приложение Android состоит не только из кода — ему необходимы такие существующие отдельно от исходного кода ресурсы, как изображения, аудиофайлы и все, что связано с визуальным представлением приложения. Например, необходимо определять анимацию, меню, стили, цвета и макет пользовательских интерфейсов операций в файлах XML. Используя ресурсы приложения, можно без труда изменять его различные характеристики, не меняя код, а, кроме того, —путем предоставления наборов альтернативных ресурсов — можно оптимизировать свое приложение для работы с различными конфигурациями устройств (например, для различных языков или размеров экрана).

Для каждого ресурса, включаемого в проект Android, инструменты SDK задают уникальный целочисленный идентификатор, который может использоваться, чтобы сослаться на ресурс из кода приложения или из других ресурсов, определенных в XML. Например, если в вашем приложении имеется файл изображения с именем logo.png (сохраненный в папке res/drawable/), инструменты SDK сформируют идентификатор ресурса под именем R.drawable.logo , с помощью которого на изображение можно будет ссылаться и вставлять его в пользовательский интерфейс.

Один из наиболее важных аспектов предоставления ресурсов отдельно от исходного кода заключается в возможности использовать альтернативные ресурсы для различных конфигураций устройств. Например, определив строки пользовательского интерфейса в XML, вы сможете перевести их на другие языки и сохранить эти переводы в отдельных файлах. Затем по квалификатору языка, добавленному к имени каталога ресурса (скажем res/values-fr/ для строк на французском языке), и выбранному пользователем языку система Android применит к вашему пользовательскому интерфейсу строки на соответствующем языке.

Android поддерживает разные квалификаторы для соответствующих ресурсов. Квалификатор представляет собой короткую строку, которая включается в имена каталогов ресурсов с целью определения конфигурации устройства, для которой эти ресурсы следует использовать. В качестве другого примера можно сказать, что для своих операций следует создавать разные макеты, которые будут соответствовать размеру и ориентации экрана устройства. Например, когда экран устройства имеет книжную ориентацию (расположен вертикально), кнопки в макете можно также размещать по вертикали, а когда экран развернут горизонтально (альбомная ориентация), кнопки следует размещать по горизонтали. Чтобы при изменении ориентации экрана изменялся макет, можно определить два разных макета и применить соответствующий квалификатор к имени каталога каждого макета. После этого система будет автоматически применять соответствующий макет в зависимости от ориентации устройства.

Предоставление ресурсов Описание структуры приложений Android, в которой ресурсы приложения существуют отдельно от его кода, а также сведения о том, как предоставлять альтернативные ресурсы для определенных конфигураций устройств.

Большая подборка ресурсов для изучения Andro >

  • Подборки, 25 мая 2020 в 13:14
  • Дмитрий Юрченко

В этой статье собраны полезные ресурсы, которые могут пригодиться всем Android-разработчикам, независимо от уровня знаний.

Также советуем взглянуть на наш вводный материал — рассказываем, как начать разрабатывать под Andro >

Для новичков

Ресурсы, которые могут оказаться полезными для любого Java-программиста, начинающего разрабатывать под Android.

Простой курс для новичков, разработанный Google, поможет быстро изучить основы и фундаментальные концепции Android-разработки.

Простое, легкое и полное руководство для начинающих разработчиков, желающих написать своё первое приложение на Android.

Это один из лучших ресурсов, о котором должны узнать все начинающие Android-разработчики.


Если вы вступаете в огромный мир разработки под Android, то очевидно, что вы столкнётесь со множеством новых понятий и терминов. Не беспокойтесь, Google разработал интерактивный и простой в использовании глоссарий с примерами кода, который облегчит вашу жизнь.

Официальное руководство для разработчиков под Android от Google. Оно охватывает основные концепции с большим количеством примеров кода.

Не знакомы с XML? Самое время изучить, потому что он используется для дизайна макетов в Android . Это не займет много времени.

Если вы собираетесь использовать интернет-сервисы через API для вашего Android-приложения, тогда стоит познакомиться с HTTP и REST.

Для опытных разработчиков

Если вы занимаетесь разработкой под Android не первый год, то вот несколько хороших ресурсов, которые помогут улучшить ваши навыки программирования на Android.

Хороший курс от Google, который идеально подходит для повышения квалификации.

Если вы ищите элегантные способы решения критических проблем в разработке программного обеспечения, то обратите внимание на подборку самых распространенных шаблонов проектирования. Они смогут вам в этом помочь.

Существует большое количество open source Android-приложений. Не беспокойтесь, если вы не знаете, какие из них могут помочь улучшить навыки разработки. Эта статья расскажет вам об этом.

Чтобы усовершенствовать процессы разработки, тестирования, отладки и обслуживания вашего Android-приложения, ознакомьтесь с лучшими подходами к архитектуре.

Эта статья расскажет вам о современных способах создания приложений.

Погрузитесь в мир реактивного программирования под Android с помощью этого простого учебника.

Получите подробное представление о конструкторах Android View, о том, как они работают и как их эффективно использовать при разработке приложений.

Если вы хотите понять, как устроен асинхронный мир фреймворков Android, и использовать его для создания высококачественных приложений — прочтите эту статью.

Подробный учебник, рассказывающий о том, как работают Views и ViewGroups в Android и как вы можете совершенствовать компоненты пользовательского интерфейса.

Не все разработчики правильно понимают, как работают различные режимы запуска в Android и как использовать их для создания идеальной навигации в своём приложении. Это статья расставит всё по своим местам.

Вы уже давно используете библиотеки в своих приложениях под Android и хотите внести свой вклад в сообщество? Вот полное и подробное руководство по публикации собственной библиотеки.

Узнайте, как сделать успешный запуск своего приложения в Play Market.

Этот GitHub-репозиторий станет жемчужиной для всех тех, кто хочет узнать о различных инструментах и ​​шаблонах для разработки приложений для Android.

Если вы не знаете, что выбрать: нативные или веб-приложения — эта статья для вас.

Если вы разрабатываете приложения для Android уже довольно давно и хотите улучшить свою производительность и рабочий процесс разработки, то эти инструменты помогут вам.

Вы используете Android Studio каждый день для разработки своих приложений, но, возможно, и не догадываетесь, что эта IDE может быть еще мощнее.

Лучшие практики Android-разработки

Следование лучшим практикам поможет повысить качество приложения и облегчить командную разработку.

Легкая и полезная статья, объясняющая некоторые аспекты лучших практик разработки под Android.

В этой статье мы расскажем о лучших методах использования файла ресурсов strings.xml .

Один из лучших и наиболее полных ресурсов о лучших практиках и гайдлайнах по различным аспектам разработки Android, охватывающий почти все, что вам нужно знать.

Эта статья поможет узнать, как наилучшим образом использовать Android for Work в ваших проектах.

Чрезвычайно полезный список лучших практик Android-разработки.

Книга «Effective Java» считается одной из лучших для написания высококачественного поддерживаемого Java-кода. Эта статья посвящена тому, как вы можете наилучшим образом использовать описанные в ней методы и принципы в разработке под Android.

Если вы выбрали реактивный способ создания приложений под Android, то эти советы помогут вам избежать распространенные ошибки и использовать RxJava максимально эффективно.

Углубленная статья о плюсах и минусах некоторых распространенных приемов хранения секретов (паролей, ключей API и другой информации) в приложениях Android.

Проектирование красивых приложений

Приложение считается неполным без хорошего пользовательского интерфейса. Чистый и красивый интерфейс — вот что делает ваше приложение привлекательным и выделяет его среди остальных.

Хорошо написанная статья, дающая глубокое понимание Material Design для Android.

Узнайте, как создать прекрасные и выразительные анимации в своем Android-приложении, чтобы порадовать своих пользователей и сделать его выделяющимся среди остальных приложений.

Свежий ConstraintLayout полностью меняет приложение, когда дело доходит до разработки макетов для ваших приложений для Android. Эта статья поможет понять основы ConstraintLayout.

Создавайте качественные приложения, не допуская распространенных ошибок, в то же время применяя Material Design.

Полезная статья, которая рассказывает, как наилучшим образом использовать Material Design при разработке своего приложения.

Бонус

Интересные ресурсы, которые нельзя пропустить.

Если вы хотите быть в курсе последних событий разработки в мире Android, следите за передовыми разработчиками Android в Twitter.

Хотите погрузиться в мир IoT с помощью инструментов Android и его инфраструктуры? Вот хороший ресурс, чтобы начать это путешествие.

Если вы заинтересованы в разработке приложений для носимых устройств на Android, то вот исчерпывающий урок, который поможет вам начать разработку.

Интересная статья о деталях повседневной работы с ресурсами и атрибутами в Android, которые могут быть вам неизвестны.

Хотите разрабатывать приложения для телевизоров с большим экраном? Вот отличная статья, которая послужит хорошим введением в эту среду разработки

Полезные сайты

Хотите быть в курсе последних новостей в мире Android? Вот некоторые из лучших ресурсов для вас.

Вы никогда не пропустите новости о развитии, статьи, видеоролики и другие события в мире Android.

Если вы любите слушать подкасты, то этот сайт для вас. Здесь можно обсуждать инструменты, шаблоны и лучшие практики. Вы наверняка не захотите пропустить двухнедельные эпизоды.

Один из лучших и самых современных ресурсов с большим количеством пользователей для Android-разработчиков, где можно найти руководства и учебные пособия обо всех аспектах разработки. А главным плюсом является то, что он полностью открыт для вас, чтобы каждый мог внести свой вклад в развитие сообщества.

Получайте высококачественные еженедельные скринкасты для Android с широким спектром тем для разработки.

Ресурс, где вы найдете всю информацию, необходимую для проектирования пользовательского интерфейса и UX вашего приложения.

Это официальный блог, через который Google напрямую взаимодействует с разработчиками Android. Все последние новости и анонсы, связанные с разработкой, появляются здесь раньше, чем где-либо еще.

Мы надеемся, что эти ресурсы помогут вам узнать больше о разработке Android и создать более качественные приложения.

Подробный гайд по разработке Android-приложений с помощью Clean Architecture

Гайд по разработке Android-приложений с помощью Clean Architecture

Данный туториал поможет вам разобраться в очень полезном подходе по разработке приложений Clean Architecture. Благодаря примерам все становится понятно!

Комментарии (5)

Алексей Литвинов

Сначала вы показывали, что у вас херовый оригинальный контент. А теперь решили показать, что вы и переводите тексты так, будто у вас жопы вместо голов? Молодцы.

Библиотека программиста

Алексей, что исправить?

Алексей Литвинов

Библиотека программиста, достаточно прочитатть текст вслух и подумать, способен ли русскоязычный человек на самом деле их произнести. Мета-замечания: 1) переводчик явно берёт разбивку на предложения из оригинала, и в итоге текст на русском выглядит как хаотичный набор обрубков мыслей; 2) переводчик явно сам писал свои курсачи, потому что этот шаблонный язык он использовал и в переводе. Для комментария в паблике текста получилось слишком много, так что вот https://pastebin.com/PuBUQDXv

Библиотека программиста

Алексей, спасибо за вашу работу!

Николай Покровский

Отличный материал! Как раз разбираюсь в этом вопросе. Честно сказать ни разу не споткнулся о трудности перевода. Смысл везде понятен. Спасибо за работу!

Реализация Interactors с Andro >

В настоящее время я создаю приложение для Android и хотел бы основать его на «чистой архитектуре» аналогично предложению следующих авторов:

  • Фернандо Сеяс – Архитектор Android … Чистый путь?
  • Дарио Миличич – подробное руководство по разработке приложений для Android с использованием шаблона «Чистая архитектура»
  • Ромен Пил – Ingedients для здоровой архитектуры Android
  • Дядя Боб – Чистая архитектура
  • Ханнес Дорфманн – Библиотека Мосби
  • Pedro Vicente Gómez Sánchez – эффективный интерфейс для Android
  • Дэвид Герреро – введение в чистую архитектуру Android: шаблон mvp
  • Patryk Poborca ​​- Чистая архитектура и тестирование

Текущая архитектура:

Просмотр (фрагмент) Presenter Интерактор Репозиторий

  • RoboBinding vs Android Binding для MVVM
  • Что нужно сделать в Activity / Fragment и ViewModel в MVVM
  • Архитектура Android-приложений — MVVM или MVC?
  • Показать диалог из ViewModel в Android MVVM Architecture
  • Как установить ошибку в EditText с помощью DataBinding Framwork MVVM
  • Фрагмент реализует представление и создает презентатор.
  • Презентатор ссылается на фрагмент по интерфейсу View.
  • Ведущий реализует интерфейс Interactor.Callback для представления данных
  • Ведущий создает и запускает Interactor.
  • Interactor извлекает и обновляет данные для выполнения бизнес-логики в фоновом потоке из репозитория.
  • Interactor реализует Repository.Callback для данных DB / Server из репозитория.
  • Interactor регистрируется в репозитории обновлений для требуемых данных.

В текущем проекте на дисплее отображается 1 взаимодействующий (дисплей может включать в себя несколько фрагментов, например ViewPager с 30 фрагментами того же типа) и 1 презентатор на фрагмент. Презентатор и Interactor не имеют зависимостей от структуры, чтобы обеспечить легкое тестирование.

Моя главная проблема заключается в реализации Interactors / UseCases и их отношении к Ведущим (MVP) или ViewModel (MVVM).

Проблема:

Планируется, что Interactor сначала отобразит все необходимые бизнес-объекты (BO) для отображения. Извлечение осуществляется синхронно с уровня данных, и каждый принятый BO направляется на презентатор. Это вызывает задержку, пока все данные не будут показаны в представлении.

Кроме того, он регистрирует обновления для BOs, которых он интересует (так же, как и раньше), чтобы постоянно обновлять представление через презентаторов.

Поэтому я ищу руководство о том, как настроить Interactor в моем случае. У реализации, упомянутых выше, есть одна задача, затем выполнить ее, и фоновый поток Interactor может быть закрыт.

В моем случае Interactor регистрируется для обновлений из datalayer и ждет их обработки, а затем отправляет данные в поток пользовательского интерфейса Presenter, таким образом, живет до тех пор, пока прослушивается презентатор.

Эта функциональность отличается, и я ищу хорошую практику, чтобы заставить ее работать с «чистой архитектурой».

Поэтому, если я понимаю ваш вопрос, ваша озабоченность или сомнения возникают из-за того, что ваш Interactor не собирается выполнять задачу, а затем заканчивается, но ее подписывают или слушают до тех пор, пока операция не будет выполнена.

На мой взгляд, это прекрасно, Interactor реализует прецедент, и в вашей программе, что асинхронный запрос является прецедентом, не имеет значения, требуется ли время и является асинхронной задачей или является синхронной операцией.

Он по-прежнему является прецедентом, когда Презентатор будет создавать экземпляр Interactor, и когда это закончится, он вернет результат операции. Пока вы сохраняете модульность, а Presenter и Interactor не связаны с прямыми зависимостями, но они обмениваются данными по косвенным отношениям, это прекрасно.

Структура и описание ос андроид. Подробный гайд по разработке Android-приложений с помощью Clean Architecture

Следует начать с того что все приложения для ОС Android распространяются в виде инсталляционных пакетов — файлов с расширением APK.

APK (Android Package) — формат архивных исполняемых файлов-приложений для Android.

Каждое приложение Android скомпилировано и упаковано в один файл, который включает в себя весь код приложения (.DEX файлы), ресурсы, активы и файл manifest. Файл приложения может иметь любое имя, но расширение должно быть.APK. Например: myAppFile.apk.

Файлы с данным расширением хранятся в магазине Google Play, и загружаются с его помощью в смартфон для их использования, либо устанавливаются пользователем вручную на устройстве.

Файлы этого формата не шифруются, являются подмножеством формата архива ZIP.

Каждый.APK файл — это сжатый архив для исполнения в DalvikVM (виртуальная машина), который может быть установлен не только на операционной системе Android.

APK файл как архив обычно содержит следующие директории:

§ MANIFEST.MF: манифест файл

§ CERT.RSA: сертификат приложения

§ CERT.SF: список ресурсов и их SHA1 хеш-сумма например на Рисунке 6:

· Created-By: 1.0 (Android)

Рисунок 6. Структура файла со списком ресурсов и их хеш-сумм.

· Директория lib: содержит скомпилированный исполняемый код адаптированный под различные типы процессоров, обычно разделена на следующие директории:

Ш armeabi: код только для ARM процессоров

Ш armeabi-v7a: код для для процессоров ARMv7 и ниже только.

Ш x86: скомпилированный код только для архитектуры x86

Ш mips: скомпилированный код только для архитектуры MIPS

· Директория res: директория содержит файлы ресурсы не вошедшие в файл resources.arsc(см. ниже)

· Директория assets: содержит активы которые могут получены с помощью AssetManager

· Файл AndroidManifest.xml: дополнительный манифест файл, описывающий версию приложения, разрешения, используемые библиотеки. Как правило это файл идёт в формате binary XML, это формат файлов можно привести к читаемому виду с помощью сторонних утилит таких как AXMLPrinter2, apktool, или Androguard.

· Файл classes.dex: исполняемый файл виртуальной машины Dalvik, полученный путём преобразования скомпилированных JAVA классов с помощью утилиты DX. Утилита входит в состав Android SDK.

· Файл resources.arsc: файл содержит пре-компилированные ресурсы, например в виде бинарных XML файлов.

Из всего выше обозначенного в данной работе при анализе уровня опасности будут использоваться только два файла это AndroidManifest.xml и classes.dex. Ни их структуре остановимся более подробно.

Данный файл, как уже говорилось ранее, содержит информацию о приложении, в том числе список требуемых разрешений приложения. В том числе на основе этих данных можно ранжировать уровень опасности приложения. Обратимся как его структуре: в первую очередь необходимо отметить что в установочном пакете androidmanifest.xml имеет бинарный вид, то есть преобразованный xml, хотя в оригинальном состоянии этот файл имеет структуру, обозначенную на Рисунке 7:

Рисунок 7. Подтверждение установки приложения из стороннего источника.

Следует отметить, что нас особенно интересуют поля обозначенные . Эти полня показывают системе какие разрешения хочет получить приложения для своей работы. На их основе можно выставлять уровень опасности приложению.

В Таблице 1 приведены некоторые разрешения которые представляют наибольшую опасность:

Таблица 1. Описание некоторых опасных разрешений в ОС Android.

Приложение сможет получить доступ к приблизительному местоположению, полученному из сетевого расположения источников, таких как вышки сотовой связи и Wi-Fi.

Приложение сможет получить доступ к точному местоположению от места расположения источников, таких как GPS, вышек сотовой связи и Wi-Fi.

Приложение сможет инициировать телефонный звонок, минуя пользовательский интерфейс Dialer для пользователя.

Приложение сможет сделать снимок встроенной камерой

Позволяет приложению удалять пакеты.

Позволяет приложению отключить питание устройства

Как видно из описаний некоторые разрешения можно группировать, выставив при этом уровень опасности целой группе функций, например разрешения READ_SMS, BRICK являются очень опасными и им можно присвоить уровень опасности 10(максимальный). Это вызвано тем, что под разрешением READ_SMS понимается чтение личных данных пользователей, что является потенциально опасным для пользователя действием со стороны приложения. Под BRICK понимается отключение устройства в целом — что тоже очень опасно для пользователя потому что устройство полностью прекращает свою работу.


Исходя из всего выше сказанного, для ранжирования уровня опасности необходимо проанализировать все разрешения, выставить им уровни, так же можно группировать разрешения в составные при этом выставив общий уровень опасности приложению.

Данный файл носит в себе основной функционал приложения, содержит байт-код, понятный виртуальной машине Dalvik. Имеет следующую внутреннюю структуру, представленную в Таблице 2:

Таблица 2. Структура Dex файла.

На самом деле это файл, содержащий в себе программный код для виртуальной машины Dalvik. Приложения для Android пишутся на языке Java, но после компиляции кода в.class-файлы, вызывается утилита dx, которая транслирует их в один файл classes.dex, являющийся основной составляющей APK файла. Общий алгоритм формирования dex представлен на Рисунке 8.

Рисунок 8. Механизм формирования файла classes.dex.

Следует отметить, что функционирование данного файла абсолютно связано с использованием API операционной системы. При этом исходный код приложения написан на объектно-ориентированном языке программирования JAVA. И является своего рода компиляцией в компиляции. Как следствие состоит из большего числа строк, содержащих имена методов API, имена различных констант. Вся эта информация может явным образом служить для понимания функционала приложения и как следствие ранжирования его уровня опасности, опираясь на использованные наборы API функций.

Так нужно понимать, что файл имеет четкую устоявшуюся структуру, которая позволяет получить из файла путём статического анализа необходимую информацию для понимания функционала. Для этого необходимо разобрать формат файла и научится извлекать строки, имена методов.

Есть четыре стандартных блока приложения Android:

Не у каждого приложения должны быть все четыре блока, но Ваше приложение будет написано с их некоторой комбинацией.

Как только Вы решили, в каких компонентах Вы нуждаетесь для своего приложения, Вы должны перечислить их в файле по имени AndroidManifest.xml. Это — файл XML, где Вы объявляете компоненты своего приложения и каковы их возможности и требования. Мы скоро обсудим, за что AndroidManifest.xml ответственен.

(Это могло быть написано ОЧЕНЬ криво. Тут много текста и никаких картинок примеров. Рекомендую потерпеть и прочесть эту теорию, зато потом Вам будет понятней. Потом все написано гораздо глаже, не волнуйтесь)

Activity

Activity – самый распространенный из четырех стандартных блоков Андроид. Activity обычно — единственный экран в Вашем приложении. Каждый Activity осуществлен как единственный класс, который расширяет базовый класс Activity. Ваш класс отобразит пользовательский интерфейс, составленный из Views, и ответит на события. Большинство приложений состоит из множественных экранов. Например, у приложения обмена сообщениями мог бы быть один экран, который показывает список контактов, второй экран, чтобы написать сообщение выбранному контакту, и другие экраны, чтобы делать обзор старых сообщений или изменить настройку. Каждый из этих экранов был бы осуществлен как Activity. Перемещение в другой экран достигнуто стартом нового Activity. В некоторых случаях Activity может возвратить значение предыдущего Activity — например Activity, которая позволяет пользователю выбирать фотографию, возвратил бы выбранную фотографию вызывающей программе. Когда новый экран открывается, предыдущий экран приостановлен и помещен на стек хронологии. Пользователь может переместиться назад через ранее открытые экраны в хронологии. Экраны могут также хотеть быть удаленными от стека хронологии, когда было бы неуместно для них остаться. Андроид сохраняет стеки хронологии для каждого приложения, начатого от начала экрана.

Intent и фильтры Intent

Андроид использует специальный класс под названием Intent, чтобы двигаться от экрана к экрану. Intent описывает то, что приложение собирается сделать. Две самых важных части структуры Intent — действие и данные к действию. Типичные значения для действия – MAIN (главный экран приложения), VIEW, PICK, EDIT, и т.д. Данные выражены как Uniform Resource Indicator (URI). Например, чтобы рассмотреть веб сайт в браузере, Вы создали бы Intent с действием VIEW и набором данных – адресом сайта.

new Intent(android.content.Intent.VIEW_ACTION , ContentURI.create («https://anddev.org»));

Есть связанный класс, названный IntentFilter. В то время как Intent — запрос сделать кое-что, IntentFilter — описание того, что Intent Activity (или intent receiver, см. ниже), способен к обработке. Activity, который в состоянии отобразить информацию для человека, издала бы IntentFilter, который сказал, что знает, как обработать VIEW действия. Activity издает свой IntentFilters в файле AndroidManifest.xml.

Навигация от экрана к экрану достигнута достигается с помощью Intent. Чтобы переместиться вперед, Activity вызывает startActivity (myIntent). Система тогда смотрит на IntentFilter для всех установленных приложений и выбирает Activity, Intent которого фильтрует myIntent. Новому Activity сообщают о Intent, которое заставляет его начаться. Процесс решения Intent происходит, когда startActivity вызывают. Процесс предлагает две ключевых льготы:

Действия могут многократно использовать функциональные возможности от других компонентов, просто делая запрос в форме Intent.

Действия могут быть заменены в любое время новым Activity с эквивалентным IntentFilter.

Intent Receiver

Вы можете использовать IntentReceiver, когда Вы хотите, чтобы код в своем приложении выполнился в реакции на внешнее событие, например, когда телефон звонит, или когда сеть передачи данных доступна, или когда это — полночь. Intent Receiver не отображают UI, хотя они могут отобразить Уведомления, чтобы привести пользователя в готовность, если кое-что интересное случилось. Поглощенные получатели также регистрированы в AndroidManifest.xml, но Вы можете также регистрировать их в коде, используя Context.registerReceiver(). Ваше приложение не должно работать для его Intent Receiver, которые вызываются; система запустит Ваше приложение, в случае необходимости, когда Intent Receiver будет вызван. Приложения могут также послать свои собственные Intent Receiver другим с Context.broadcastIntent().

Service

Service — код, который долговечен и выполняется без UI. Хороший пример этого — универсальный проигрыватель, запускающий песни из плейлиста. В приложении универсального проигрывателя, вероятно, были бы одно или более Activity, которые позволяют пользователю выбирать песни и запускать их. Однако, воспроизведение самой музыки не должно быть обработано Activity, потому что пользователь будет ожидать, что музыка продолжит играть даже после сворачивания проигрывателя. В этом случае, деятельность универсального проигрывателя могла запустить Service, используя Context.startService(), чтобы работать на заднем плане и сохранить воспроизведение музыки. Тогда система сохранит воспроизведение музыки, пока оно не закроется само. (Вы можете узнать больше о приоритете, данном службам в системе, читая Цикл Жизни Приложения Андроид). Отметьте, что Вы можете соединиться с Service (и запустить его, если он уже не работает) с методом Context.bindService(). Когда есть подключение с Service, Вы можете общаться с этим через интерфейс, выставленный Service. Для Service музыки это могло бы позволить Вам приостанавливать, перематывать, и т.д.

Content Provider

Приложения могут хранить свои данные в файлах, базе данных SQLite, персональных настройках или любом другом механизме, который имеет смысл. Content Provider, однако, полезен, если Вы хотите, чтобы данные Вашего приложения были разделены с другими приложениями. Content Provider — класс, который осуществляет стандартный набор методов, чтобы позволить другим приложениям сохранять и восстанавливать тип данных, которые обработаны другим(that) Content Provider.

Пользовательские интерфейсы Андроид

Пользовательские интерфейсы (UI) в Андроид могут быть созданы двумя путями, через XML-код или в java-коде. Создание структуры графического интерфейса пользователя в XML очень предпочтительно, потому что по принципу Образцового управления средства просмотра, UI должен всегда отделяться от логики программы. К тому же, приспосабливание программы от одной разрешающей способности экрана до другой намного более просто. Определение UI в XML очень похоже к созданию общего документа HTML, где Вы имеете то есть такой простой файл:

The content of the body element.

Все равно как в Андроидовских XML-Layouts. Все хорошо структурировано и может быть выражено древовидными структурами:

Иерархия Элементов Экрана

Основной функциональный модуль приложения Android — Activity — объект класса android.app.activity. Activity может сделать много вещей, но отдельно у него нет присутствия на экране. Чтобы дать Вашему Activity присутствие на экрана и проектировать его UI, Вы работаете с Views и Viewgroups — основными единицами выражения пользовательского интерфейса на платформе Андроид.

Views

View — объект, расширяющий базовый класс android.view.view . Это — структура данных, свойства которой сохраняют Layouts и информационное наполнение для определенной прямоугольной области экрана. Объект View обрабатывает измерение, его схему размещения, рисунок, изменения центра, прокрутку, и клавиши/знаки для области экрана, которую он представляет. Класс View служит базовым классом для всех графических фрагментов — ряд полностью осуществленных подклассов, которые рисуют интерактивные элементы экрана. Графические фрагменты обрабатывают свое собственное измерение и рисунок, таким образом Вы можете использовать их, чтобы создать Ваш UI более быстро. Список доступных графических фрагментов включает TextView, EditText, Button, RadioButton, Checkbox, ScrollView и т.д.

Viewgroups

Viewgroup — объект класса android.view.viewgroup. Viewgroup — специальный тип объекта View, функция которого — содержать набором View и Viewgroup и управлять ими. Viewgroups позволяют Вам добавлять структуру к Вашему UI и создавать сложные элементы экрана, к которым можно обратиться как к единственному объекту. Класс Viewgroup служит базовым классом для Layouts — ряда полностью осуществленных подклассов, обеспечивающего общие типы Layouts экрана. Layouts дают Вам способ встроить структуру для ряда View.

UI с древовидной структурой

На платформе Андроид Вы определяете UI Activity использование дерева View и Viewgroup узлов, как показано в диаграмме ниже. Дерево может быть столь же простым или сложным, как Вы его сделаете, и Вы можете построить его, используя наборы предопределенных графических фрагментов и Layouts Андроида, или заказных типов View, которые Вы создаете самостоятельно.

UI Андроид – древовидная структура.

Чтобы прикрепить дерево к экрану и отрендрить его, Ваш Activity вызывает свой метод setContentView() и передает информацию на корневой объект узла. Как только у система Андроид получает информацию на корневой объект узла, она начинает работать непосредственно с узлом, чтобы измерить, и отрисовать дерево. Когда Ваш Activity становится активным и получает приоритет, система регистрирует Ваш Activity и просит корневой узел измерить и отрисовать дерево. Тогда корневой узел просит, чтобы его дочерние вершины отрисовали себя — в свою очередь, каждый Viewgroup узел в дереве ответственен за отрисовку его прямых дочерних узлов. Как упомянуто ранее, у каждой группы View есть ответственность измерения ее доступного пространства, расположения ее дочерних узлов, и вызов draw() на каждом дочернем узле, чтобы позволить все им рендрить себя. Дочерние узлы могут просить размер и местоположение в родителе, но у родительского объекта есть конечное решение, где и насколько большой каждый ребенок может быть.

Сравнение Андроида Элементы UI к Swing Элементы UI

Поскольку некоторые разработчики, которые читают это, возможно, нашли, что UIs схож с Swing, сейчас будет немного общих черт между Андроидом и Swing.

Activity в Андроид — почти (J) Frame в Swing.

View в Андроид — (J) Component в Swing.

TextViews в Андроид — (J) TextField в Swing.

EditTexts в Андроид — (J) TextField в Swing.

Button в Андроид — (J) Button в Swing.

Установка слушателей к View в Андроид является почти тем же самым, чем и в Swing.

Разрабатывать под Android я начал относительно недавно (3 месяца назад) и первое с чем столкнулся — очень малое количество «русскоязычного» материала по этой теме, особенно, касательно таких вопросов как продумывание интерфейса или использование встроенных фич среды Android. Все что я находил, в большинстве своем, сводилось или к переводу статей с https://developer.android.com или примерами решающими конкретную задачу.

Скажу сразу, что на истину в последней инстанции я не претендую, и лишь хочу донести до начинающих Android разработчиков материал, который, возможно, поможет им в их дальнейшей деятельности.

Я предполагаю выдать несколько статей, целью которых, будет являться раскрыть суть проектирования и разработки приложений под платформу Android.

Конкретно в этой статье пойдет речь о программном обеспечении, необходимом для начала разработки под Android, литературе и немного о структуре приложения.

Быстрый старт

Для начала разработки вам необходимо 2 вещи: Android SDK (можно скачать с https://developer.android.com/intl/zh-TW/sdk/index.html) и, собственно, среда разработки.

SDK устанавливается через интернет (сам установочный модуль весит немного, на момент когда начинал я — около 10 Мб). Запустив установочный модуль (SDK Setup.exe) сразу запустится проверка репозитория на наличие обновлений API и прочих приблуд, по умолчанию ваше SDK пустое, и по любому для начала работы надо будет что-то из предложенного выбора обновлений скачать. Внимание! Возможно возникнет проблема соединением с репозиторием, для того чтобы её решить нужно перейти в Settings и поставить галочку напротив «Force https: \\ что-то там еще. » и попробовать соединится с репозиторием снова.

Размер загружаемого контента будет зависить от вашего выбора (в текущем варианте около 1 Гб). Для того чтобы ускорить загрузку, можно ограничить свой выбор 1 — 2 модулями (например SDK Platform Android API 1.5 и 2.0). В целом, все зависит от того под какую версию Android-а вы планируете разрабатывать. Наиболее популярные на данный момент версии API: 1.5, 1.6, 2.0. Лично я качал все.

Внимание! Учтите тот факт, что это вам не iPhone! Платформа постоянно растет и развивается и то что хорошо компилилось на Android API 1.5, под 2.0 вам выдаст предупреждение о том, что вы пользуетесь устаревшими библиотеками или методами (эх, Java ;-)). Плюс, в новых версиях API доступны методы недоступные в более ранних, для того чтобы наглядно увидеть какие библиотеки поддерживает данное API, можно на https://developer.android.com/intl/zh-TW/reference/android/app/Activity.html в правом верхнем углу поставить галочку напротив надписи «Filter by API level» и указать интересующую вас версию API (по состоянию на март 2010 года — максимальная 7-ка).

Увидеть загруженные модули можно перейдя по вкладке «Installed Packages». Обновить и удалить их можно там же.

Итак, SDK вы загрузили. теперь требуется создать эмуляторы для отладки ваших приложений. Для того чтобы создать эмулятор, необходимо в том же модуле загрузки, перейти по вкладке Virtual Device. В открывшемся окне перед вами появится таблица (сначала пустая) со списком созданных эмуляторов. Функционал окна позволяет вам создавать, удалять, «ремонтировать» (данную функцию никогда не использовал), просматривать информацию и запускать эмуляторы. Нажав на кнопку «New» вы попадете в мастер создания эмуляторов.

В мастере, вам необходимо указать имя эмулятора, целевую платформу (выпадающий список Target), объем SD карты памяти (обратите внимание, по умолчанию, объём в Мб), скин (тип и разрешение экрана устройства), а так же поддерживаемое «железо»: поддержка камеры, работы батареи, GPS и т.д. (нажав на кнопку «New» вы попадает в окно выбора свойства эмулятора и его значения).

Для завершения создания эмулятора нужно нажать на «Create AVD». Внимание! После создания эмулятора невозможно поменять его свойства. Если вам необходим эмулятор с другими свойствами — то прийдется создавать новый. После создания эмулятора, считайте, что пол пути для «быстрого» старта уже пройдено.

Теперь переходим к среде разработки. Выбор тут у нас невелик: или ставим плагин под Eclipse/NetBeans (https://developer.android.com/intl/zh-TW/sdk/eclipse-adt.html и https://kenai.com/projects/nbandroid/ , соответственно) или качаем уже собранную IDE (например, MOTODEV Studio for Android 1.1 с сайта https://developer.motorola.com/docstools/motodevstudio/). Впринципе, установив плагин под Eclipse в конечном итоге вы и получите, почти, MOTODEV Studio for Android 1.1, но вам нужна эта возня. Что же касается плагина под NetBeans, то я не знаю в каком он состоянии сейчас, но та версия с которой мне приходилось работать не имела визуального редактора UI, что, мягко говоря, тормозило работу.

Лично мой выбор пал на второй вариант (MOTODEV Studio рулит;-)). Всю необходимую информацию по установке и настройке среды разработки, вы найдете без проблем погуглив с пол часика.

Чтиво

Наличие русскоязычной литературы по разработке под Android, лично я, пока не наблюдал. Поэтому сразу перейдем к англоязычному контенту. Итак, для меня стали Библией Android: Apress Beginning Android и Apress Pro Android.

В этих книгах достаточно популярно и доступно описывается что, зачем и почему, а так же приводятся достаточно понятные примеры. Стоит отметить, что обе книги в качестве базы рассматривают платформу Android 1.5, версии книг под 2.0 (Apress Beginning Android 2 и Apress Pro Android 2, соответственно) хоть и присутствуют на сайте издателя, для загрузки мне еще не попадались.

Много справочного материала (правда с частично неработающими примерами;-)) есть на основном сайте проекта (https://developer.android.com), полезным будет не только почитать реферы и DevGuide но и посмотреть видео уроки.

Структура

О том как создавать HelloWorld приложение вам поведают и без меня, поэтому, сразу перейдем к структуре. В принципе, все что я сейчас поведаю вы можете почитать и из выше предложенного вам чтива, но я постараюсь вам сэкономить немножко времени.

Итак, условно, приложение под Android состоит из 3-х блоков:

  1. Манифест (AndroidManifest.xml) — дескриптор файла приложения, обязательный элемент, в котором определены такие страшные штуки как: activities, content providers, services, intent receivers (о них пойдет речь в следующих статьях). А так же в манифесте вы можете описывать «разрешения» (permissions) необходимые для работы вашего приложения. Более подробно манифест я планирую описать в следующих статьях.
  2. Папка «src» — папка содержащая весь исходный код программы, является обязательной.
  3. Папка «res» — самая вкусная папка, в ней содержаться все «ресурсы» приложения;-) Вы пока еще об этом не знаете, но она сильно вам облегчит жизнь, более того, я бы сказал, что самое ВСЕ разработчика для Android. Наличие данной папки для проекта обязательно.

Кроме того, корневая папка приложения может еще содержать в себе папку «assets», данная папка не обязательна и может содержать в себе различные вспомогательные ресурсы (другие папки и файлы).

И еще одна тонкость. В корень приложения можно забросить папку «libs», в которую, в последующем можно будет добавить нативные С/С++ библиотеки (о них мы тоже как-нибудь поговорим, но позже).

В процессе сборки приложения, появится папка «gen» с вложенным пакетом и файлом R.xml — это тоже очень полезный файл, в котором содержаться дисрипторы ресурсов, генерится средой разработки и лезть туда руками крайне не рекомендуется.

Ресурсы (кроме графических) как и манифест представленны в виде XML файлов. И те кто что-то понимает в Java EE сразу для себя найдут много сходного, и будут правы. Структуру XML для различных типов ресурсов я так же планирую расписать в следующих статьях, а пока лишь распишу вкратце содержимое папки «res»:

  1. drawable -папка, которая содержит файлы с графическим контентом а так же xml-предписания работы с ними, не обязательна.
  2. anim — папка, в которой содержаться xml-файлы с описанием анимации.
  3. layout — папка, содержит xml описание слоем для реализации UI.
  4. values — папка — контейнер для таких xml-файлов как: strings, styles, colors, dimens, arrays (контент обозначенных файлов соответствует их названию).
  5. xml — папка содержащая различные xml-файлы вспомогательного характера.
  6. raw — папка для хранения не XML данных используемых в приложении.

Казалось бы, судя по структуре все и так понятно. Но уверяю вас — это только вершина айсберга. Путем нехитрых манипуляций с этими папками и их контентом можно достаточно легко реализовывать такие вещи как интернационализация и локализация вашего приложения, а так же реализовать, примерно, до 80% работы с вашим UI.

Подводя итоги

В ходе написания статьи, я понял что меня «понесло»..и писать статей мне еще придется целую кучу, дабы излить все то, что «накипело» по разработке для Android.

Надеюсь, кому-то она покажется полезной, а я, в свою очередь, постараюсь как можно быстрее выдать продолжение.

Поскольку разработка приложений под Android набирает популярность, думаю обзор основных UI паттернов для Android-приложений будет кому-то полезен. Основой для статьи является вот этот вот источник. Рассматриваемые паттерны: Dashboard, Action Bar, Quick Actions, Search Bar и Companion Widget.

На мой взгляд тема UI паттернов является важной по нескольким причинам:

  1. Привлечение пользователей: паттерны помогают сделать приложение более юзабильным, более понятным.
  2. Проход на рынок: следование паттернам может сыграть важную роль при продвижении приложения на app market’ы.
  3. Не стоит строить велосипед: при знании паттернов намного проще заниматься проектированием интерфейса приложения, используя имеющиеся решения.

Принципы дизайна интерфейса, отмеченные инженерами Google:

  • Simple vs Clear : интерфейс должен быть простым(не нагруженным) и понятным для использования
  • Content vs Chrome : необходимо использовать максимум экрана, при этом уменьшать его визуальную сложность (использовать ограниченное число кнопок/иконок)
  • Consistent yet engaging : консистентность реакции пользователя – пользователь должен понимать что он делает/как сделать то, что ему необходимо
  • Enhanced by cloud : данные пользователя следует хранить в облаке; пользователь должен иметь возможность выбирать настройки(организовывать данные) один раз, без повторных действий.

UI Design Patterns (по аналогии с Software Design Patterns) описывают общее решение для повторно возникаемых задач/проблем и возникают как “побочный продукт” процесса разработки.

Ниже перечислены пять UI паттернов с примерами на основе .

DashBoard

Dashboard (Панель инструментов) – представляет описание основных возможностей приложения, является главным меню приложения. Dashboard занимает весь экран, фокусируется на 3-6 наиболее важных функциях приложения, также может содержать информацию об обновлениях.
Поскольку паттерн Dashboard по сути является лицом приложения, подходить к его разработке нужно особенно аккуратно.

Action Bar

Action Bar предоставляет быстрый доступ к дополнительным функциям приложения, является наилучшим решением представления функций, используемых из любой точки приложения(поиск, синхронизация, рефреш и т.п.).
Action Bar не является полноценной заменой меню, но содержит ключевые действия, которые пользователь может выполнить(пользователь не должен входить в меню для выполнения этих действий). При этом паттерн не должен содержать контекстуальных действий(таких как копировать/вставить) Action Bar может также использоваться для ориентирования пользователя в приложении(а именно, показывать ему где он находится).

Quick Actions

Quick Actions – предоставляет доступ к контекстуальным функциям приложения, вызывается при клике на ”цели”, выводится на эран в качестве popup. Ключевые характеристики Quick Actions: действия должны соответствовать контексту, быть простыми и понятными(возможно использование иконок), действий не должно быть много. Стоит также отметить, что всплывающий popup не должен перекрывать “цели”(должен появляться либо снизу, либо сверху по отношению к “цели”). Использовать данный паттерн рекомендуется когда нет детального описания item-a, а также когда в приложении необходимо выполнить дополнительные действия, связанные с контекстом. Quick Actions не следует использовать, когда доступен мультиселект.

Search Bar
Companion Widget

Companion Widget – виджет, представляет основную информацию о приложении, может быть настроен пользователем. Кроме иконки должен иметь содержание(описание, значек апдейта, возможно некоторые функции приложения), должен сохранять пространство рабочего стола, а также предоставлять пользователю возможность настройки вида виджета.
Инженеры Google рекомендуют уделять больше внимания этому элементу интерфейса, поскольку он играет важное значение во взаимодействии с пользователем. Простой ярлык приложения – не самое лучшее решение.

Рассмотренные паттерны являются базовыми при разработке Android приложений, однако это не значит что все их обязательно необходимо применять. Главной все же остается идея, исходя из которой можно рассматривать различные варианты решений(это к тому что, разрабатывать все же стоит от идеи, а не от паттерна).
Удачи вам в реализации ваших идей!

Gu devsite-toc-embedded >

This guide encompasses best practices and recommended architecture for building robust, production-quality apps.

This page assumes a basic familiarity with the Android Framework. If you are new to Android app development, check out our Developer guides to get started and learn more about the concepts mentioned in this guide.

If you’re interested in app architecture, and would like to see the material in this guide from a Kotlin programming perspective, check out the Udacity course Developing Android Apps with Kotlin.

Mobile app user experiences

In the majority of cases, desktop apps have a single entry point from a desktop or program launcher, then run as a single, monolithic process. Android apps, on the other hand, have a much more complex structure. A typical Android app contains multiple app components, including activities, fragments, services, content providers, and broadcast receivers.

You declare most of these app components in your app manifest. The Android OS then uses this file to decide how to integrate your app into the device’s overall user experience. Given that a properly-written Android app contains multiple components and that users often interact with multiple apps in a short period of time, apps need to adapt to different kinds of user-driven workflows and tasks.

For example, consider what happens when you share a photo in your favorite social networking app:

  1. The app triggers a camera intent. The Android OS then launches a camera app to handle the request. At this point, the user has left the social networking app, but their experience is still seamless.
  2. The camera app might trigger other intents, like launching the file chooser, which may launch yet another app.
  3. Eventually, the user returns to the social networking app and shares the photo.

At any point during the process, the user could be interrupted by a phone call or notification. After acting upon this interruption, the user expects to be able to return to, and resume, this photo-sharing process. This app-hopping behavior is common on mobile devices, so your app must handle these flows correctly.

Keep in mind that mobile devices are also resource-constrained, so at any time, the operating system might kill some app processes to make room for new ones.

Given the conditions of this environment, it’s possible for your app components to be launched individually and out-of-order, and the operating system or user can destroy them at any time. Because these events aren’t under your control, you shouldn’t store any app data or state in your app components, and your app components shouldn’t depend on each other.

Common architectural principles

If you shouldn’t use app components to store app data and state, how should you design your app?

Separation of concerns

The most important principle to follow is separation of concerns. It’s a common mistake to write all your code in an Activity or a Fragment . These UI-based classes should only contain logic that handles UI and operating system interactions. By keeping these classes as lean as possible, you can avoid many lifecycle-related problems.

Keep in mind that you don’t own implementations of Activity and Fragment ; rather, these are just glue classes that represent the contract between the Android OS and your app. The OS can destroy them at any time based on user interactions or because of system conditions like low memory. To provide a satisfactory user experience and a more manageable app maintenance experience, it’s best to minimize your dependency on them.

Drive UI from a model

Another important principle is that you should drive your UI from a model, preferably a persistent model. Models are components that are responsible for handling the data for an app. They’re independent from the View objects and app components in your app, so they’re unaffected by the app’s lifecycle and the associated concerns.

Persistence is ideal for the following reasons:

  • Your users don’t lose data if the Android OS destroys your app to free up resources.
  • Your app continues to work in cases when a network connection is flaky or not available.

By basing your app on model classes with the well-defined responsibility of managing the data, your app is more testable and consistent.

Recommended app architecture

In this section, we demonstrate how to structure an app using Architecture Components by working through an end-to-end use case.

Imagine we’re building a UI that shows a user profile. We use a private backend and a REST API to fetch the data for a given profile.

Overview

To start, consider the following diagram, which shows how all the modules should interact with one another after designing the app:

Notice that each component depends only on the component one level below it. For example, activities and fragments depend only on a view model. The repository is the only class that depends on multiple other classes; in this example, the repository depends on a persistent data model and a remote backend data source.

This design creates a consistent and pleasant user experience. Regardless of whether the user comes back to the app several minutes after they’ve last closed it or several days later, they instantly see a user’s information that the app persists locally. If this data is stale, the app’s repository module starts updating the data in the background.

Build the user interface

The UI consists of a fragment, UserProfileFragment , and its corresponding layout file, user_profile_layout.xml .

To drive the UI, our data model needs to hold the following data elements:

  • User ID: The identifier for the user. It’s best to pass this information into the fragment using the fragment arguments. If the Android OS destroys our process, this information is preserved, so the ID is available the next time our app is restarted.
  • User object: A data class that holds details about the user.

We use a UserProfileViewModel , based on the ViewModel architecture component, to keep this information.

A ViewModel object prov >ViewModel can call other components to load the data, and it can forward user requests to modify the data. The ViewModel doesn’t know about UI components, so it isn’t affected by configuration changes, such as recreating an activity when rotating the device.

We’ve now defined the following files:

  • user_profile.xml : The UI layout definition for the screen.
  • UserProfileFragment : The UI controller that displays the data.
  • UserProfileViewModel : The >UserProfileFragment and reacts to user interactions.

The following code snippets show the starting contents for these files. (The layout file is omitted for simplicity.)

Now that we have these code modules, how do we connect them? After all, when the user field is set in the UserProfileViewModel class, we need a way to inform the UI.


To obtain the user , our ViewModel needs to access the Fragment arguments. We can either pass them from the Fragment, or better, using the SavedState module, we can make our ViewModel read the argument directly:

Now we need to inform our Fragment when the user object is obtained. This is where the LiveData architecture component comes in.

LiveData is an observable data holder. Other components in your app can monitor changes to objects using this holder without creating explicit and rigid dependency paths between them. The LiveData component also respects the lifecycle state of your app’s components—such as activities, fragments, and services—and includes cleanup logic to prevent object leaking and excessive memory consumption.

Note: If you’re already using a library like RxJava, you can continue using them instead of LiveData. When you use libraries and approaches like these, however, make sure you handle your app’s lifecycle properly. In particular, make sure to pause your data streams when the related LifecycleOwner is stopped and to destroy these streams when the related LifecycleOwner is destroyed. You can also add the android.arch.lifecycle:reactivestreams artifact to use LiveData with another reactive streams library, such as RxJava2.

To incorporate the LiveData component into our app, we change the field type in the UserProfileViewModel to LiveData . Now, the UserProfileFragment is informed when the data is updated. Furthermore, because this LiveData field is lifecycle aware, it automatically cleans up references after they’re no longer needed.

Now we modify UserProfileFragment to observe the data and update the UI:

Every time the user profile data is updated, the onChanged() callback is invoked, and the UI is refreshed.

If you’re familiar with other libraries where observable callbacks are used, you might have realized that we d >onStop() method to stop observing the data. This step isn’t necessary with LiveData because it’s lifecycle aware, which means it doesn’t invoke the onChanged() callback unless the fragment is in an active state; that is, it has received onStart() but hasn’t yet received onStop() ). LiveData also automatically removes the observer when the fragment’s onDestroy() method is called.

We also d >UserProfileViewModel is automatically restored when the configuration changes, so as soon as the new fragment is created, it receives the same instance of ViewModel , and the callback is invoked immediately using the current data. Given that ViewModel objects are intended to outlast the corresponding View objects that they update, you shouldn’t include direct references to View objects within your implementation of ViewModel . For more information about the lifetime of a ViewModel corresponds to the lifecycle of UI components, see The lifecycle of a ViewModel.

Fetch data

Now that we’ve used LiveData to connect the UserProfileViewModel to the UserProfileFragment , how can we fetch the user profile data?

For this example, we assume that our backend provides a REST API. We use the Retrofit library to access our backend, though you are free to use a different library that serves the same purpose.

Here’s our definition of Webservice that communicates with our backend:

A first >ViewModel might involve directly calling the Webservice to fetch the data and assign this data to our LiveData object. This design works, but by using it, our app becomes more and more difficult to maintain as it grows. It gives too much responsibility to the UserProfileViewModel >ViewModel is tied to an Activity or Fragment lifecycle, which means that the data from the Webservice is lost when the associated UI object’s lifecycle ends. This behavior creates an undesirable user experience.

Instead, our ViewModel delegates the data-fetching process to a new module, a repository.

Repository modules handle data operations. They provide a clean API so that the rest of the app can retrieve this data easily. They know where to get the data from and what API calls to make when data is updated. You can consider repositories to be mediators between different data sources, such as persistent models, web services, and caches.

Our UserRepository >WebService to fetch a user’s data:

Even though the repository module looks unnecessary, it serves an important purpose: it abstracts the data sources from the rest of the app. Now, our UserProfileViewModel doesn’t know how the data is fetched, so we can provide the view model with data obtained from several different data-fetching implementations.

Manage dependencies between components

The UserRepository >Webservice to fetch the user’s data. It could simply create the instance, but to do that, it also needs to know the dependencies of the Webservice >UserRepository is probably not the only >Webservice . This situation requires us to duplicate code, as each >Webservice needs to know how to construct it and its dependencies. If each >WebService , our app could become very resource heavy.

You can use the following design patterns to address this problem:

  • Dependency injection (DI): Dependency injection allows classes to define their dependencies without constructing them. At runtime, another class is responsible for providing these dependencies. We recommend the Dagger 2 library for implementing dependency injection in Android apps. Dagger 2 automatically constructs objects by walking the dependency tree, and it provides compile-time guarantees on dependencies.
  • Service locator: The service locator pattern provides a registry where classes can obtain their dependencies instead of constructing them.

It’s easier to implement a service registry than use DI, so if you aren’t familiar with DI, use the service locator pattern instead.

These patterns allow you to scale your code because they provide clear patterns for managing dependencies without duplicating code or adding complexity. Furthermore, these patterns allow you to quickly switch between test and production data-fetching implementations.

Our example app uses Dagger 2 to manage the Webservice object’s dependencies.

Connect ViewModel and the repository

Now, we modify our UserProfileViewModel to use the UserRepository object:

Cache data

The UserRepository implementation abstracts the call to the Webservice object, but because it relies on only one data source, it’s not very flexible.

The key problem with the UserRepository implementation is that after it fetches data from our backend, it doesn’t store that data anywhere. Therefore, if the user leaves the UserProfileFragment , then returns to it, our app must re-fetch the data, even if it hasn’t changed.

This design is suboptimal for the following reasons:

  • It wastes valuable network bandwidth.
  • It forces the user to wait for the new query to complete.

To address these shortcomings, we add a new data source to our UserRepository , which caches the User objects in memory:

Persist data

Using our current implementation, if the user rotates the device or leaves and immediately returns to the app, the existing UI becomes visible instantly because the repository retrieves data from our in-memory cache.

However, what happens if the user leaves the app and comes back hours later, after the Android OS has killed the process? By relying on our current implementation in this situation, we need to fetch the data again from the network. This refetching process isn’t just a bad user experience; it’s also wasteful because it consumes valuable mobile data.

You could fix this issue by caching the web requests, but that creates a key new problem: What happens if the same user data shows up from another type of request, such as fetching a list of friends? The app would show inconsistent data, which is confusing at best. For example, our app might show two different versions of the same user’s data if the user made the list-of-friends request and the single-user request at different times. Our app would need to figure out how to merge this inconsistent data.

The proper way to handle this situation is to use a persistent model. This is where the Room persistence library comes to the rescue.

Room is an object-mapping library that provides local data persistence with minimal boilerplate code. At compile time, it validates each query against your data schema, so broken SQL queries result in compile-time errors instead of runtime failures. Room abstracts away some of the underlying implementation details of working with raw SQL tables and queries. It also allows you to observe changes to the database’s data, including collections and join queries, exposing such changes using LiveData objects. It even explicitly defines execution constraints that address common threading issues, such as accessing storage on the main thread.

Note: If your app already uses another persistence solution, such as a SQLite object-relational mapping (ORM), you don’t need to replace your existing solution with Room. However, if you’re writing a new app or refactoring an existing app, we recommend using Room to persist your app’s data. That way, you can take advantage of the library’s abstraction and query validation capabilities.

To use Room, we need to define our local schema. First, we add the @Entity annotation to our User data model >@PrimaryKey annotation to the >id field. These annotations mark User as a table in our database and id as the table’s primary key:

Notice that UserDatabase is abstract. Room automatically provides an implementation of it. For details, see the Room documentation.

We now need a way to insert user data into the database. For this task, we create a data access object (DAO).

Notice that the load method returns an object of type LiveData . Room knows when the database is modified and automatically notifies all active observers when the data changes. Because Room uses LiveData, this operation is efficient; it updates the data only when there is at least one active observer.

With our UserDao class defined, we then reference the DAO from our database class:

Now we can modify our UserRepository to incorporate the Room data source:

Notice that even though we changed where the data comes from in UserRepository , we d >UserProfileViewModel or UserProfileFragment . This small-scoped update demonstrates the flexibility that our app’s architecture prov >UserRepository and test our production UserProfileViewModel at the same time.

If users wait a few days before returning to an app that uses this architecture, it’s likely that they’ll see out-of-date information until the repository can fetch updated information. Depending on your use case, you may not want to show this out-of-date information. Instead, you can display placeholder data, which shows dummy values and indicates that your app is currently fetching and loading up-to-date information.

Single source of truth

It’s common for different REST API endpoints to return the same data. For example, if our backend has another endpoint that returns a list of friends, the same user object could come from two different API endpoints, maybe even using different levels of granularity. If the UserRepository were to return the response from the Webservice request as-is, without checking for consistency, our UIs could show confusing information because the version and format of data from the repository would depend on the endpoint most recently called.

For this reason, our UserRepository implementation saves web service responses into the database. Changes to the database then trigger callbacks on active LiveData objects. Using this model, the database serves as the single source of truth, and other parts of the app access it using our UserRepository . Regardless of whether you use a disk cache, we recommend that your repository designate a data source as the single source of truth for the rest of your app.

Show in-progress operations

In some use cases, such as pull-to-refresh, it’s important for the UI to show the user that there’s currently a network operation in progress. It’s good practice to separate the UI action from the actual data because the data might be updated for various reasons. For example, if we fetched a list of friends, the same user might be fetched again programmatically, triggering a LiveData update. From the UI’s perspective, the fact that there’s a request in flight is just another data point, similar to any other piece of data in the User object itself.

We can use one of the following strategies to display a consistent data-updating status in the UI, regardless of where the request to update the data came from:

  • Change getUser() to return an object of type LiveData . This object would include the status of the network operation.
    For an example, see the NetworkBoundResource implementation in the andro >UserRepository >User . This option is better if you want to show the network status in your UI only when the data-fetching process originated from an explicit user action, such as pull-to-refresh.

Test each component

In the separation of concerns section, we mentioned that one key benefit of following this principle is testability.

The following list shows how to test each code module from our extended example:

UserDao: Test DAO > Caution:Room allows specifying the database implementation, so it’s possible to test your DAO by prov >SupportSQLiteOpenHelper . This approach isn’t recommended, however, because the SQLite version running on the device might differ from the SQLite version on your development machine.

Webservice: In these tests, avoid making network calls to your backend. It’s important for all tests, especially web-based ones, to be independent from the outside world. Several libraries, including MockWebServer, can help you create a fake local server for these tests.

Testing Artifacts: Architecture Components prov >androidx.arch.core:core-testing artifact contains the following JUnit rules:

  • InstantTaskExecutorRule : Use this rule to instantly execute any background operation on the calling thread.
  • CountingTaskExecutorRule : Use this rule to wait on background operations of Architecture Components. You can also associate this rule with Espresso as an idling resource.

Best practices

Programming is a creative field, and building Android apps isn’t an exception. There are many ways to solve a problem, be it communicating data between multiple activities or fragments, retrieving remote data and persisting it locally for offline mode, or any number of other common scenarios that nontrivial apps encounter.

Although the following recommendations aren’t mandatory, it has been our experience that following them makes your code base more robust, testable, and maintainable in the long run:

Avoid designating your app’s entry points—such as activities, services, and broadcast receivers—as sources of data.

Instead, they should only coordinate with other components to retrieve the subset of data that is relevant to that entry point. Each app component is rather short-lived, depending on the user’s interaction with their device and the overall current health of the system.

Create well-defined boundaries of responsibility between various modules of your app.

For example, don’t spread the code that loads data from the network across multiple classes or packages in your code base. Similarly, don’t define multiple unrelated responsibilities—such as data caching and data binding—into the same class.

Expose as little as possible from each module.

Don’t be tempted to create «just that one» shortcut that exposes an internal implementation detail from one module. You might gain a bit of time in the short term, but you then incur technical debt many times over as your codebase evolves.

Consider how to make each module testable in isolation.

For example, having a well-defined API for fetching data from the network makes it easier to test the module that persists that data in a local database. If, instead, you mix the logic from these two modules in one place, or distribute your networking code across your entire code base, it becomes much more difficult—if not impossible—to test.

Focus on the unique core of your app so it stands out from other apps.

Don’t reinvent the wheel by writing the same boilerplate code again and again. Instead, focus your time and energy on what makes your app unique, and let the Android Architecture Components and other recommended libraries handle the repetitive boilerplate.

Persist as much relevant and fresh data as possible.

That way, users can enjoy your app’s functionality even when their device is in offline mode. Remember that not all of your users enjoy constant, high-speed connectivity.

Assign one data source to be the single source of truth.

Whenever your app needs to access this piece of data, it should always originate from this single source of truth.

Addendum: exposing network status

In the recommended app architecture section above, we omitted network error and loading states to keep the code snippets simple.

This section demonstrates how to expose network status using a Resource class that encapsulate both the data and its state.

The following code snippet prov >Resource :

Because it’s common to load data from the network while showing the disk copy of that data, it’s good to create a helper >NetworkBoundResource .

The following diagram shows the decision tree for NetworkBoundResource :

It starts by observing the database for the resource. When the entry is loaded from the database for the first time, NetworkBoundResource checks whether the result is good enough to be dispatched or that it should be re-fetched from the network. Note that both of these situations can happen at the same time, given that you probably want to show cached data while updating it from the network.

If the network call completes successfully, it saves the response into the database and re-initializes the stream. If network request fails, the NetworkBoundResource dispatches a failure directly.

Note: After saving new data to disk, we re-initialize the stream from the database. We usually don’t need to do that, however, because the database itself happens to dispatch the change.

Keep in mind that relying on the database to dispatch the change involves relying on the associated side effects, which isn’t good because undefined behavior from these side effects could occur if the database ends up not dispatching changes because the data hasn’t changed.

Also, don’t dispatch the result that arrived from the network because that would violate the single source of truth principle. After all, maybe the database includes triggers that change data values during a «save» operation. Similarly, don’t dispatch `SUCCESS` without the new data, because then the client receives the wrong version of the data.

The following code snippet shows the public API prov >NetworkBoundResource class for its subclasses:

Note these important details about the class’s definition:

  • It defines two type parameters, ResultType and RequestType , because the data type returned from the API might not match the data type used locally.
  • It uses a >ApiResponse for network requests. ApiResponse is a simple wrapper around the Retrofit2.Call >LiveData .

The full implementation of the NetworkBoundResource class appears as part of the android-architecture-components GitHub project.

After creating the NetworkBoundResource , we can use it to write our disk- and network-bound implementations of User in the UserRepository class:

Content and code samples on this page are subject to the licenses described in the Content License. Java is a registered trademark of Oracle and/or its affiliates.

Подробный гайд по разработке Android-приложений с помощью Clean Architecture

Clean Architecture Manifest (v. 0.9.5)

Здесь вы найдете описание основных принципов и правил, которыми стоит руководствоваться при разработке Android-приложений с использованием чистой архитектуры.

Translations:

Если вы хотите перевести этот документ на свой язык, пожалуйста посетите эту страницу.

Clean Achitecture — способ построения архитектуры приложения, предложенный Робертом Мартином (который также известен как дядюшка Боб — Uncle Bob) в 2012 году.

Clean Architecture включает в себя два основных принципа:

  1. Разделение на слои
  2. Инверсия зависимостей

Давайте расшифруем каждый из них.

Разделение на слои

Суть принципа заключается в разделении всего кода приложения на слои. Всего мы имеем три слоя:

  • слой отображения
  • слой бизнес логики
  • слой работы с данными

Самым главным слоем является слой бизнес логики. Особенность данного слоя заключается в том, что он не зависит ни от каких внешних библиотек или фреймворков. Это достигается за счет инверсии зависимостей.

Инверсия зависимостей

Согласно данному принципу слой бизнес-логики не должен зависеть от внешних. То есть классы из внешних слоев не должны использоваться в классах бизнес-логики. Взаимодействие с внешними слоями происходит через интерфейсы, которые реализуют классы внешних слоев.

Благодаря разделению ответственности между классами мы легко можем изменять код приложения, а также добавлять новый функционал, затрагивая при этом минимальное количество классов. Помимо этого мы получаем легко тестируемый код. Стоит заметить, что построение правильной архитектуры целиком и полностью зависит от самого разработчика и его опыта.

Преимущества чистой архитектуры:

  • независимость от UI, БД и фреймворков
  • позволяет быстрее добавлять новые функции
  • более высокий процент покрытия кода тестами
  • повышенная простота навигации по структуре пакетов

Недостатки чистой архитектуры:

  • большое количество классов
  • довольно высокий порог вхождения и, зачастую, неправильное понимание на первых порах

Внимание: перед прочтением данного документа, настоятельно рекомендую ознакомиться со следующими темами (иначе, вы ничего не поймете):

Очень желательно, чтобы у вас был практический опыт их использования, так вы быстрее войдете в курс дела. Если вы уже знакомы с ними, то можете смело приступать к прочтению. Всё объяснение темы Clean Architecture будет строиться вокруг новостного приложения, которое мы, в теории, хотели бы создать.

P. S. Я разработал приложение, чтобы продемонстрировать использование чистой архитектуры на практике. Исходный код вы можете найти здесь — Bubbble.

Слои и инверсия зависимостей

Как уже говорилось ранее, архитектуру приложения, построенную по принципу Clean Architecture можно разделить на три слоя:

  • слой отображения (presentation)
  • слой бизнес-логики (domain)
  • слой работы с данными (data)

Выше представлена схема того, как эти слои взаимодействуют. Черными стрелками обозначены зависимости одних слоев от других, а синими — поток данных. Как видите, слои data и presentation зависят от domain, т. е. они используют его классы. Сам же слой domain ничего не знает о внешних слоях и использует только собственные классы и интерфейсы. Далее мы разберем более подробно каждый из этих слоев, и то, как они взаимодействуют между собой.

Как видно из схемы, все три слоя могут обмениваться данными. Следует отметить, что нельзя допускать прямого взаимодействия между слоями presentation и data. Поток данных должен идти от слоя presentation к domain, а от него к слою data (это может быть, например, передача строки с поисковым запросом или регистрационные данные пользователя). То же самое может происходить и в обратном направлении (например, при передаче списка с результатами поиска).

Слой бизнес-логики (Domain)

Бизнес-логика — это правила, описывающие, как работает бизнес (например, пользователь не может совершить покупку на сумму больше, чем есть на его счёте). Бизнес-логика не зависит от реализации базы данных или интерфейса пользователя. Бизнес-логика меняется только тогда, когда меняются требования бизнеса, и не зависит от используемой СУБД или интерфейса пользователя.

Роберт Мартин разделяет бизнес-логику на два вида: специфичную для конкретного приложения и общую для всех приложений (в том случае, если вы хотите сделать ваш код общим между приложениями под разные платформы).

Бизнес объект (Entity) — хранят бизнес-логику общую для всех приложений.

Interactor – объект, реализующий бизнес-логику специфичную для конкретного приложения.


Но это все в теории. На практике же используются только Interactor’ы. По крайней мере, мне не встречались приложения, использующие Entity. Кстати, многие путают Entity с DTO (Data Transfer Object). Дело в том, что Entity из Clean Architecture — это не совсем те Entity, которые мы привыкли видеть. Данная статья проливает свет на этот вопрос, а также на многие другие.

Сценарий использования (Use Case) — набор операций для выполнения какой-либо задачи. Пример сценария использования при регистрации пользователя:

  1. Проверяем данные пользователя
  2. Отправляем данные на сервер для регистрации
  3. Сообщаем пользователю об успешной регистрации или ошибке
  1. Пользователь ввел неверные данные (выдаем ошибку)

Давайте теперь посмотрим как это выглядит на практике. Роберт Мартин предлагает создавать для каждого сценария использования отдельный класс, который имеет один метод для его запуска. Пример такого класса:

Однако практика показывает, что при таком подходе получается огромное количество классов, с малым количеством кода. Более правильным будет создание одного Interactor’а на один экран, методы которого реализуют определенный сценарий, например:

Как видите иногда методы Interactor’а могут и вовсе не содержать бизнес-логики, а методы Interactor’а выступают в качестве прослойки между Repository и Presenter’ом.

Если вы заметили, методы Interactor’а возвращают не просто результат, а классы RxJava 2 (в зависимости от типа операции мы используем разные классы — Single, Completable и т. д.). Это дает несколько преимуществ:

  1. Не нужно создавать слушатели для получения результата.
  2. Легко переключать потоки.
  3. Легко обрабатывать ошибки.

Для переключения потоков мы используем, как обычно, метод subscribeOn, однако мы получаем Scheduler не через статические методы класса Schedulers, а при помощи SchedulersProvider’а. В будущем это поможет нам при тестировании.

Слой работы с данными (Data)

В данном слое содержится всё, что связано с хранением данных и управлением ими. Это может работа с базой данных, SharedPreferences, сетью или файловой системой, а также логика кеширования, если она имеется.

«Мостом» между слоями data и domain является интерфейс Repository (в оригинальной схеме дядюшки Боба он называется Gateway). Сам интерфейс находится в слое domain, а уже реализация располагается в слое data. При этом классы domain-слоя не знают откуда берутся данные — из БД, сети или откуда-то ещё. Именно поэтому вся логика кеширования должна содержаться в data-слое.

Repository — представляет из себя интерфейс, с которым работает Interactor. В нем описывается какие данные хочет получать Interactor от внешних слоев. В приложении может быть несколько репозиториев, в зависимости от задачи. Например, если мы делаем новостное приложение, репозиторий работающий со статьями может называться ArticleRepository, а репозиторий для работы с комментариями CommentRepository. Пример репозитория, работающего со статьями:

Слой отображения (Presentation)

Слой представления содержит все компоненты, которые связаны с UI, такие как View-элементы, Activity, Fragment’ы и т. д. Помимо этого здесь содержатся Presenter’ы и View (или ViewModel’и при использовании MVVM). В данном туториале для реализации слоя presentation будет использован шаблон MVP, но вы можете выбрать любой другой (MVVM, MVI).

Для более удобной связки View и Presenter мы будем использовать библиотеку Moxy. Она помогает решить многие проблемы, связанные с жизненным циклом Activity или Fragment’а. Moxy имеет базовые классы, такие как MvpView и MvpPresenter , от которых должны наследоваться наши View и Presenter. Для избежания написания большого количества кода по связыванию View и Presenter, Moxy использует кодогенерацию. Для правильной работы кодогенерации мы должны использовать специальные аннотации, которые предоставляет нам Moxy. Более подробную информацию о библиотеке можно найти здесь.

MVP расшифровывается как Model-View-Presenter (модель-представление-презентер). Model содержит в себе бизнес-логику и код по работе с данными. Т. к. мы используем связку Clean Architecture + MVP, то Model у нас является код находящийся в слоях Data (работа с данными) и Domain (бизнес-логика). Следовательно, в слое Presentation остаются лишь два компонента — View и Presenter.

View отвечает за то, каким образом данные будут показаны пользователю. В случае с Android в качестве View выступает Activity или Fragment. Также View сообщает о действиях пользователя Presenter’у, будь то нажатие на кнопку или ввод текста. Пример View:

Пока мы описали лишь интерфейс View, т. е. какие команды Presenter может отдавать View. Обратите внимание, что наш интерфейс наследуется от интерфейса MvpView, входящего в библиотеку Moxy. Это является обязательным условием для корректной работы библиотеки.

Согласно концепции MVP, View не может напрямую взаимодействовать с Model, поэтому связующим звеном между ними является Presenter. Presenter реагирует на действия пользователя, о которых ему сообщила View (такие как нажатие на кнопку, пункт списка или ввод текста), после чего принимает решения о том, что делать дальше. Например, это может быть запрос данных у модели и отображение их во View. Пример Presenter’а:

Все необходимые классы для работы Presenter’а (как и всех остальных классов) мы передаем через конструктор. Этот способ так и называется — внедрение через конструктор.

При создании объекта Presenter’а мы должны передать ему запрашиваемые конструктором зависимости. Если их будет много, то создание Presenter’а будет довольно сложным делом. Чтобы не делать этого вручную, мы доверим это дело Component’у.

Он подставит нужные зависимости, а нам нужно будет лишь получить инстанс Presenter’а вызвав метод getPresenter(). Если у вас возник вопрос «А как в таком случае передавать аргументы в Presenter?», то загляните в FAQ — там подробно описан этот вопрос.

Иногда можно встретить такое, что в конструктор передается DI-контейнер (Component), после чего все необходимые зависимости внедряются в поля:

Однако, данный способ является неправильным, т. к. усложняет тестирование класса и создает кучу ненужного кода. Если в первом случае мы сразу могли передать mock’и классов через конструктор, то теперь нам нужно создать DI-контейнер и передавать его. Также данный способ делает класс зависимым от конкретного DI-фреймворка, что тоже не есть хорошо.

Также обратите внимание на то, что перед тем как отобразить результаты, полученные от Interactor’а, мы переключаем поток на UI при помощи observeOn(schedulersProvider.ui()) . Это сделано потому, что мы не знаем заранее в каком потоке нам придут данные.

Связывание View с Presenter’ом

В контексте разработки под Android роль View на себя берет Activity (или Fragment), поэтому после создания интерфейса View, мы должны реализовать его в нашей Activity или Fragment’е:

Хочу заметить, что для правильной работы библиотеки Moxy, наша Activity должна обязательно наследоваться от класса MvpAppCompatActivity (или MvpAppCompatFragment в случае, если вы используете фрагменты). С помощью аннотации @InjectPresenter мы сообщаем Annotation Processor’у в какую переменную нужно «положить» Presenter.

Так как конструктор нашего Presenter’а не пустой, а принимает на вход определенные параметры, нам нужно предоставить библиотеке объект Presenter’а. Мы делаем это при помощи метода provideArticlesListPresenter , который мы пометили аннотацией @ProvidePresenter . Как и во всех других случаях использования кодогенерации, переменные и методы, помеченные аннотациями, должны быть видны на уровне пакета, т. е. у них не должно быть модификаторов видимости (private, public, protected).

Разбиение классов по пакетам

Ниже представлен пример разбиения пакетов по фичам новостного приложения:

Прежде чем делить код по фичам, мы разделили его на слои. Данный подход позволяет сразу определить к какому слою относится тот или иной класс. Если вы заметили, классы слоя data разбиты немного не так, как в слоях domain, presentation и di. Здесь вместо фич приложения мы выделили типы источников данных — сеть, база данных, файловая система. Это связано с тем, что все фичи используют практически одни и те же классы (например, NewsApiService) и их не имеет смысла разбивать по фичам.

В пакетах с именем global хранятся общие классы, которые используются в нескольких фичах. Например, в пакете data/global хранятся модели и интерфейсы репозиториев.

Слой presentation разбит на два пакета — mvp и ui. В mvp хранятся, как понятно из названия, классы Presenter’ов и View. В ui хранятся реализация слоя View из MVP, т. е. Activity, Fragment’ы и т. д.

Разбиение классов по фичам имеет ряд преимуществ:

  • Очевидность. Даже не знакомый с проектом разработчик, при первом взгляде на структуру пакетов сможет примерно понять что делает приложение, не заглядывая в сам код.
  • Добавление нового функционала. Если вы решили добавить новую функцию в приложение, например, просмотр профиля пользователя, то вам лишь нужно добавить пакет userprofile и работать только с ним, а не «гулять» по всей структуре пакетов, создавая нужные классы.
  • Удобство редактирования. При редактировании какой либо фичи, нужно держать открытыми максимум два-три пакета и вы видите только те классы, которые относятся к конкретной фиче. При разбиении по типу класса, раскрытой приходится держать практически всё дерево пакетов и вы видите классы, которые вам сейчас не нужны, относящиеся к другим фичам.
  • Удобство масштабирования. При увеличении количества функций приложения, увеличивается и количество классов. При разбиении классов по типу, добавление новых классов делает навигацию по ним очень не удобным, т.к. приходится искать нужный класс, среди десятков других, что сказывается на скорости и удосбстве разработки. Разбиение по фичам решает эту проблему, т.к. вы можете объединить связанные между собой пакеты с фичами (например, можно объединить пакеты login и registration в пакет authentication).

Также хочется сказать пару слов об именовании пакетов: в каком числе их нужно называть — множественном или единственном? Я придерживаюсь подхода, описанного здесь:

  1. Если пакет содержит однородные классы, то имя пакета ставится во множественном числе. Например, пакет с классами Dog, Cat и Cow будет называться animals. Другой пример — различные реализации какого-либо интерфейса (XmlResponseAdapter, JsonResponseAdapter).
  2. Если пакет содержит разнородные классы, реализующую определенную функцию, то имя пакета ставится в единственном числе. Пример — пакет order, содержащий классы OrderInfo, OrderInteractor, OrderVal >

Дополнительные сущности, используемые на практике

Т. к. Presenter содержит в себе логику реагирования на действия пользователя, то он также знает о том, на какой экран нужно перейти. Однако сам Presenter не может осуществлять переход на новый экран, т. к. для этого нам требуется Context. Поэтому за открытие нового экрана должна отвечать View. Для осуществления перехода на следующий экран мы должны вызвать метод View, например, openProfileScreen(), а уже в реализации самого метода осуществлять переход. Помимо данного подхода некоторые разработчики используют для навигации так называемый Router.

Router — класс, для осуществления переходов между экранами (активити или фрагментами).

Для реализации Router’а вы можете использовать библиотеку Alligator.

Mapper — специальный класс, для конвертирования моделей из одного типа в другой, например, из модели БД в модель бизнес-логики. Обычно они имеют название типа XxxMapper, и имеют единственный метод с названием map (иногда встречаются названия convert/transform), например:

Т. к. слой domain ничего не знает о классах других слоев, то маппинг моделей должен выполняться во внешних слоях, т. е. репозиторием (при конвертации data > domain или domain > data) или презентером (при конвертации domain > presentation и наоборот) .

В некоторых случаях может потребоваться получить строку или число из ресурсов приложения в Presenter’е или слое domain . Однако, мы знаем, что они не должны напрямую взаимодействовать с фреймворком Android. Чтобы решить эту проблему мы можем создать специальную сущность ResourceManager, для доступа к внешним ресурсам. Для этого мы создаем интерфейс:

Сам интерфейс должен располагаться в слое domain. После этого в слое presentation мы создаем реализацию нашего интерфейса:

Далее мы должны связать интерфейс и реализацию нашего ResourceManager’а в ApplicationModule:

Теперь мы можем использовать ResourceManager в Presenter’е или Interactor’ах:

Наверное, у внимательных читателей возник вопрос: почему мы используем класс R в Presenter’е? Ведь он также относится к Android? На самом деле, это не совсем так. Класс R вообще не использует никакие классы, и представляет из себя набор идентификаторов ресурсов. Поэтому, нет ничего плохого, чтобы использовать его в Presenter’е.

Перед началом тестирования нам нужно сделать все операции синхронными. Для этого мы должны заменить все Scheduler’ы на TestScheduler, поэтому мы не устанавливаем Scheduler’ы напрямую через класс Schedulers, а используем SchedulersProvider:

Благодаря этому мы можем легко заменить Scheduler’ы на нужные нам, всего лишь создав наследника класса SchedulersProvider’а и переопределив методы:

Далее, при самом тестировании, нам нужно будет лишь использовать TestSchedulersProvider вместо SchedulersProvider. Более подробно о тестировании кода с RxJava можно почитать здесь.

[раздел на доработке]

Одним из самых главных преимуществ чистой архитектуры является то, что мы можем покрыть тестами намного больший функционал приложения за счет разбиения кода на мелкие классы, каждый из которых выполняет строго определенную задачу. Благодаря принципу инверсии зависимостей, используемому в чистой архитектуре мы можем с легкостью подменять реализацию тех или иных классов на фейковые, которые реализуют нужное нам поведение.

Прежде чем начать писать тесты, мы должны ответить себе на два вопроса:

  • что мы хотим тестировать?
  • как мы будем это тестировать?

Что мы хотим тестировать:

  • Мы хотим проверить нашу бизнес-логику независимо от какого-либо фреймворка или библиотеки.
  • Мы хотим протестировать нашу интеграцию с API.
  • Мы хотим протестировать нашу интеграцию с нашей системой персистентности.
  • Все, что содержит условия.

Что мы НЕ должны тестировать:

  • Сторонние библиотеки (мы предполагаем, что они работают правильно, потому что уже протестированы разработчиками)
  • Тривиальный код (например, геттеры и сеттеры)

Теперь, давайте разберём то, как мы будем тестировать каждый из слоев.

Тестирование слоя представления

Данный слой включает в себя 2 типа тестов: Unit-тесты и UI-тесты.

  • Unit-тесты используются для тестирования Presenter’ов.
  • UI-тесты используются для тестирования Activity (проверяется корректность отображения элементов и т. д.).

Существуют различные соглашения по именованию тестовых методов. Например, в этой статье описаны некоторые из них. В примерах, которые я буду приводить далее, я не буду придерживаться какого-либо соглашения. Самое главное понять из названия, что тестирует наш метод и что мы хотим получить в результате.

Давайте рассмотрим пример теста для ArticlesListPresenter:

Как видите, мы разделили код теста на три части:

  • Подготовка к тестированию. Здесь мы инициализируем объекты для тестирования, подготавливаем тестовые данные, а также предопределяем поведение моков.
  • Само тестирование.
  • Проверка результатов тестирования. Здесь мы проверяем, что у View были вызваны нужные методы и переданы аргументы.

В данном слое тестируюится классы Interactor’ов и Entity. Необходимо проверить, действительно ли бизнес-логика реализует требуемое поведение .

[раздел на доработке]

Тестирование слоя работы с данными

[раздел на доработке]

Начало разработки приложения с использованием Clean Architecture

Если вы начиначете разработку нового мобильного приложения, то лучше начать с создания пользовательского интерфейса, т. к. именно UI определяет

[раздел на доработке]

Перенос на Clean Architecture существующих проектов

[раздел на доработке]

FAQ по Clean Architecture

Стоит ли переписывать весь проект при переносе проекта на Clean Architecture?

Наверное, нет однозначного ответа на этот вопрос. Если проект большой и переход на Clean Architecture может занять длительный промежуток времени, то лучше переписывать код постепенно, используя подход, который мы описали выше. Если же проект простой и состоит из 2-3 экранов, а сроки не поджимают, то вы можете попробовать переписать проект с нуля.

В конце хочется привести поучительную историю про Netscape, который переписывали с нуля больше, чем три года — Things You Should Never Do, Part I

Обязательно ли создавать отдельные сущности для каждого из слоев (Domain, Data, Presentaion)?

Согласно принципам Clean Architecture, слой Domain ничего не должен знать о внешних слоях (Data и Presentation), но внешние слои без проблем могут использовать классы из слоя Domain. Следовательно, можно не создавать отдельные сущности для каждого из слоев, а использовать только те, что лежат в слое Domain. Однако, если их формат не совпадает с тем, что используется во внешних слоях, то нужно создать отдельную сущность. Если вам нужно добавить к моделям аннотации от библиотек (например, от Gson или Room), то вы можете добавить их прямо в модели Domain-слоя, несмотря на то, что это внешние библиотеки слоя Data, т. к. создание отдельных моделей приведёт к ненужному дублированию кода.

Нужно ли создавать интерфейсы для классов Presenter и Interactor для улучшения тестируемости кода?

Пример Presenter’а с интерфейсом:

Нет, интерфейсы для презентера и интерактора создавать не нужно. Это создает дополнительные сложности при разработке, при этом пользы от данного подхода практически нет. Вот лишь некоторые проблемы, которые порождает создание лишних интерфейсов:

  • Если мы хотим добавить новый метод или изменить существующий, нам нужно изменить интерфейс. Помимо этого мы также должны изменить реализацию метода. Это занимает довольно времени, даже при использовании такой продвинутой IDE как Android Studio.
  • Использование дополнительных интерфейсов усложняет навигацию по коду. Если вы хотите перейти к реализации метода Presenter’а из Activity (т. е. реализации View), то вы переходите к интерфейсу Presenter’а.
  • Интерфейс никак не улучшает тестируемость кода. Вы с легкостью можете заменить класс Presenter’а на mock, используя любую библиотеку для mock’ирования.

Более подробно можете почитать об этом в следующих статьях:

Как передать аргументы в Presenter, если его инстанс создает DI-контейнер?

Часто при создании презентера возникает необходимость передать дополнительные аргументы. Например, мы хотим передать идентфикатор статьи, чтобы получить её содержимое от сервера. Чтобы сделать это, нам необходимо создать отдельный модуль для Presenter’а и передать аргументы туда:

Далее нам нужно добавить наш модуль в Component:

Правильная архитектура Andro >

Подскажите пожалуйста, где научиться писать расширяемый, поддерживаемый, и надежный код под андроид. Мне кажется должен быть универсальный подход, который позволит эффективно писать 90% приложений, но почему-то не вижу его в документации гугла(у эпла то такие гайдлайны есть!). Чтобы функционал был разбит на независимые модули, а модули разделены по задачам (модель, отображение, уровень получения данных из кеша и т.п.), все данные надо бы immutable, но сложности с хранением состояния.

Ищу:
— готовые большые опенсорс приложения с поддерживаемым и безопасном кодом (очень не хватает, с Rx — было бы отлично)
— курсы, книги по архитектуре приложений — но конкретно применительно к андроиду. Ясно, что вариантов 100500, но мне нужны лишь те несколько, которые эфффективно решают 99% типичных андроид проблем.

Типичные вопросы, на которые не хватает ответов:
— в каком классе отправлять сетевые запросы для отображения списка? И передать потом данные в правильную активити.
— где кешировать ответ — средствами retrofit/valley, в памяти или в модели ручками?
— что будет контроллером если подход MVC, и должна ли на практике activity только пробрасывать запросы от вью в правильный кастомный контроллер? Где хранить состояние о том, какие фрагменты сейчас отображены?
— Где правильно хранить константы? Например тег, по которому в интенте данные лежат. Когда классов много, уже с этими константами возникает путаница.
— Если модель — не синглтон — то когда, где и как она должна удаляться из памяти?

Про Фернандо знаю, очень полезно, но мало, на многие вопросы ответов нет.

Подробный гайд по разработке Android-приложений с помощью Clean Architecture. Объявление возможностей компонентов. Как начать создание Чистых приложений

Пожалуй, никто не будет спорить с тем, что прежде чем начинать писать программы для любой системы, необходимо ознакомиться с ее основными характеристиками, структурой и идеологией. Но писать что-то хочется начать сразу, а разбираться со всем этим надо достаточно долго.

В этой и следующих статьях попробуем вкратце разобраться с тем из чего может состоять программа для Android.

Итак первый элемент приложений в андроиде это Activity и его производные. Если отбросить все нюансы, то можно сказать что это окно приложения со всеми элементами управления (кнопками, бегунками, списками). По сути Activity представляет собой пользовательский интерфейс.

Как это дело использовать? Просто создать свой класс-наследник Activity и переопределить несколько методов. Вообще что и ы какой момент происходит с Activity лучше всего понятно из этой диаграммы:

Скачал ее на developer.android.com.

Итак, что же может произойти с нашей activity во время ее жизни и как мы можем контролировать эти процессы? Во-первых наша activity должна создаться. Корректно обработать ее создание нам поможет переопределение метода onCreate() .

Следующим шагом после создания будет ее показ пользователю, и если мы хотим например, инициализировать текст в текстовых полях, или запустить фоновую музыку, то можно сделать это здесь. Для этого используется метод onStart() . После метода onStart() вызывается метод onResume() .

Если мы с вызовем какое-то диалоговое окно, и наша аctivity перейдет на второй план, мы легко можем обработать такую ситуацию, переопределив метод onPause() .

Ну а если мы (или пользователь) вызвали какую-то другую activity, и наша с вами перешла в фоновый режим, то мы переопределяем метод onStop() . Ну например если нам надо остановить фоновую музыку, или сбрость какие-то значения введенные пользователем. Если же пользователь опять решил вернуться к нашей activity то мы обрабатываем это событие с помошью метода onRestart() . При этом, мы не должны забывать, что если системе потребовалась память, то наша активити может быть уничтожена и это мы также можем обработать переопределив метод onDestroy() (К примеру, если мы открыли соединение с каким-то сервером и все время держали его открытым, то в этом методе его не плохо бы и закрыть ��).

Ну вот, краткий теоретический экскурс закончился, давайте попробуем написать небольшое приложение, которое будет обрабатывать все эти события.

Package ru.dav , Toast.LENGTH_SHORT).show(); > >

С помощью этого приложения вы сможете посмотреть в каком порядке вызываются все эти методы. Обратите внимание, что в каждом методе который мы переопределили, прежде всего мы запускаем такой-же родительский метод! И только псле этого начинаем писать свой код.

Android apps can be written using Kotlin, Java, and C++ languages. The Android SDK tools compile your code along with any data and resource files into an APK, an Android package , which is an archive file with an .apk suffix. One APK file contains all the contents of an Android app and is the file that Android-powered devices use to install the app.

Each Android app lives in its own security sandbox, protected by the following Android security features:

  • The Android operating system is a multi-user Linux system in which each app is a different user.
  • By default, the system assigns each app a unique Linux user ID (the ID is used only by the system and is unknown to the app). The system sets permissions for all the files in an app so that only the user ID assigned to that app can access them.
  • Each process has its own virtual machine (VM), so an app»s code runs in isolation from other apps.
  • By default, every app runs in its own Linux process. The Android system starts the process when any of the app»s components need to be executed, and then shuts down the process when it»s no longer needed or when the system must recover memory for other apps.

The Android system implements the principle of least privilege . That is, each app, by default, has access only to the components that it requires to do its work and no more. This creates a very secure environment in which an app cannot access parts of the system for which it is not given permission. However, there are ways for an app to share data with other apps and for an app to access system services:

  • It»s possible to arrange for two apps to share the same Linux user ID, in which case they are able to access each other»s files. To conserve system resources, apps with the same user ID can also arrange to run in the same Linux process and share the same VM. The apps must also be signed with the same certificate.
  • An app can request permission to access device data such as the user»s contacts, SMS messages, the mountable storage (SD card), camera, and Bluetooth. The user has to explicitly grant these permissions. For more information, see .

The rest of this document introduces the following concepts:

  • The core framework components that define your app.
  • The manifest file in which you declare the components and the required device features for your app.
  • Resources that are separate from the app code and that allow your app to gracefully optimize its behavior for a variety of device configurations.

App components

App components are the essential building blocks of an Android app. Each component is an entry point through which the system or a user can enter your app. Some components depend on others.

There are four different types of app components:

  • Activities
  • Services
  • Broadcast receivers
  • Content providers

Each type serves a distinct purpose and has a distinct lifecycle that defines how the component is created and destroyed. The following sections describe the four types of app components.

Activities An activity is the entry point for interacting with the user. It represents a single screen with a user interface. For example, an email app might have one activity that shows a list of new emails, another activity to compose an email, and another activity for reading emails. Although the activities work together to form a cohesive user experience in the email app, each one is independent of the others. As such, a different app can start any one of these activities if the email app allows it. For example, a camera app can start the activity in the email app that composes new mail to allow the user to share a picture. An activity facilitates the following key interactions between system and app:

  • Keeping track of what the user currently cares about (what is on screen) to ensure that the system keeps running the process that is hosting the activity.
  • Knowing that previously used processes contain things the user may return to (stopped activities), and thus more highly prioritize keeping those processes around.
  • Helping the app handle having its process killed so the user can return to activities with their previous state restored.
  • Providing a way for apps to implement user flows between each other, and for the system to coordinate these flows. (The most classic example here being share.)

Content providers A content provider manages a shared set of app data that you can store in the file system, in a SQLite database, on the web, or on any other persistent storage location that your app can access. Through the content provider, other apps can query or modify the data if the content provider allows it. For example, the Android system provides a content provider that manages the user»s contact information. As such, any app with the proper permissions can query the content provider, such as , to read and write information about a particular person. It is tempting to think of a content provider as an abstraction on a database, because there is a lot of API and support built in to them for that common case. However, they have a different core purpose from a system-design perspective. To the system, a content provider is an entry point into an app for publishing named data items, identified by a URI scheme. Thus an app can decide how it wants to map the data it contains to a URI namespace, handing out those URIs to other entities which can in turn use them to access the data. There are a few particular things this allows the system to do in managing an app:

  • Assigning a URI doesn»t require that the app remain running, so URIs can persist after their owning apps have exited. The system only needs to make sure that an owning app is still running when it has to retrieve the app»s data from the corresponding URI.
  • These URIs also provide an important fine-grained security model. For example, an app can place the URI for an image it has on the clipboard, but leave its content provider locked up so that other apps cannot freely access it. When a second app attempts to access that URI on the clipboard,the system can allow that app to access the data via a temporary URI permission grant so that it is allowed to access the data only behind that URI, but nothing else in the second app.

Content providers are also useful for reading and writing data that is private to your app and not shared.


A content provider is implemented as a subclass of and must implement a standard set of APIs that enable other apps to perform transactions. For more information, see the developer guide.

A unique aspect of the Android system design is that any app can start another app’s component. For example, if you want the user to capture a photo with the device camera, there»s probably another app that does that and your app can use it instead of developing an activity to capture a photo yourself. You don»t need to incorporate or even link to the code from the camera app. Instead, you can simply start the activity in the camera app that captures a photo. When complete, the photo is even returned to your app so you can use it. To the user, it seems as if the camera is actually a part of your app.

When the system starts a component, it starts the process for that app if it»s not already running and instantiates the classes needed for the component. For example, if your app starts the activity in the camera app that captures a photo, that activity runs in the process that belongs to the camera app, not in your app»s process. Therefore, unlike apps on most other systems, Android apps don»t have a single entry point (there»s no main() function).

Because the system runs each app in a separate process with file permissions that restrict access to other apps, your app cannot directly activate a component from another app. However, the Android system can. To activate a component in another app, deliver a message to the system that specifies your intent to start a particular component. The system then activates the component for you.

Activating components

Three of the four component types—activities, services, and broadcast receivers—are activated by an asynchronous message called an intent . Intents bind individual components to each other at runtime. You can think of them as the messengers that request an action from other components, whether the component belongs to your app or another.

The manifest file

Before the Android system can start an app component, the system must know that the component exists by reading the app»s manifest file , AndroidManifest.xml . Your app must declare all its components in this file, which must be at the root of the app project directory.

The manifest does a number of things in addition to declaring the app»s components, such as the following:

  • Identifies any user permissions the app requires, such as Internet access or read-access to the user»s contacts.
  • Declares the minimum required by the app, based on which APIs the app uses.
  • Declares hardware and software features used or required by the app, such as a camera, bluetooth services, or a multitouch screen.
  • Declares API libraries the app needs to be linked against (other than the Android framework APIs), such as the Google Maps library .

Declaring components

The primary task of the manifest is to inform the system about the app»s components. For example, a manifest file can declare an activity as follows:

For more about how to structure the manifest file for your app, see documentation.

Declaring component capabilities

Declaring app requirements

There are a variety of devices powered by Android and not all of them provide the same features and capabilities. To prevent your app from being installed on devices that lack features needed by your app, it»s important that you clearly define a profile for the types of devices your app supports by declaring device and software requirements in your manifest file. Most of these declarations are informational only and the system does not read them, but external services such as Google Play do read them in order to provide filtering for users when they search for apps from their device.

For example, if your app requires a camera and uses APIs introduced in Android 2.1 ( 7), you must declare these as requirements in your manifest file as shown in the following example:

With the declarations shown in the example, devices that do not have a camera or have an Android version lower than 2.1 cannot install your app from Google Play. However, you can declare that your app uses the camera, but does not require it. In that case, your app must set the attribute to false and check at runtime whether the device has a camera and disable any camera features as appropriate.

More information about how you can manage your app»s compatibility with different devices is provided in the document.

App resources

An Android app is composed of more than just code—it requires resources that are separate from the source code, such as images, audio files, and anything relating to the visual presentation of the app. For example, you can define animations, menus, styles, colors, and the layout of activity user interfaces with XML files. Using app resources makes it easy to update various characteristics of your app without modifying code. Providing sets of alternative resources enables you to optimize your app for a variety of device configurations, such as different languages and screen sizes.

For every resource that you include in your Android project, the SDK build tools define a unique integer ID, which you can use to reference the resource from your app code or from other resources defined in XML. For example, if your app contains an image file named logo.png (saved in the res/drawable/ directory), the SDK tools generate a resource ID named R.drawable.logo . This ID maps to an app-specific integer, which you can use to reference the image and insert it in your user interface.

One of the most important aspects of providing resources separate from your source code is the ability to provide alternative resources for different device configurations. For example, by defining UI strings in XML, you can translate the strings into other languages and save those strings in separate files. Then Android applies the appropriate language strings to your UI based on a language qualifier that you append to the resource directory»s name (such as res/values-fr/ for French string values) and the user»s language setting.

Android supports many different qualifiers for your alternative resources. The qualifier is a short string that you include in the name of your resource directories in order to define the device configuration for which those resources should be used. For example, you should create different layouts for your activities, depending on the device»s screen orientation and size. When the device screen is in portrait orientation (tall), you might want a layout with buttons to be vertical, but when the screen is in landscape orientation (wide), the buttons could be aligned horizontally. To change the layout depending on the orientation, you can define two different layouts and apply the appropriate qualifier to each layout»s directory name. Then, the system automatically applies the appropriate layout depending on the current device orientation.

How Android works on different types of devices and an introduction to how you can optimize your app for each device or restrict your app»s availability to different devices. How Android restricts app access to certain APIs with a permission system that requires the user»s consent for your app to use those APIs.

Изначально разрабатывалась компанией Android Inc., которую затем (июль, 2005) купила Google. Впоследствии (ноябрь, 2007) Google инициировала создание бизнес-альянса Open Handset Alliance (в его состав вошли Google, HTC, Intel, Motorola, Nvidia и другие компании), который и занимается сейчас поддержкой и дальнейшим развитием платформы.

С момента выхода первой версии (сентябрь, 2008) произошло несколько обновлений системы. Эти обновления, как правило, касаются исправления обнаруженных ошибок и добавления нового функционала в систему. Каждая версия системы получает собственное кодовое имя на тему десерта.

1.2. Инструментарий разработчика

Перед тем, как приступить к созданию Android -приложений, необходимо выбрать подходящий инструментарий разработки.

Как правило, разработка Android -приложений осуществляется на языке Java . Поэтому, в первую очередь , необходимо установить Java Development Kit ( JDK ). Но для начала следует разобраться, что представляет из себя Java .

Java – это объектно-ориентированный язык программирования . Программы на Java транслируются в байт-код, выполняемый виртуальной машиной Java , которая обрабатывает байтовый код и передает инструкции оборудованию как интерпретатор . Достоинство подобного способа выполнения программ заключается в полной независимости байт-кода от операционной системы и оборудования, что позволяет выполнять Java -приложения на любом устройстве, для которого существует соответствующая виртуальная машина . Другой важной особенностью Java является гибкая система безопасности благодаря тому, что исполнение программы полностью контролируется виртуальной машиной. Любые операции , которые превышают установленные полномочия программы (например, попытка несанкционированного доступа к данным или соединения с другим компьютером) вызывают немедленное прерывание . Следует заметить, что фактически, большинство архитектурных решений, принятых при создании Java , было продиктовано желанием предоставить синтаксис , схожий с С/C++. В Java используются практически идентичные соглашения для объявления переменных, передачи параметров и операторов. Поэтому те, кто уже имеет опыт программирования на C/C++, смогут быстро освоиться и начать писать Java -приложения.

JDK – это бесплатно распространяемый комплект разработчика приложений на языке Java , включающий в себя компилятор Java , стандартные библиотеки классов Java , примеры, документацию, различные утилиты и исполнительную систему Java Runtime Environment ( JRE ). В состав JDK не входит интегрированная среда разработки (Integrated Development Environment ). Поэтому после того, как будет установлен JDK , следует установить IDE .

Существует несколько популярных сред разработки, но в данном курсе мы остановим свой выбор на Eclipse IDE и соответствующем для нее плагине Android Development Tools ( ADT ).

Android Software Development Kit ( SDK ) содержит множество инструментов и утилит для создания и тестирования приложений. Например, с помощью SDK Manager можно установить Android API любой версии (Рис. 1.1), а также проверить репозиторий на наличие доступных, но еще не установленных пакетов и архивов.

Android Native Development Kit (NDK) позволяет осуществлять разработку Android -приложений на языке C/C++. Зачем это может потребоваться? Есть несколько причин. Например, необходимость использовать код, который уже написан для нативной платформы, или ускорение выполнения критических кусков кода.

1.3. Архитектура Android

Рассмотрим основные компоненты операционной системы Android (Рис. 1.2).

Applications. Android поставляется с набором основных приложений, включающим календарь, карты, браузер , менеджер контактов и другие. Все перечисленные приложения написаны на Java .

Application Framework. Предоставляя открытую платформу разработки, Android дает разработчикам возможность создавать гибкие и инновационные приложения. Разработчики могут использовать аппаратные возможности устройства, получать информацию о местоположении, выполнять задачи в фоновом режиме, устанавливать оповещения и многое другое. Разработчики имеют полный доступ к тем же API , что используются в основных приложениях.

Архитектура приложений разработана с целью упрощения повторного использования компонентов; любое приложение может «публиковать» свои возможности и любое другое приложение может затем использовать эти возможности (с учетом ограничений безопасности). Этот же механизм позволяет заменять стандартные компоненты на пользовательские.

Libraries. Android включает в себя набор C/C++ библиотек, используемых различными компонентами системы. Эти возможности доступны разработчикам в контексте применения Android Aplication Framework. Некоторые основные библиотеки, перечислены ниже:

  • Mедиа библиотеки – эти библиотеки предоставляют поддержку воспроизведения и записи многих популярных аудио, видео форматов и форматов изображений, в том числе MPEG4, MP3, AAC, AMR, JPG, PNG и других;
  • Surface Manager – управляет доступом к подсистеме отображения 2D и 3D графических слоев;
  • LibWebCore – современный веб-движок, на котором построен браузер Android;
  • SGL – основной графический движок 2D;
  • 3D библиотеки – реализованы на основе OpenGL; библиотеки используют либо аппаратное 3D-ускорение (при его наличии), либо включены программно;
  • FreeType – поддержка растровых и векторных шрифтов
  • SQLite – механизм базы данных, доступной для всех приложений.

Android Runtime. Android включает в себя набор основных библиотек, которые обеспечивают большинство функций, доступных в библиотеках Java . Каждое приложение Android работает в своем собственном процессе, со своим собственным экземпляром виртуальной машины Dalvik. Dalvik была написана так, что устройство может работать эффективно с несколькими виртуальными машинами одновременно.

Dalvik проектировалась специально под платформу Android . Виртуальная машина оптимизирована для низкого потребления памяти и работы на мобильном аппаратном обеспечении. Dalvik использует собственный байт-код. Android -приложения переводятся компилятором в специальный машинно-независимый низкоуровневый код. И именно Dalvik интерпретирует и выполняет такую программу при выполнении на платформе. Кроме того, с помощью специальной утилиты, входящей в состав Android SDK , Dalvik способна переводить байт-коды Java в коды собственного формата и также исполнять их в своей виртуальной среде.

Linux Kernel. Android основан на Linux версии 2.6 с основными системными службами – безопасность , управление памятью , управление процессами и модель драйверов. Разработчики Android модифицировали ядро Linux, добавив поддержку аппаратного обеспечения, используемого в мобильных устройствах и, чаще всего, недоступного на компьютерах.

1.4. Обзор Java-интерфейсов

Для прикладного программиста Android – это набор интерфейсов на языке Java . Рассмотрим, как он организован. В основе набора – пакеты, входящие в

Приложения для Android пишутся на языке программирования Java. Инструменты Android SDK (Software Development Kit – комплект разработки программного обеспечения) компилируют написанный вами код — и все требуемые файлы данных и ресурсов — в файл APK – программный пакет Android , который представляет собой файл архива с расширением.apk . В файле APK находится все, что требуется для работы Android-приложения, и он позволяет установить приложение на любом устройстве под управлением системы Android.

Каждое приложение Android, установленное на устройстве, работает в собственной «песочнице» (изолированной программной среде):

  • операционная система Android представляет собой многопользовательскую систему Linux, в которой каждое приложение является отдельным пользователем;
  • по умолчанию система назначает каждому приложению уникальный идентификатор пользователя Linux (этот идентификатор используется только системой и неизвестен приложению); система устанавливает полномочия для всех файлов в приложении, с тем чтобы доступ к ним был разрешен только пользователю с идентификатором, назначенным этому приложению;
  • у каждого процесса имеется собственная виртуальная машина (ВМ), так что код приложения выполняется изолированно от других приложений;
  • по умолчанию каждое приложение выполняется в собственном процессе Linux. Android запускает процесс, когда требуется выполнить какой-либо компонент приложения, а затем завершает процесс, когда он больше не нужен либо когда системе требуется освободить память для других приложений.

Таким образом система Android реализует принцип предоставления минимальных прав . То есть каждое приложение по умолчанию имеет доступ только к тем компонентам, которые ему необходимы для работы, и ни к каким другим. Благодаря этому формируется исключительно безопасная среда, в которой приложение не имеет доступа к недозволенным областям системы.

Однако у приложения есть варианты предоставления своих данных другим приложениям и доступа к системным службам:

  • двум приложениям можно назначить один идентификатор пользователя Linux. В этом случае каждый из них сможет обращаться к файлам другого приложения. Для экономии ресурсов системы также можно сделать так, чтобы приложения с одинаковым идентификатором пользователя выполнялись в одном процессе Linux и использовали одну ВМ (приложения также должны быть подписаны одним сертификатом);
  • приложение может запросить разрешение на доступ к данным устройства, например к контактам пользователя, SMS-сообщениям, подключаемой карте памяти (SD-карте), камере, Bluetooth и др. Все разрешения должны предоставляться приложению при его установке.

Это основные сведения о том, каким образом приложение Android существует в системе. В остальной части этого документа раскрываются следующие темы:

  • базовые компоненты, которые определяют приложение;
  • файл манифеста, в котором объявляются компоненты и функции устройства, необходимые для приложения;
  • ресурсы, которые существуют отдельно от кода приложения и позволяют приложению адаптировать свою работу к устройствам с различными конфигурациями.

Компоненты приложения

Компоненты приложения являются кирпичиками, из которых состоит приложение для Android. Каждый компонент представляет собой отдельную точку, через которую система может войти в приложение. Не все компоненты являются точками входа для пользователя, а некоторые из них зависят друг от друга. При этом каждый компонент является самостоятельной структурной единицей и играет определенную роль — каждый из них представляет собой уникальный элемент структуры, который определяет работу приложения в целом.

Компоненты приложения можно отнести к одному из четырех типов. Компоненты каждого типа предназначены для определенной цели, они имеют собственный жизненный цикл, который определяет способ создания и прекращения существования компонента.

Четыре типа компонентов:

Операции Операция (Activity) представляет собой один экран с пользовательским интерфейсом. Например, в приложении для работы с электронной почтой одна операция может служить для отображения списка новых сообщений, другая – для составления сообщения и третья операция – для чтения сообщений. Несмотря на то что операции совместно формируют связное взаимодействие пользователя с приложением по работе с электронной почтой, каждая из них не зависит от других операций. Любые из этих операций могут быть запущены другим приложением (если это позволяет приложение по работе с электронной почтой). Например, приложение для камеры может запустить операцию в приложении по работе с электронной почтой, которая составляет новое сообщение, чтобы пользователь мог отослать фотографию.

Операция относится к подклассу класса . Подробные сведения об этом можно найти в руководстве для разработчиков в статье .

Службы Служба (Service) представляет собой компонент, который работает в фоновом режиме и выполняет длительные операции, связанные с работой удаленных процессов. Служба не имеет пользовательского интерфейса. Например, она может воспроизводить музыку в фоновом режиме, пока пользователь работает в другом приложении, или же она может получать данные по сети, не блокируя взаимодействие пользователя с операцией. Служба может быть запущена другим компонентом, который затем будут взаимодействовать с ней, – например операцией.

Служба относится к подклассу класса . Подробные сведения об этом можно найти в руководстве для разработчиков в статье .

Поставщики контента Поставщик контента (Content provider) управляет общим набором данных приложения. Данные можно хранить в файловой системе, базе данных SQLite, в Интернете или любом другом постоянном месте хранения, к которому у вашего приложения имеется доступ. Посредством поставщика контента другие приложения могут запрашивать или даже изменять данные (если поставщик контента позволяет делать это). Например, в системе Android есть поставщик контента, который управляет информацией контактов пользователя. Любое приложение, получившее соответствующие разрешения, может запросить часть этого поставщика контента (например ), для чтения и записи сведений об определенном человеке.

Поставщики контента также используются для чтения и записи данных, доступ к которым внешним компонентам приложение не предоставляет. Например, в образце приложения с помощью поставщика контента выполняется сохранение заметок.

Поставщик контента относится к подклассу класса . Он должен реализовывать стандартный набор API-интерфейсов, с помощью которых другие приложения будут выполнять транзакции. Подробные сведения можно найти в руководстве для разработчиков в статье .

Приемники широковещательных сообщений Приемник широковещательных сообщений (Broadcast receiver) представляет собой компонент, который реагирует на объявления распространяемые по всей системе. Многие из этих объявлений рассылает система — например объявление о том, что экран выключился, аккумулятор разряжен или был сделан фотоснимок. Объявления также могут рассылаться приложениями, — например, чтобы сообщить другим приложениям о том, что какие-то данные были загружены на устройство и теперь готовы для использования. Несмотря на то что приемники широковещательных сообщений не имеют пользовательского интерфейса, они могут чтобы предупредить пользователя о событии «рассылка объявления». Однако чаще всего они являются просто «шлюзом» для других компонентов и предназначены для выполнения минимального объема работы. Например, они могут инициировать выполнение службой определенных действий при возникновении события.

Приемник широковещательных сообщений относится к подклассу класса , а каждое такое сообщение предоставляется как объект . Подробные сведения изложены в руководстве, посвященном классу .

Уникальной особенностью системы Android является то, что любое приложение может запустить компонент другого приложения. Например, если вы хотите дать пользователю возможность фотографировать, используя камеру устройства, то, поскольку наверняка имеется другое приложение, которое может выполнить это действие, вместо того чтобы разработать операцию фотографирования в своем приложении, вы можете вызвать такое приложение. Вам не нужно внедрять код из приложения для камеры или даже устанавливать на него ссылку. Вместо этого вы можете просто запустить операцию фотографирования из приложения для камеры. По завершении этой операции фотография будет возвращена в ваше приложение, и ее можно будет использовать. Для пользователя это будет выглядеть как одно приложение.

Когда система запускает компонент, она запускает процесс для этого приложения (если он еще не был запущен) и создает экземпляры классов, которые требуются этому компоненту. Например, если ваше приложение запустит операцию фотографирования в приложении для камеры, эта операция будет выполняться в процессе, который относится к этому стороннему приложению, а не в процессе вашего приложения. Поэтому, в отличие от приложений для большинства других систем, в приложениях для Android отсутствует единая точка входа (например, в них нет функции main()).

Поскольку система выполняет каждое приложение в отдельном процессе с такими правами доступа к файлам, которые ограничивают доступ в другие приложения, ваше приложение не может напрямую вызвать компонент из другого приложения. Это может сделать сама система Android. Поэтому, чтобы вызвать компонент в другом приложении, необходимо сообщить системе о своем намерении (Intent ) запустить определенный компонент. После этого система активирует для вас этот компонент.

Активация компонентов

Компоненты трех из четырех возможных типов — операции, службы и приемники широковещательных сообщений — активируются асинхронным сообщением, которое называется Intent (намерение). Объекты Intent связывают друг с другом отдельные компоненты во время выполнения, будь то это компоненты вашего или стороннего приложения (эти объекты Intent можно представить себе в виде мессенджеров, которые посылают другим компонентам запрос на выполнение действий).

Объект Intent создается с помощью объекта , который описывает запрос на активацию либо конкретного компонента, либо компонента конкретного типа — соответственно, намерение Intent может быть явным или неявным.

Для операций и служб Объект Intent определяет действие, которое требуется выполнить (например, просмотреть (view) или отправить (send) что-то), а также может указывать URI (Uniform Resource Identifier – унифицированный идентификатор ресурса) данных, с которыми это действие нужно выполнить (помимо прочих сведений, которые нужно знать запускаемому компоненту). Например, объект Intent может передавать запрос на выполнение операции «показать изображение» или «открыть веб-страницу». В некоторых ситуациях операцию можно запустить, чтобы получить результат. В этом случае операция возвращает результат также в виде объекта (например, можно отправить сообщение Intent, чтобы дать пользователю возможность выбрать контакт и вернуть его вам — в ответном сообщении Intent будет содержаться URI, указывающий на выбранный контакт).

Для приемников широковещательных сообщений Intent просто определяет передаваемое объявление (например, широковещательное сообщение о низком уровне заряда аккумулятора содержит только строку «аккумулятор разряжен»).

Компоненты четвертого типа – поставщики контента – сообщениями Intent не активируются. Они активируются по запросу от . Процедура определения контента (content resolver) обрабатывает все прямые транзакции с поставщиком контента, с тем чтобы этого не пришлось делать компоненту, который выполняет транзакции с поставщиком. Вместо этого он вызывает методы для объекта . Это формирует слой, абстрагирующий (в целях безопасности) поставщика контента от компонента, запрашивающего информацию.

Для активации компонентов каждого типа имеются отдельные методы:

  • Можно запустить операцию (или определить для нее какое-то новое действие), передав объект методу или (если требуется, чтобы операция вернула результат).
  • Можно запустить службу (либо выдать работающей службе новые инструкции), передав объект методу . Либо можно установить привязку к службе, передав объект методу .
  • Можно инициировать рассылку сообщений, передав объект таким методам, как , и .
  • Можно выполнить запрос к поставщику контента, вызвав метод для объекта .

Подробные сведения об использовании объектов Intent приведены в документе . Более подробная информация об активации определенных компонентов также приведена в следующих документах: , и .

Файл манифеста

Для запуска компонента приложения системе Android необходимо знать, что компонент существует. Для этого она читает файл AndroidManifest.xml приложения (файл манифеста). В этом файле, который должен находиться в корневой папке приложения, должны быть объявлены все компоненты приложения.

Помимо объявления компонентов приложения, манифест служит и для других целей, среди которых:

  • указание всех полномочий пользователя, которые требуются приложению, например разрешения на доступ в Интернет или на чтение контактов пользователя;
  • объявление минимального , требуемого приложению, с учетом того, какие API-интерфейсы оно использует;
  • объявление аппаратных и программных функций, которые нужны приложению или используются им, например камеры, службы Bluetooth или сенсорного экрана;
  • указание библиотек API, с которыми необходимо связать приложение (отличные от API-интерфейсов платформы Android), например библиотеки Google Maps ;
  • и многое другое.

Объявление компонентов

Основная задача манифеста – это информировать систему о компонентах приложения. Например, файл манифеста может объявлять операцию следующим образом:

Подробные сведения о структуризации файла манифеста для приложения см. в документе .

Объявление возможностей компонентов

Как уже говорилось в разделе , с помощью объекта можно запускать операции, службы и приемники широковещательных сообщений. Для этого в объекте Intent следует явно указать имя целевого компонента (с помощью имени класса компонента). Однако в полной мере возможности объектов Intent раскрываются при использовании концепции неявных Intent . В неявном сообщении Intent просто описывается тип действия, которое требуется выполнить (а также, хотя это и не обязательно, данные, в отношении которых вы бы хотели выполнить это действие). Системе же предоставляется возможности найти на устройстве компонент, который может выполнить это действие, и запустить его. При наличии нескольких компонентов, которые могут выполнить действие, описанное в сообщении Intent, пользователь выбирает, какой из них будет использоваться.

Система определяет компоненты, которые могут ответить на сообщение Intent, путем сравнения полученного сообщения Intent с фильтрами объектов Intent, указанными в файле манифеста других приложений, имеющихся на устройстве.

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

Например, если вы создали приложение для работы с электронной почтой с операцией составления нового сообщения, вы можете объявить фильтр для ответа на сообщения Intent типа «send» (для отправки нового сообщения электронной почты) следующим образом:

Затем, если другое приложение создаст объект Intent с действием и передаст его в , система сможет запустить вашу операцию, дав пользователю возможность написать и отправить сообщение электронной почты.

Подробные сведения о создании фильтров объектов Intent приведены в документе .

Объявление требований приложения

Существует огромное количество устройств, работающих под управлением Android, и не все они имеют одинаковые функциональные возможности. Чтобы ваше приложение не могло быть установлено на устройствах, в которых отсутствуют функции, необходимые приложению, важно четко определить профиль для типов устройств, поддерживаемых вашим приложением, указав требования к аппаратному и программному обеспечению в файле манифеста. Эти объявления по большей части носят информационный характер, система их не читает. Однако их читают внешние службы, например Google Play, с целью обеспечения фильтрации для пользователей, которые ищут приложения для своих устройств.

Например, если вашему приложению требуется камера и оно использует API-интерфейсы из Android 2.1 ( 7), эти параметры следует объявить в файле манифеста в качестве требований следующим образом:

Теперь ваше приложение нельзя будет установить из Google Play на устройствах, в которых нет камеры, а также на устройствах, работающих под управлением Android версии ниже 2.1.

Однако можно также объявить, что приложение использует камеру, но для его работы она не является непременно необходимой . В этом случае в приложении атрибуту необходимо задать значение «false» , а во время работы оно должно проверять, имеется ли на устройстве камера, и при необходимости отключать свои функции, которые используют камеру.

Более подробные сведения об управлении совместимостью своего приложения с различными устройствами приведены в документе .

Ресурсы приложения

Приложение Android состоит не только из кода — ему необходимы такие существующие отдельно от исходного кода ресурсы, как изображения, аудиофайлы и все, что связано с визуальным представлением приложения. Например, необходимо определять анимацию, меню, стили, цвета и макет пользовательских интерфейсов операций в файлах XML. Используя ресурсы приложения, можно без труда изменять его различные характеристики, не меняя код, а, кроме того, —путем предоставления наборов альтернативных ресурсов — можно оптимизировать свое приложение для работы с различными конфигурациями устройств (например, для различных языков или размеров экрана).

Для каждого ресурса, включаемого в проект Android, инструменты SDK задают уникальный целочисленный идентификатор, который может использоваться, чтобы сослаться на ресурс из кода приложения или из других ресурсов, определенных в XML. Например, если в вашем приложении имеется файл изображения с именем logo.png (сохраненный в папке res/drawable/), инструменты SDK сформируют идентификатор ресурса под именем R.drawable.logo , с помощью которого на изображение можно будет ссылаться и вставлять его в пользовательский интерфейс.

Один из наиболее важных аспектов предоставления ресурсов отдельно от исходного кода заключается в возможности использовать альтернативные ресурсы для различных конфигураций устройств. Например, определив строки пользовательского интерфейса в XML, вы сможете перевести их на другие языки и сохранить эти переводы в отдельных файлах. Затем по квалификатору языка, добавленному к имени каталога ресурса (скажем res/values-fr/ для строк на французском языке), и выбранному пользователем языку система Android применит к вашему пользовательскому интерфейсу строки на соответствующем языке.

Android поддерживает разные квалификаторы для соответствующих ресурсов. Квалификатор представляет собой короткую строку, которая включается в имена каталогов ресурсов с целью определения конфигурации устройства, для которой эти ресурсы следует использовать. В качестве другого примера можно сказать, что для своих операций следует создавать разные макеты, которые будут соответствовать размеру и ориентации экрана устройства. Например, когда экран устройства имеет книжную ориентацию (расположен вертикально), кнопки в макете можно также размещать по вертикали, а когда экран развернут горизонтально (альбомная ориентация), кнопки следует размещать по горизонтали. Чтобы при изменении ориентации экрана изменялся макет, можно определить два разных макета и применить соответствующий квалификатор к имени каталога каждого макета. После этого система будет автоматически применять соответствующий макет в зависимости от ориентации устройства.

Предоставление ресурсов Описание структуры приложений Android, в которой ресурсы приложения существуют отдельно от его кода, а также сведения о том, как предоставлять альтернативные ресурсы для определенных конфигураций устройств.

Возможно, вас также заинтересует:

Есть четыре стандартных блока приложения Android:

Не у каждого приложения должны быть все четыре блока, но Ваше приложение будет написано с их некоторой комбинацией.

Как только Вы решили, в каких компонентах Вы нуждаетесь для своего приложения, Вы должны перечислить их в файле по имени AndroidManifest.xml. Это — файл XML, где Вы объявляете компоненты своего приложения и каковы их возможности и требования. Мы скоро обсудим, за что AndroidManifest.xml ответственен.

(Это могло быть написано ОЧЕНЬ криво. Тут много текста и никаких картинок примеров. Рекомендую потерпеть и прочесть эту теорию, зато потом Вам будет понятней. Потом все написано гораздо глаже, не волнуйтесь)

Activity

Activity – самый распространенный из четырех стандартных блоков Андроид. Activity обычно — единственный экран в Вашем приложении. Каждый Activity осуществлен как единственный класс, который расширяет базовый класс Activity. Ваш класс отобразит пользовательский интерфейс, составленный из Views, и ответит на события. Большинство приложений состоит из множественных экранов. Например, у приложения обмена сообщениями мог бы быть один экран, который показывает список контактов, второй экран, чтобы написать сообщение выбранному контакту, и другие экраны, чтобы делать обзор старых сообщений или изменить настройку. Каждый из этих экранов был бы осуществлен как Activity. Перемещение в другой экран достигнуто стартом нового Activity. В некоторых случаях Activity может возвратить значение предыдущего Activity — например Activity, которая позволяет пользователю выбирать фотографию, возвратил бы выбранную фотографию вызывающей программе. Когда новый экран открывается, предыдущий экран приостановлен и помещен на стек хронологии. Пользователь может переместиться назад через ранее открытые экраны в хронологии. Экраны могут также хотеть быть удаленными от стека хронологии, когда было бы неуместно для них остаться. Андроид сохраняет стеки хронологии для каждого приложения, начатого от начала экрана.

Intent и фильтры Intent

Андроид использует специальный класс под названием Intent, чтобы двигаться от экрана к экрану. Intent описывает то, что приложение собирается сделать. Две самых важных части структуры Intent — действие и данные к действию. Типичные значения для действия – MAIN (главный экран приложения), VIEW, PICK, EDIT, и т.д. Данные выражены как Uniform Resource Indicator (URI). Например, чтобы рассмотреть веб сайт в браузере, Вы создали бы Intent с действием VIEW и набором данных – адресом сайта.

new Intent(android.content.Intent.VIEW_ACTION , ContentURI.create («https://anddev.org»));

Есть связанный класс, названный IntentFilter. В то время как Intent — запрос сделать кое-что, IntentFilter — описание того, что Intent Activity (или intent receiver, см. ниже), способен к обработке. Activity, который в состоянии отобразить информацию для человека, издала бы IntentFilter, который сказал, что знает, как обработать VIEW действия. Activity издает свой IntentFilters в файле AndroidManifest.xml.

Навигация от экрана к экрану достигнута достигается с помощью Intent. Чтобы переместиться вперед, Activity вызывает startActivity (myIntent). Система тогда смотрит на IntentFilter для всех установленных приложений и выбирает Activity, Intent которого фильтрует myIntent. Новому Activity сообщают о Intent, которое заставляет его начаться. Процесс решения Intent происходит, когда startActivity вызывают. Процесс предлагает две ключевых льготы:

Действия могут многократно использовать функциональные возможности от других компонентов, просто делая запрос в форме Intent.

Действия могут быть заменены в любое время новым Activity с эквивалентным IntentFilter.

Intent Receiver

Вы можете использовать IntentReceiver, когда Вы хотите, чтобы код в своем приложении выполнился в реакции на внешнее событие, например, когда телефон звонит, или когда сеть передачи данных доступна, или когда это — полночь. Intent Receiver не отображают UI, хотя они могут отобразить Уведомления, чтобы привести пользователя в готовность, если кое-что интересное случилось. Поглощенные получатели также регистрированы в AndroidManifest.xml, но Вы можете также регистрировать их в коде, используя Context.registerReceiver(). Ваше приложение не должно работать для его Intent Receiver, которые вызываются; система запустит Ваше приложение, в случае необходимости, когда Intent Receiver будет вызван. Приложения могут также послать свои собственные Intent Receiver другим с Context.broadcastIntent().

Service

Service — код, который долговечен и выполняется без UI. Хороший пример этого — универсальный проигрыватель, запускающий песни из плейлиста. В приложении универсального проигрывателя, вероятно, были бы одно или более Activity, которые позволяют пользователю выбирать песни и запускать их. Однако, воспроизведение самой музыки не должно быть обработано Activity, потому что пользователь будет ожидать, что музыка продолжит играть даже после сворачивания проигрывателя. В этом случае, деятельность универсального проигрывателя могла запустить Service, используя Context.startService(), чтобы работать на заднем плане и сохранить воспроизведение музыки. Тогда система сохранит воспроизведение музыки, пока оно не закроется само. (Вы можете узнать больше о приоритете, данном службам в системе, читая Цикл Жизни Приложения Андроид). Отметьте, что Вы можете соединиться с Service (и запустить его, если он уже не работает) с методом Context.bindService(). Когда есть подключение с Service, Вы можете общаться с этим через интерфейс, выставленный Service. Для Service музыки это могло бы позволить Вам приостанавливать, перематывать, и т.д.

Content Provider

Приложения могут хранить свои данные в файлах, базе данных SQLite, персональных настройках или любом другом механизме, который имеет смысл. Content Provider, однако, полезен, если Вы хотите, чтобы данные Вашего приложения были разделены с другими приложениями. Content Provider — класс, который осуществляет стандартный набор методов, чтобы позволить другим приложениям сохранять и восстанавливать тип данных, которые обработаны другим(that) Content Provider.

Пользовательские интерфейсы Андроид

Пользовательские интерфейсы (UI) в Андроид могут быть созданы двумя путями, через XML-код или в java-коде. Создание структуры графического интерфейса пользователя в XML очень предпочтительно, потому что по принципу Образцового управления средства просмотра, UI должен всегда отделяться от логики программы. К тому же, приспосабливание программы от одной разрешающей способности экрана до другой намного более просто. Определение UI в XML очень похоже к созданию общего документа HTML, где Вы имеете то есть такой простой файл:

The content of the body element.

Все равно как в Андроидовских XML-Layouts. Все хорошо структурировано и может быть выражено древовидными структурами:

Иерархия Элементов Экрана

Основной функциональный модуль приложения Android — Activity — объект класса android.app.activity. Activity может сделать много вещей, но отдельно у него нет присутствия на экране. Чтобы дать Вашему Activity присутствие на экрана и проектировать его UI, Вы работаете с Views и Viewgroups — основными единицами выражения пользовательского интерфейса на платформе Андроид.

Views

View — объект, расширяющий базовый класс android.view.view . Это — структура данных, свойства которой сохраняют Layouts и информационное наполнение для определенной прямоугольной области экрана. Объект View обрабатывает измерение, его схему размещения, рисунок, изменения центра, прокрутку, и клавиши/знаки для области экрана, которую он представляет. Класс View служит базовым классом для всех графических фрагментов — ряд полностью осуществленных подклассов, которые рисуют интерактивные элементы экрана. Графические фрагменты обрабатывают свое собственное измерение и рисунок, таким образом Вы можете использовать их, чтобы создать Ваш UI более быстро. Список доступных графических фрагментов включает TextView, EditText, Button, RadioButton, Checkbox, ScrollView и т.д.

Viewgroups

Viewgroup — объект класса android.view.viewgroup. Viewgroup — специальный тип объекта View, функция которого — содержать набором View и Viewgroup и управлять ими. Viewgroups позволяют Вам добавлять структуру к Вашему UI и создавать сложные элементы экрана, к которым можно обратиться как к единственному объекту. Класс Viewgroup служит базовым классом для Layouts — ряда полностью осуществленных подклассов, обеспечивающего общие типы Layouts экрана. Layouts дают Вам способ встроить структуру для ряда View.

UI с древовидной структурой

На платформе Андроид Вы определяете UI Activity использование дерева View и Viewgroup узлов, как показано в диаграмме ниже. Дерево может быть столь же простым или сложным, как Вы его сделаете, и Вы можете построить его, используя наборы предопределенных графических фрагментов и Layouts Андроида, или заказных типов View, которые Вы создаете самостоятельно.

UI Андроид – древовидная структура.

Чтобы прикрепить дерево к экрану и отрендрить его, Ваш Activity вызывает свой метод setContentView() и передает информацию на корневой объект узла. Как только у система Андроид получает информацию на корневой объект узла, она начинает работать непосредственно с узлом, чтобы измерить, и отрисовать дерево. Когда Ваш Activity становится активным и получает приоритет, система регистрирует Ваш Activity и просит корневой узел измерить и отрисовать дерево. Тогда корневой узел просит, чтобы его дочерние вершины отрисовали себя — в свою очередь, каждый Viewgroup узел в дереве ответственен за отрисовку его прямых дочерних узлов. Как упомянуто ранее, у каждой группы View есть ответственность измерения ее доступного пространства, расположения ее дочерних узлов, и вызов draw() на каждом дочернем узле, чтобы позволить все им рендрить себя. Дочерние узлы могут просить размер и местоположение в родителе, но у родительского объекта есть конечное решение, где и насколько большой каждый ребенок может быть.

Сравнение Андроида Элементы UI к Swing Элементы UI

Поскольку некоторые разработчики, которые читают это, возможно, нашли, что UIs схож с Swing, сейчас будет немного общих черт между Андроидом и Swing.

Activity в Андроид — почти (J) Frame в Swing.

View в Андроид — (J) Component в Swing.

TextViews в Андроид — (J) TextField в Swing.

EditTexts в Андроид — (J) TextField в Swing.

Button в Андроид — (J) Button в Swing.

Установка слушателей к View в Андроид является почти тем же самым, чем и в Swing.

ПРОграммирование под Android

Страницы

21 февраля 2014 г.

Архитектура операционной системы Андроид

Классический рисунок представляющий архитектуру ОС Android:

Если кому-то сложно с английским, то на всякий случай то же самое по на русском:

Сразу приведу оригинальное видео с канала Android Developers на Youtube, где все авторитетно рассказывается и показывается, правда на враждебном нам буржуйском языке. Я использовал это видео, чтобы описать некоторые пункты архитектуры, описания которых не нашел в сети на русском языке.

Если представить компонентную модель Android в виде некоторой иерархии, то в самом низу, как самая фундаментальная и базовая составляющая, будет располагаться ядро операционной системы (Linux Kernel).
Часто компонентную модель ещё называют программным стеком. Действительно, это определение тут уместно, потому что речь идет о наборе программных продуктов, которые работают вместе для получения итогового результата. Действия в этой модели выполняются последовательно, и уровни иерархии также последовательно взаимодействуют между собой.

LINUX KERNEL (ЯДРО ЛИНУКС)
Как известно, Андроид основан на несколько урезанном ядре ОС Linux и поэтому на этом уровне мы можем видеть именно его (версии 2.6.x). Оно обеспечивает функционирование системы и отвечает за безопасность, управление памятью, энергосистемой и процессами, а также предоставляет сетевой стек и модель драйверов. Ядро также действует как уровень абстракции между аппаратным обеспечением и программным стеком.

LIBRARIES (БИБЛИОТЕКИ)
«Выше» ядра, как программное обеспечение промежуточного слоя, лежит набор библиотек (Libraries), предназначенный для обеспечения важнейшего базового функционала для приложений. То есть именно этот уровень отвечает за предоставление реализованных алгоритмов для вышележащих уровней, поддержку файловых форматов, осуществление кодирования и декодирования информации (в пример можно привести мультимедийные кодеки), отрисовку графики и многое другое. Библиотеки реализованы на C/C++ и скомпилированы под конкретное аппаратное обеспечение устройства, вместе с которым они и поставляются производителем в предустановленном виде.
Краткое описание некоторых из них:

    Surface Manager – в ОС Andro > 3D библиотеки — используются для высокооптимизированной отрисовки 3D-графики, при возможности используют аппаратное ускорение. Их реализации строятся на основе API OpenGL ES 1.0.

На этом же уровне располагается Android Runtime – среда выполнения. Ключевыми её составляющими являются набор библиотек ядра и виртуальная машина Dalvik. Библиотеки обеспечивают большую часть низкоуровневой функциональности, доступной библиотекам ядра языка Java.

ANDROID RUNTIME (СРЕДА ВЫПОЛНЕНИЯ АНДРОИД)
На этом же уровне располагается Android Runtime – среда выполнения. Ключевыми её составляющими являются набор библиотек ядра (Core Libraries) и виртуальная машина Dalvik. Библиотеки обеспечивают большую часть низкоуровневой функциональности, доступной библиотекам ядра языка Java.
Каждое приложение в ОС Android запускается в собственном экземпляре виртуальной машины Dalvik. Таким образом, все работающие процессы изолированы от операционной системы и друг от друга. И вообще, архитектура Android Runtime такова, что работа программ осуществляется строго в рамках окружения виртуальной машины. Благодаря этому осуществляется защита ядра операционной системы от возможного вреда со стороны других её составляющих. Поэтому код с ошибками или вредоносное ПО не смогут испортить Android и устройство на его базе, когда сработают. Такая защитная функция, наряду с выполнением программного кода, является одной из ключевых для надстройки Android Runtime.
Dalvik полагается на ядро Linux для выполнения основных системных низкоуровневых функций, таких как, безопасность, потоки, управление процессами и памятью. Вы можете также писать приложения на C/C++, которые будут работать непосредственно на базовом уровне ОС Linux. Хотя такая возможность и существует, необходимости в этом нет никакой.
Если для приложения важны присущие C/C++ скорость и эффективность работы, Android предоставляет доступ к нативной среде разработки (NDK – Native Development Kit). Она позволяет разрабатывать приложения на C/C++ с использованием библиотек libc и libm, а также обеспечивает нативный доступ к OpenGL.
Доступ к устройствам и системным службам Android осуществляется через виртуальную машину Dalvik, которая считается промежуточным слоем. Благодаря использованию Dalvik для выполнения кода программы разработчики получают в свое распоряжение уровень абстракции, который позволяет им не беспокоиться об особенностях конструкции того или иного устройства.
Виртуальная машина Dalvik может выполнять программы в исполняемом формате DEX (Dalvik Executable). Данный формат оптимизирован для использования минимального объема памяти. Исполняемый файл с расширением .dex создается путем компиляции классов Java с помощью инструмента dx, входящего в состав Android SDK. При использовании IDE Eclipse и плагина ADT (Android Development Tools) компиляция классов Java в формат .dex происходит автоматически.
Как было сказано выше, инструмент dx из Android SDK компилирует приложения, написанные на Java, в исполняемый формат (dex) виртуальной машины Dalvik. Помимо непосредственно исполняемых файлов, в состав приложения Android входят прочие вспомогательные компоненты (такие, например, как файлы с данными и файлы ресурсов). SDK упаковывает все необходимое для установки приложения в файл с расширением .apk (Android package). Весь код в одном файле .apk считается одним приложением и этот файлиспользуется для установки данного приложения на устройствах с ОС Android.

APPLICATION FRAMEWORK (КАРКАС ПРИЛОЖЕНИЙ)
Уровнем выше располагается Application Framework, иногда называемый уровнем каркаса приложений. Именно через каркасы приложений разработчики получают доступ к API, предоставляемым компонентами системы, лежащими ниже уровнем. Кроме того, благодаря архитектуре фреймворка, любому приложению предоставляются уже реализованные возможности других приложений, к которым разрешено получать доступ.
В базовый набор сервисов и систем, лежащих в основе каждого приложения и являющихся частями фреймворка, входят:

  • Activity Manager – менеджер Активностей, который управляет жизненными циклами приложений, сохраняет данные об истории работы с Активностями, а также предоставляет систему навигации по ним.
  • Package Manager – менеджер пакетов, управляет установленными пакетами на вашем устройстве, отвечает за установку новых и удаление существующих.
  • Window Manager – менеджер окон, управляет окнами, и предоставляет для приложений более высокий уровень абстракции библиотеки Surface Manager.
  • Telephony Manager – менеджер телефонии, содержит API для взаимодействия с возможностями телефонии (звонки, смс и т.п.)
  • Content Providers – контент-провайдеры, управляют данными, которые одни приложения открывают для других, чтобы те могли их использовать для своей работы.
  • Resource Manager – менеджер ресурсов, обеспечивает доступ к ресурсам без функциональности (не несущими кода), например, к строковым данным, графике, файлам и другим.
  • View System – богатый и расширяемый набор представлений (Views), который может быть использован для создания визуальных компонентов приложений, например, списков, текстовых полей, таблиц, кнопок или даже встроенного web-браузера.
  • Location Manager – менеджер местоположения, позволяет приложениям периодически получать обновленные данные о текущем географическом положении устройства.
  • Notification Manager – менеджер оповещений, благодаря которому все приложения могут отображать собственные уведомления для пользователя в строке состояния.

Таким образом, благодаря Application Framework, приложения в ОС Android могут получать в своё распоряжение вспомогательный функционал, благодаря чему реализуется принцип многократного использования компонентов приложений и операционной системы. Естественно, в рамках политики безопасности.
Стоит отметить, просто на понятийном уровне, что фреймворк лишь выполняет код, написанный для него, в отличие от библиотек, которые исполняются сами. Ещё одно отличие заключается в том, что фреймворк содержит в себе большое количество библиотек с разной функциональностью и назначением, в то время как библиотеки объединяют в себе наборы функций, близких по логике.

APPLICATIONS (ПРИЛОЖЕНИЯ)
На вершине программного стека Android лежит уровень приложений (Applications). Сюда относится набор базовых приложений, который предустановлен на ОС Android. Например, в него входят браузер, почтовый клиент, программа для отправки SMS, карты, календарь, менеджер контактов и многие другие. Список интегрированных приложений может меняться в зависимости от модели устройства и версии Android. И помимо этого базового набора к уровню приложений относятся в принципе все приложения под платформу Android, в том числе и установленные пользователем.
Считается, что приложения под Android пишутся на языке Java, но нужно отметить, что существует возможность разрабатывать программы и на C/C++ (с помощью Native Development Kit), и на Basic (с помощью Simple) и с использованием других языков. Также можно создавать собственные программы с помощью конструкторов приложений, таких как App Inventor. Словом, возможностей тут много.

Добавить комментарий