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

14.06.2019

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 набирает популярность, думаю обзор основных 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

Search Bar – используется для поиска по приложению (заменяет Action Bar). Search Bar должен поддерживать предложения по поиску, а также может содержать селектор для выбора типа поиска.
Рекомендации по реализации паттерна: следует использовать для простого поиска по приложению, представлять богатые предложения поиска(например, заголовок с иконкой и описанием).
Companion Widget

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

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

P.S. Если тема вызывет интерес, можно продолжить обзором других UI паттернов.

Данный туториал поможет вам разобраться в очень полезном подходе по разработке приложений 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 { interface Callback { void onMessageRetrieved(String message); void onRetrievalFailed(String error); } }

public interface WelcomingInteractor extends Interactor {

interface Callback {

void onMessageRetrieved (String message ) ;

void onRetrievalFailed (String error ) ;

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

public interface MessageRepository { String getWelcomeMessage(); }

public interface MessageRepository {

String getWelcomeMessage () ;

public class WelcomingInteractorImpl extends AbstractInteractor implements WelcomingInteractor { ... private void notifyError() { mMainThread.post(new Runnable() { @Override public void run() { mCallback.onRetrievalFailed("Nothing to welcome you with:("); } }); } private void postMessage(final String msg) { mMainThread.post(new Runnable() { @Override public void run() { mCallback.onMessageRetrieved(msg); } }); } @Override public void run() { // получение сообщения final String message = mMessageRepository.getWelcomeMessage(); // проверяем, получили ли мы сообщение if (message == null || message.length() == 0) { // уведомляем об ошибке основной поток notifyError(); return; } // мы получили наше сообщение, уведомляем об этом UI в основном потоке postMessage(message); }

public class WelcomingInteractorImpl extends AbstractInteractor implements WelcomingInteractor {

. . .

private void notifyError () {

@ Override

public void run () {

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

} ) ;

private void postMessage (final String msg ) {

mMainThread . post (new Runnable () {

@ Override

public void run () {

mCallback . onMessageRetrieved (msg ) ;

} ) ;

@ Override

public void run () {

// получение сообщения

Final String message = mMessageRepository . getWelcomeMessage () ;

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

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

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

notifyError () ;

return ;

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

postMessage (message ) ;

Что же, взглянем на зависимости, создаваемые нашим 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 void testWelcomeMessageFound() throws Exception { String msg = "Welcome, friend!"; 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); }

. . .

@ Test

public void testWelcomeMessageFound () throws Exception {

String msg = "Welcome, friend!" ;

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 ) ;

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

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

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

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

public interface MainPresenter extends BasePresenter { interface View extends BaseView { void displayWelcomeMessage(String msg); } }

public interface MainPresenter extends BasePresenter {

interface View extends BaseView {

void displayWelcomeMessage (String msg ) ;

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

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

@Override protected void onResume() { super.onResume(); // начнем возврат приветствующего сообщения, при возобновлении работы приложения mPresenter.resume(); }

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

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

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

@Override public void resume() { mView.showProgress(); // инициализируем interactor WelcomingInteractor interactor = new WelcomingInteractorImpl(mExecutor, mMainThread, this, mMessageRepository); // запускаем interactor interactor.execute(); }

@ Override

public void resume () { mView . showProgress () ; // инициализируем interactor

WelcomingInteractor interactor = new WelcomingInteractorImpl (

mExecutor ,

mMainThread ,

this ,

mMessageRepository

) ; // запускаем interactor

interactor . execute () ;

Метод 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) { mView.hideProgress(); onError(error); }

x86 .

Изначально разрабатывалась компанией 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), а также проверить репозиторий на наличие доступных, но еще не установленных пакетов и архивов.


Рис. 1.1.

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

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

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

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

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

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


Рис. 1.2.

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 я начал относительно недавно (3 месяца назад) и первое с чем столкнулся - очень малое количество "русскоязычного" материала по этой теме, особенно, касательно таких вопросов как продумывание интерфейса или использование встроенных фич среды Android. Все что я находил, в большинстве своем, сводилось или к переводу статей с http://developer.android.com или примерами решающими конкретную задачу.

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

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

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

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

Для начала разработки вам необходимо 2 вещи: Android SDK (можно скачать с http://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, можно на http://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 (http://developer.android.com/intl/zh-TW/sdk/eclipse-adt.html и http://kenai.com/projects/nbandroid/ , соответственно) или качаем уже собранную IDE (например, MOTODEV Studio for Android 1.1 с сайта http://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, соответственно) хоть и присутствуют на сайте издателя, для загрузки мне еще не попадались.

Много справочного материала (правда с частично неработающими примерами;-)) есть на основном сайте проекта (http://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.

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

Похожие статьи