Архитектура андроид: Архитектура Android / Хабр

Содержание

Архитектура Android / Хабр

Автор перевода и источник:

Максим Юдин

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

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

На представленном ниже рисунке показана архитектура ОС Android.


Уровень приложений (Applications)

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

Уровень каркаса приложений (Application Framework)

Android позволяет использовать всю мощь API, используемого в приложениях ядра.

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

Основой всех приложений является набор систем и служб:
1. Система представлений (View System) – это богатый набор представлений с расширяемой функциональностью, который служит для построения внешнего вида приложений, включающий такие компоненты, как списки, таблицы, поля ввода, кнопки и т.п.
2. Контент-провайдеры (

Content Providers) – это службы, которые позволяют приложениям получать доступ к данным других приложений, а также предоставлять доступ к своим данным.
3. Менеджер ресурсов (Resource Manager) предназначен для доступа к строковым, графическим и другим типам ресурсов.
4. Менеджер извещений (Notification Manager) позволяет любому приложению отображать пользовательские уведомления в строке статуса.
5. Менеджер действий (Activity Manager) управляет жизненным циклом приложений и предоставляет систему навигации по истории работы с действиями.

Уровень библиотек (Libraries)

Платформа Android включает набор C/C++ библиотек, используемых различными компонентами ОС. Для разработчиков доступ к функциям этих библиотек реализован через использование Application Framework. Ниже представлены некоторые из них:
1. System C library — BSD-реализация стандартной системной библиотеки C (libc) для встраиваемых устройств, основанных на Linux.
2. Media Libraries – библиотеки, основанные на PacketVideo‘s OpenCORE, предназначенные для поддержки проигрывания и записи популярных аудио- и видео- форматов (MPEG4, H.264, MP3, AAC, AMR, JPG, PNG и т.п.).
3.

Surface Manager – менеджер поверхностей управляет доступом к подсистеме отображения 2D- и 3D- графических слоев.
4. LibWebCore – современный движок web-браузера, который предоставляет всю мощь встроенного Android-браузера.
5. SGL – движок для работы с 2D-графикой.
6. 3D libraries – движок для работы с 3D-графикой, основанный на OpenGL ES 1.0 API.
7. FreeType – библиотека, предназначенная для работы со шрифтами.
8. SQLite – мощный легковесный движок для работы с реляционными БД.

Уровень среды исполнения (Android Runtime)

В состав Android входит набор библиотек ядра, которые предоставляют большую часть функциональности библиотек ядра языка Java.

Платформа использует оптимизированную, регистр-ориентированную виртуальную машину Dalvik, в отличии от нее стандартная виртуальная машина Java – стек-ориентированная. Каждое приложение запускается в своем собственном процессе, со своим собственным экземпляром виртуальной машины. Dalvik использует формат Dalvik Executable (*. dex), оптимизированный для минимального использования памяти приложением. Это обеспечивается такими базовыми функциями ядра Linux, как организация поточной обработки и низкоуровневое управление памятью. Байт-код Java, на котором написаны ваши приложения, компилируются в dex-формат при помощи утилиты dx, входящей в состав SDK.

Уровень ядра Linux (Linux Kernel)

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

Clean Architecture в Android для начинающих

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

“Задача архитектуры программного обеспечения  — минимизация человеческих ресурсов, необходимых для создания и обслуживания требуемой системы”

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

Теоретически обосновал достижение этих целей Robert Martin (он же Uncle Bob). Он написал три книги о применении «чистого» подхода при разработке программного обеспечения (ПО). Одна из этих книг называется «Чистая архитектура, профессиональное руководство по структуре и дизайну программного обеспечения (ПО

)», она и явилась источником вдохновения при создании этой статьи.

Uncle Bob считается джедаем в разработке ПО

Кто-то скажет, это так, но меня это не касается, ведь в моем приложении уже есть архитектура MVVM?

Что ж, возможно, Clean Architecture может показаться излишней в том случае, если вы работаете над простым проектом. Но как быть, если нужно разделить модули, протестировать их изолированно и помочь всей команде в работе над отдельными контейнерами кода? Подход Clean Architecture освобождает разработчиков от дотошного изучения программного кода, пытаясь понять функции и логику функционирования.

Немного теории

Слои Clean Architecture

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

· Сущности (Entities): инкапсулируют наиболее важные правила функционирования корпоративного уровня. Сущности могут быть объектом с методами или набором из структур данных и функций.

· Сценарии использования (Use cases): организуют поток данных к объектам и от них.

· Контроллеры, сетевые шлюзы, презентеры (

Controllers, Gateways, Presenters): все это набор адаптеров, которые наиболее удобным способом преобразуют данные из сценариев использования и формата объектов для передачи в верхний слой (обычно пользовательский интерфейс).

· UI, External Interfaces, DB, Web, Devices: самый внешний слой архитектуры, обычно состоящий из таких элементов, как пользовательские и внешние интерфейсы, базы данных и веб-фреймворки.

После прочтения этих определений я всегда оказывался в замешательстве и не был готов реализовать «чистый» подход в своих Android проектах.

Прагматичный подход

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

· Домен: содержит определения логики функционирования приложения, модели данных сервера, абстрактное определение репозиториев и определение сценариев использования. Это простой, чистый модуль kotlin (независимый от Android).

Модуль домена

· Данные: содержит реализацию абстрактных определений доменного слоя. Может быть повторно использован любым приложением без модификаций. Он содержит репозитории и реализации источников данных, определение базы данных и ее DAO, определения сетевых API, некоторые средства преобразования для конвертации моделей сетевого API в модели базы данных и наоборот.

Модуль данных

· Приложение (или слой представления): он зависит от Android и содержит фрагменты, модели представления, адаптеры, действия и т.д. Он также содержит указатель служб для управления зависимостями, но при желании вы можете использовать Hilt.

Модуль приложения или представления

Практическая часть  —  небольшое приложение для книжного каталога

Чтобы применить на практике все эти “абстрактные” понятия, я разработал простое приложение, которое демонстрирует список книг, написанных дядей Бобом, и которое дает пользователям возможность помечать некоторые из них как “избранные”.

Модуль домена

Чтобы получить список книг, я использовал API Google книги. Это API возвращает список книг, отфильтрованных по параметру строки запроса:

API находится здесь.

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

data class Volume(val id: String, val volumeInfo: VolumeInfo)

Простая сущность класса данных:

data class VolumeInfo(
     val title: String,
     val authors: List<String>,
     val imageUrl: String?
 )

interface BooksRepository {

    suspend fun getRemoteBooks(author: String): Result<List<Volume>>

    suspend fun getBookmarks(): Flow<List<Volume>>

    suspend fun bookmark(book: Volume)

    suspend fun unbookmark(book: Volume)
}

Абстрактное определение репозитария книг

class GetBooksUseCase(private val booksRepository: BooksRepository) {
    suspend operator fun invoke(author: String) = booksRepository.getRemoteBooks(author)
}

Сценарии использования “Get books”

Модуль домена

Чтобы получить список книг, я использовал API Google книги. Это API возвращает список книг, отфильтрованных по параметру строки запроса:

Ссылка здесь.

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

Простой объект (entity) класса данных:

data class Volume(val id: String, val volumeInfo: VolumeInfo)

data class VolumeInf (
     val title: String,
     val authors: List<String>,
     val imageUrl: String?
 )

Абстрактное определение репозитария книг:

interface BooksRepository {

    suspend fun getRemoteBooks(author: String): Result<List<Volume>>

    suspend fun getBookmarks(): Flow<List<Volume>>

    suspend fun bookmark(book: Volume)

    suspend fun unbookmark(book: Volume)
}

Сценарии использования “Get books”:

class GetBooksUseCase(private val booksRepository: BooksRepository) {
    suspend operator fun invoke(author: String) = booksRepository. getRemoteBooks(author)
}

Слой данных

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

class BooksRepositoryImpl(
    private val localDataSource: BooksLocalDataSource,
    private val remoteDataSource: BooksRemoteDataSource
) : BooksRepository {

    override suspend fun getRemoteBooks(author: String): Result<List<Volume>> {
        return remoteDataSource.getBooks(author)
    }

    override suspend fun getBookmarks(): Flow<List<Volume>> {
        return localDataSource.getBookmarks()
    }

    override suspend fun bookmark(book: Volume) {
        localDataSource.bookmark(book)
    }

    override suspend fun unbookmark(book: Volume) {
        localDataSource. unbookmark(book)
    }
}

Поскольку мы определили источник данных для управления постоянством (persistence), на этом уровне нам также необходимо определить базу данных (можно использовать Room) и ее объекты. Кроме того, рекомендуется создать несколько модулей (mappers) для сопоставления ответа API с соответствующим объектом базы данных. Помните, нам нужно, чтобы доменный слой был независим от слоя данных, поэтому мы не можем напрямую аннотировать объект доменного тома (Volume) с помощью аннотации @Entity room. Нам определенно нужен еще один класс BookEntity, и мы определим маппер (mapper) между Volume и BookEntity.

class BookEntityMapper {
    fun toBookEntity(volume: Volume): BookEntity {
        return BookEntity(
            id = volume.id,
            title = volume.volumeInfo.title,
            authors = volume.volumeInfo.authors,
            imageUrl = volume. volumeInfo.imageUrl
        )
    }

    fun toVolume(bookEntity: BookEntity): Volume {
        return Volume(
            bookEntity.id,
            VolumeInfo(bookEntity.title, bookEntity.authors, bookEntity.imageUrl)
        )
    }
}

@Entity(tableName = "book")
data class BookEntity(
    @PrimaryKey
    val id: String,
    val title: String,
    val authors: List<String>,
    val imageUrl: String?
)

Слой презентации или приложения

В этом слое нам нужен фрагмент для отображения списка книг. Мы можем сохранить наш любимый подход MVVM. Модель представления принимает сценарии использования в своих конструкторах и вызывает соответствующий сценарий использования соответственно действиям пользователя (get books, bookmark, unbookmark).

Каждый сценариё использования вызывает соответствующий метод в репозитории:

class BooksViewModel(
    private val getBooksUseCase: GetBooksUseCase,
    private val getBookmarksUseCase: GetBookmarksUseCase,
    private val bookmarkBookUseCase: BookmarkBookUseCase,
    private val unbookmarkBookUseCase: UnbookmarkBookUseCase,
    private val mapper: BookWithStatusMapper
) : ViewModel() {

    private val _dataLoading = MutableLiveData(true)
    val dataLoading: LiveData<Boolean> = _dataLoading

    private val _books = MutableLiveData<List<BookWithStatus>>()
    val books = _books

    private val _error = MutableLiveData<String>()
    val error: LiveData<String> = _error

    private val _remoteBooks = arrayListOf<Volume>()

    // Getting books with uncle bob as default author :)
    fun getBooks(author: String) {
        viewModelScope. launch {
            _dataLoading.postValue(true)
            when (val booksResult = getBooksUseCase.invoke(author)) {
                is Result.Success -> {
                    _remoteBooks.clear()
                    _remoteBooks.addAll(booksResult.data)

                    val bookmarksFlow = getBookmarksUseCase.invoke()
                    bookmarksFlow.collect { bookmarks ->
                        books.value = mapper.fromVolumeToBookWithStatus(_remoteBooks, bookmarks)
                        _dataLoading.postValue(false)
                    }
                }

                is Result.Error -> {
                    _dataLoading.postValue(false)
                    books.value = emptyList()
                    _error.postValue(booksResult.exception.message)
                }
            }
        }
    }

    fun bookmark(book: BookWithStatus) {
        viewModelScope. launch {
            bookmarkBookUseCase.invoke(mapper.fromBookWithStatusToVolume(book))
        }
    }

    fun unbookmark(book: BookWithStatus) {
        viewModelScope.launch {
            unbookmarkBookUseCase.invoke(mapper.fromBookWithStatusToVolume(book))
        }
    }

    class BooksViewModelFactory(
        private val getBooksUseCase: GetBooksUseCase,
        private val getBookmarksUseCase: GetBookmarksUseCase,
        private val bookmarkBookUseCase: BookmarkBookUseCase,
        private val unbookmarkBookUseCase: UnbookmarkBookUseCase,
        private val mapper: BookWithStatusMapper
    ) :
        ViewModelProvider.NewInstanceFactory() {

        @Suppress("UNCHECKED_CAST")
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            return BooksViewModel(
                getBooksUseCase,
                getBookmarksUseCase,
                bookmarkBookUseCase,
                unbookmarkBookUseCase,
                mapper
            ) as T
        }
    }
}

Этот фрагмент только наблюдает за изменениями в модели представления и обнаруживает действия пользователя в пользовательском интерфейсе:

override fun onCreate(savedInstanceState: Bundle?) {
        super. onCreate(savedInstanceState)
        booksAdapter = BookAdapter(requireContext(), object : BookAdapter.ActionClickListener {
            override fun bookmark(book: BookWithStatus) {
                booksViewModel.bookmark(book)
            }

            override fun unbookmark(book: BookWithStatus) {
                booksViewModel.unbookmark(book)
            }
        })

        booksViewModel.getBooks("Robert C. Martin")
    }

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        booksViewModel.books.observe(viewLifecycleOwner, {
            booksAdapter.submitUpdate(it)
        })

        booksViewModel.dataLoading.observe(viewLifecycleOwner, { loading ->
            when (loading) {
                true -> LayoutUtils.crossFade(pbLoading, rvBooks)
                false -> LayoutUtils. crossFade(rvBooks, pbLoading)
            }
        })

        rvBooks.apply {
            layoutManager =
                GridLayoutManager(requireContext(), COLUMNS_COUNT)
            adapter = booksAdapter
        }

        booksViewModel.error.observe(viewLifecycleOwner, {
            Toast.makeText(
                requireContext(),
                getString(R.string.an_error_has_occurred, it),
                Toast.LENGTH_SHORT
            ).show()
        })
    }

Теперь посмотрим, как мы выполнили связь между слоями:

Взаимодействие между слоями

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

Полный проект можно найти на GitHub.

Читайте также:

Читайте нас в Telegram, VK и Яндекс. Дзен


Перевод статьи Nicola Gallazzi: Clean Architecture in Android — A Beginner Approach

Как работает Android. Архитектура платформы

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

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


Linux Kernel

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

Основные задачи, выполняемые ядром.

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

Power Management — это своего рода система управления питанием. Она предоставляет различные средства, с помощью которых приложение может реагировать на режимы питания устройства, а также поддерживать необходимые компоненты устройства активными. Например, при чтении книги нам было бы удобно, если бы экран оставался постоянно активным. Или когда мы включаем музыку и она продолжает проигрываться в фоне при отключенном экране.

Помимо вышеперечисленного, ядро Linux обеспечивает управление памятью и процессом.

Управление памятью.
При запуске различных приложений ядро ​​гарантирует, что пространство памяти, которое они используют, не конфликтует и не перезаписывает друг друга. Также оно проверяет, что все приложения получают достаточный объем памяти для своей работы, и в то же время следит, чтобы ни одно приложение не занимало слишком много места.

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


Hardware Abstraction Layer (HAL)

HAL обеспечивает связь между драйверами и библиотеками. Состоит он из нескольких библиотечных модулей, каждый из которых реализует интерфейс для определенного аппаратного компонента (Bluetooth, Камера итд.). И когда к оборудованию устройства обращаются через API-интерфейс, загружается необходимый для его работы модуль.

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


Android Runtime (ART)

Основным языком Android был выбран Java, поскольку это один из самых популярных языков программирования. Для Java существует много наработок и специалистов, а написанные на нем программы переносимы между операционными системами.

Но для того, чтобы программа работала на Java необходима виртуальная машина ‒ Java Virtual Machine. В Android используется виртуальная машина Android Runtime (ART). Эта машина специально оптимизирована для работы на мобильных устройствах: с нехваткой памяти, с постоянной выгрузкой и загрузкой приложений и т.д. В версиях Android ниже 5.0 Lollipop, использовалась виртуальная машина Dalvik — старая реализация виртуальной машины для Android.

В ART, как и в Dalvik, используется свой формат байт-кода ‒ DEX (Dalvik executable). При сборке приложения исходные файлы сначала компилируются в файлы типа class обычным компилятором, а затем конвертируются специальной утилитой в DEX.

И Dalvik, и ART оптимизируют выполняемый код, используя механизм компиляции just-in-time (JIT) — компиляция происходит во время выполнения приложения, что позволяет оптимизировать код для выполнения на конкретном устройстве. При этом байт-код приложения можно переносить на другие устройства.

ART может компилировать байт-код заранее, а не во время выполнения, используя ahead-of-time (AOT). Система сама решает, когда и какие приложения необходимо скомпилировать. Например, когда устройство не загружено и подключено к зарядке. При этом ART учитывает информацию о приложении, собранную во время предыдущих запусков, что дает дополнительную оптимизацию.


Native C/C++ Libraries

Набор библиотек, написанных на языках C или C++ и используемых различными компонентами ОС.

Примеры библиотек:

  • WebKit — представляет из себя движок веб-браузера и другие связанные с ним функции.
  • Media Framework предоставляет медиа кодеки, позволяющие записывать и воспроизводить различные медиа-форматы.
  • OpenGL — используется для отображения 2D и 3D графики.
  • SQLite — движок базы данных, используемый в Android для хранения данных.

Java API Framework (Application Framework)

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

  • Activity Manager — управляет жизненным циклом приложения и обеспечивает общий навигационный стек обратных вызовов.
  • Window Manager — управляет окнами и является абстракцией библиотеки Surface Manager.
  • Content Providers — позволяет приложению получать доступ к данным из других приложений или обмениваться собственными данными, т.е. предоставляет механизм для обмена данными между приложениями.
  • View System — содержит строительные блоки для создания пользовательского интерфейса приложения (списки, тексты, кнопки итд.), а также управляет событиями элементов пользовательского интерфейса.
  • Package Manager — управляет различными видами информации, связанными с пакетами приложений, которые в настоящее время установлены на устройстве.
  • Telephony Manager — позволяет приложению использовать возможности телефонии.
  • Resource Manager — обеспечивает доступ к таким ресурсам, как локализованные строки, растровые изображения, графика и макеты.
  • Location Manager — возможность определения местоположения.
  • Notification Manager — отображение уведомлений в строке состояния.

System Apps

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

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

Этот уровень использует все уровни ниже (если смотреть на схему) для правильного функционирования приложений.


Полезные ссылки

Официальная документация.
Hardware Abstraction Layer (HAL) на wiki.
Android Runtime на wiki.

Архитектура Android-приложений – MVVM или MVC? Oh! Android

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

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

О шаблонах, которые вы знаете, позвольте мне сделать что-то не так: я буду смешивать три разных шаблона, чтобы вы почувствовали, что делает что-то в android. Я считаю, что Presenter / ModelView должен быть где-то в Фрагменте или Деятельности. Адаптеры иногда могут выполнять эту работу, поскольку они заботятся о входах в списки. Вероятно, действия должны работать как контроллеры. Модели должны быть обычными java-файлами, тогда как представление должно содержать ресурсы макета и некоторые пользовательские компоненты, которые могут потребоваться реализовать.


Я могу дать вам несколько советов. Это ответ на вики сообщества, поэтому, надеюсь, другие люди могут включить другие предложения.

Организация файлов

Я думаю, что в основном есть две разумные возможности:

  • Организовать все по типу – создать папку для всех действий, другую папку для всех адаптеров, другую папку для всех фрагментов и т. Д.
  • Организовать все по домену (возможно, не лучшее слово). Это означало бы, что все, что связано с «ViewPost», будет находиться внутри одной папки – активности, фрагмента, адаптеров и т. Д. Все, что связано с «ViewPost», будет в другой папке. То же самое для «EditPost» и т. Д. Я предполагаю, что действия будут определять папки, которые вы создали, а затем для базовых классов, например, будет несколько более общих.

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

Именование

  • При создании макетов и стилей всегда указывайте (или идентифицируйте их) с помощью префикса для активности (/ фрагмента), где они используются.

Таким образом, все строки, стили, идентификаторы, используемые в контексте «ViewPost», должны начинаться с «@ id / view_post_heading» (например, для текстового вида), «@ style / view_post_heading_style», «@ string / view_post_greeting».

Это позволит оптимизировать автозаполнение, организацию, избежать группировки имен и т. Д.

Базовые классы

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

Генеральная

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

ПРОграммирование под Android: Архитектура операционной системы Андроид

Классический рисунок представляющий архитектуру ОС Android:

Если кому-то сложно с английским, то на всякий случай то же самое по на русском:


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

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


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

LINUX KERNEL (ЯДРО ЛИНУКС)


Как известно, Андроид основан на несколько урезанном ядре ОС Linux и поэтому на этом уровне мы можем видеть именно его (версии 2.6.x). Оно обеспечивает функционирование системы и отвечает за безопасность, управление памятью, энергосистемой и процессами, а также предоставляет сетевой стек и модель драйверов. Ядро также действует как уровень абстракции между аппаратным обеспечением и программным стеком.

LIBRARIES (БИБЛИОТЕКИ)


«Выше» ядра, как программное обеспечение промежуточного слоя, лежит набор библиотек (Libraries), предназначенный для обеспечения важнейшего базового функционала для приложений. То есть именно этот уровень отвечает за предоставление реализованных алгоритмов для вышележащих уровней, поддержку файловых форматов, осуществление кодирования и декодирования информации (в пример можно привести мультимедийные кодеки), отрисовку графики и многое другое. Библиотеки реализованы на C/C++ и скомпилированы под конкретное аппаратное обеспечение устройства, вместе с которым они и поставляются производителем в предустановленном виде.
Краткое описание некоторых из них:
  • Surface Manager – в ОС Android используется композитный менеджер окон, наподобие Compiz (Linux), но более упрощенный. Вместо того чтобы производить отрисовку графики напрямую в буфер дисплея, система посылает поступающие команды отрисовки в закадровый буфер, где они накапливаются вместе с другими, составляя некую композицию, а потом выводятся пользователю на экран. Это позволяет системе создавать интересные бесшовные эффекты, прозрачность окон и плавные переходы.
  • Media Framework – библиотеки, реализованные на базе PacketVideo OpenCORE. С их помощью система может осуществлять запись и воспроизведение аудио и видео контента, а также вывод статических изображений. Поддерживаются многие популярные форматы, включая MPEG4, H.264, MP3, AAC, AMR, JPG и PNG.
  • SQLite – легковесная и производительная реляционная СУБД, используемая в Android в качестве основного движка для работы с базами данных, используемыми приложениями для хранения информации.
  • OpenGL | ES – 3D библиотеки — используются для высокооптимизированной отрисовки 3D-графики, при возможности используют аппаратное ускорение. Их реализации строятся на основе API OpenGL ES 1.0.

    Источник: http://android-shark.ru/arhitektura-operatsionnoy-sistemyi-android/
    © Акулы Андроида

    3D библиотеки которые используются для высоко оптимизированной отрисовки 3D-графики, при возможности используют аппаратное ускорение. Их реализации строятся на основе API OpenGL ES 1.0.
    OpenGL ES (OpenGL for Embedded Systems) – подмножество графического программного интерфейса OpenGL, адаптированное для работы на встраиваемых системах.
  • FreeType – библиотека для работы с битовыми картами, а также для растеризации шрифтов и осуществления операций над ними. Это высококачественный движок для шрифтов и отображения текста.
  • LibWebCore – библиотеки известного шустрого браузерного движка WebKit, используемого также в десктопных браузерах Google Chrome и Apple Safari.
  • SGL (Skia Graphics Engine) – открытый движок для работы с 2D-графикой. Графическая библиотека является продуктом Google и часто используется в других их программах.
  • SSL — библиотеки для поддержки одноименного криптографического протокола.
  • Libc – стандартная библиотека языка C, а именно её BSD реализация, настроенная для работы на устройствах на базе Linux. Носит название Bionic.
На этом же уровне располагается Android Runtime – среда выполнения. Ключевыми её составляющими являются набор библиотек ядра и виртуальная машина Dalvik. Библиотеки обеспечивают большую часть низкоуровневой функциональности, доступной библиотекам ядра языка Java.

Источник: http://android-shark.ru/arhitektura-operatsionnoy-sistemyi-android/
© Акулы Андроида

 
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. Словом, возможностей тут много.

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

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


ядро Linux

В соответствии с большинством из всех слоев Linux — в том числе около 115 патч Linux 3.6. Он обеспечивает основные системные функции, такие как управление процессами, управление памятью, управление устройствами (например, камера, клавиатура, монитор). В то же время, ядро ​​Linux обрабатывает всю хорошую работу, и много оборудования, таких как сетевых драйверов, для того, чтобы избежать большого количества периферийного интерфейса аппаратного обеспечения, совместимого неудобства.


библиотека

В верхнем слое ядра Linux представляет собой набор библиотек, в том числе с открытым исходным кодом веб-браузер на движке Webkit, известный Libc библиотека для хранилищ данных и базы данных SQLite совместное использование приложений для воспроизведения, записи аудио и видео библиотеки, SSL библиотеки для сетевой безопасности.


Android-библиотека

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

  • android.app — обеспечивает доступ к модели приложения является краеугольным камнем всех приложений Android.
  • android.content — между удобством приложений, доступ к контенту между компонентами приложений, публикации сообщений.
  • android.database — используется для доступа к данным, опубликованным провайдером контента, в том числе класса управления базами данных SQLite.
  • android.opengl — OpenGL ES 3D рендеринга изображений API для интерфейса Java.
  • android.os — отметил возможность предоставлять доступ к приложениям к операционным системным службам, включая интер-сообщениями, системных служб и межпроцессного взаимодействия.
  • android.text — рендеринг и манипулирование текстом, отображаемым на устройстве.
  • android.view — на основе пользовательского интерфейса приложения строительных блоков.
  • android.widget — богатый набор предустановленных компонентов пользовательского интерфейса, включая кнопки, метки, списки, менеджеров компоновки, радио-кнопки, и так далее.
  • android.webkit — серия набора классов, которая позволяет обеспечить встроенные возможности просмотра веб-страниц для вашего приложения.

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


Android Время воспроизведения

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

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

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


Каркас приложения

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

  • События Manager — деятельность жизненным циклом приложений управления и все аспекты стека.
  • Контент-провайдеры — позволяет публиковать и обмена данными между приложениями.
  • Диспетчер ресурсов — предоставляет доступ к не-код встроенных ресурсов, таких как строки, настройки цвета и макета пользовательского интерфейса.
  • Диспетчер уведомлений — Позволяет приложение отображает диалоговое окно, или уведомление пользователю.
  • Просмотров System — масштабируемая набор представлений для создания пользовательских интерфейсов.

приложений

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

MVVM на Android с компонентами архитектуры + библиотека Koin | by Anastasia Uvarova | NOP::Nuances of Programming

После того, как мы описали модель и все ее части, пора ее реализовать. Для этого возьмем класс, родителем которого является класс ViewModel Android Jetpack.

class UserViewModel(private val userRepository: UserRepository) : ViewModel() {

private val _loadingState = MutableLiveData<LoadingState>()
val loadingState: LiveData<LoadingState>
get() = _loadingState

val data = userRepository.data

init {
fetchData()
}

private fun fetchData() {
viewModelScope.launch {
try {
_loadingState.value = LoadingState.LOADING
userRepository.refresh()
_loadingState.value = LoadingState.LOADED
} catch (e: Exception) {
_loadingState.value = LoadingState.error(e.message)
}
}
}
}

Класс ViewModel создан для того, чтобы хранить и управлять данными, связанными с UI относительно жизненного цикла. Он позволяет данным пережить изменения конфигурации, например, повороты экрана.

View-model берет репозиторий в качестве параметра. Этот класс “знает” все источники данных для нашего приложения. В начальном блоке view-model мы обновляем данные БД. Это делается вызовом метода обновления репозитория. А еще у view-model есть свойство data. Оно получает данные локально напрямую. Это гарантия, что у пользователя всегда будет что-то в интерфейсе, даже если устройство не в сети.

Подсказка: я пользовался вспомогательным классом, который помогал мне управлять состоянием загрузки

data class LoadingState private constructor(val status: Status, val msg: String? = null) {
companion object {
val LOADED = LoadingState(Status.SUCCESS)
val LOADING = LoadingState(Status.RUNNING)
fun error(msg: String?) = LoadingState(Status.FAILED, msg)
}

enum class Status {
RUNNING,
SUCCESS,
FAILED
}
}

Это последний компонент архитектуры. Он напрямую общается с представлением-моделью, получает данные и, например, передает их в recycler-view. В нашем случае представление — это простая активность.

class MainActivity : AppCompatActivity() {

private val userViewModel by viewModel<UserViewModel>()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

userViewModel.data.observe(this, Observer {
// Todo: Populate the recyclerView here
it.forEach { githubUser ->
Toast.makeText(baseContext, githubUser.login, Toast.LENGTH_SHORT).show()
}
})

userViewModel.loadingState.observe(this, Observer {
when (it.status) {
LoadingState.Status.FAILED -> Toast.makeText(baseContext, it.msg, Toast.LENGTH_SHORT).show()
LoadingState.Status.RUNNING -> Toast.makeText(baseContext, "Loading", Toast.LENGTH_SHORT).show()
LoadingState.Status.SUCCESS -> Toast.makeText(baseContext, "Success", Toast.LENGTH_SHORT).show()
}
})
}
}

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

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

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

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

val viewModelModule = module {
viewModel { UserViewModel(get()) }
}

val apiModule = module {
fun provideUserApi(retrofit: Retrofit): UserApi {
return retrofit.create(UserApi::class.java)
}

single { provideUserApi(get()) }
}

val netModule = module {
fun provideCache(application: Application): Cache {
val cacheSize = 10 * 1024 * 1024
return Cache(application.cacheDir, cacheSize.toLong())
}

fun provideHttpClient(cache: Cache): OkHttpClient {
val okHttpClientBuilder = OkHttpClient.Builder()
.cache(cache)

return okHttpClientBuilder.build()
}

fun provideGson(): Gson {
return GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.IDENTITY).create()
}

fun provideRetrofit(factory: Gson, client: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create(factory))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.client(client)
.build()
}

single { provideCache(androidApplication()) }
single { provideHttpClient(get()) }
single { provideGson() }
single { provideRetrofit(get(), get()) }

}

val databaseModule = module {

fun provideDatabase(application: Application): AppDatabase {
return Room.databaseBuilder(application, AppDatabase::class.java, "eds.database")
.fallbackToDestructiveMigration()
.allowMainThreadQueries()
.build()
}

fun provideDao(database: AppDatabase): UserDao {
return database.userDao
}

single { provideDatabase(androidApplication()) }
single { provideDao(get()) }
}

val repositoryModule = module {
fun provideUserRepository(api: UserApi, dao: UserDao): UserRepository {
return UserRepository(api, dao)
}

single { provideUserRepository(get(), get()) }
}
//Module.kt

В Module.kt есть объявление объекта, который нужен приложению. А в представлении мы берем inject, который говорит Koin, что нужен объект view-model. Библиотека в свою очередь старается найти этот объект в модуле, который мы определили ранее. Когда найдёт, назначит ему свойство userViewModel. А если не найдёт, то выдаст исключение. В нашем случае, код скомпилируется правильно, у нас есть экземпляр view-model в модуле с соответствующим параметром.

Похожий сценарий применится к репозиторию внутри view-model. Экземпляр будет получен из модуля Koin, потому что мы уже создали репозиторий с нужным параметром внутри модуля Koin.

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

Вы можете найти полный код приложения у меня на GitHub по этой ссылке.

Читайте также:

Читайте нас в телеграмме, vk и Яндекс.Дзен

Добавление компонентов в ваш проект | Разработчики Android

Перед тем, как начать, мы рекомендуем прочитать Компоненты архитектуры Руководство по архитектуре приложений. В руководстве есть несколько полезных принципов, которые применимы ко всем приложениям Android и показывает, как использовать компоненты архитектуры вместе.

Компоненты архитектуры

доступны в репозитории Google Maven. Использовать их, вы должны добавить репозиторий в свой проект.

Откройте сборку .файл gradle для вашего проекта (не для вашего приложения или модуля) и добавьте репозиторий google () как показано ниже:

Заводной

allprojects {
    репозитории {
          гугл () 
        jcenter ()
    }
}
 

Котлин

allprojects {
    репозитории {
          гугл () 
        jcenter ()
    }
}
 
Предупреждение: Репозиторий JCenter стал доступен только для чтения 31 марта 2021 г. Подробнее информацию см. в обновлении службы JCenter.

Объявление зависимостей

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

См. Инструкции по объявлению зависимостей для каждого компонента архитектуры в примечаниях к выпуску:

См. Самые свежие версии AndroidX номера версий для каждого компонента.

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

Котлин

Модули расширения

Kotlin поддерживаются для нескольких зависимостей AndroidX. Эти модули к их именам добавлен суффикс «-ktx». Например:

Заводной

реализация "androidx.lifecycle: lifecycle-viewmodel: $ lifecycle_version"
 

Котлин

реализация ("androidx.lifecycle: lifecycle-viewmodel: $ lifecycle_version")
 

становится

Заводной

реализация "androidx.жизненный цикл: жизненный цикл-viewmodel-ktx: $ lifecycle_version "
 

Котлин

реализация ("androidx.lifecycle: lifecycle-viewmodel-ktx: $ lifecycle_version")
 

Дополнительную информацию, включая документацию по расширениям Kotlin, можно найти в Документация KTX.

Примечание: Для приложений на основе Kotlin убедитесь, что вы используете kapt вместо annotationProcessor . Вам также следует добавить плагин kotlin-kapt .

Архитектура платформы

| Разработчики Android

Android — это программный стек на базе Linux с открытым исходным кодом, созданный для широкого круга пользователей. набор устройств и форм-факторов.На следующей диаграмме показаны основные компоненты платформы Android.

Рисунок 1. Стек программного обеспечения Android.

Ядро Linux

Основой платформы Android является ядро ​​Linux. Например, Android Runtime (ART) полагается на ядро ​​Linux для базовые функции, такие как многопоточность и низкоуровневое управление памятью.

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

Уровень аппаратной абстракции (HAL)

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

Среда выполнения Android

Для устройств под управлением Android версии 5.0 (уровень API 21) или выше каждое приложение работает в собственном процессе и с собственным экземпляром Android Runtime (ИЗОБРАЗИТЕЛЬНОЕ ИСКУССТВО). ART написан для запуска нескольких виртуальных машин с малым объемом памяти. устройства, выполняя файлы DEX, формат байт-кода, разработанный специально для Android, оптимизированный для минимального использования памяти. Инструменты сборки, такие как d8, скомпилировать Исходники Java в байт-код DEX, который может работать на платформе Android.

Некоторые из основных особенностей ВРТ включают следующее:

  • Опережающая (AOT) и своевременная (JIT) компиляция
  • Оптимизированная сборка мусора (GC)
  • На Android 9 (уровень API 28) и выше преобразование пакета приложения Исполняемый файл Dalvik форматировать (DEX) файлы в более компактный машинный код.
  • Улучшенная поддержка отладки, включая специальный профилировщик выборки, подробные диагностические исключения и отчеты о сбоях, а также возможность установки точки наблюдения для мониторинга определенных полей

До версии Android 5.0 (уровень API 21), Dalvik была средой выполнения Android. Если ваше приложение хорошо работает на ART, оно должно работать и на Dalvik, но обратное не может быть правда.

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

Собственные библиотеки C / C ++

Многие основные компоненты и службы системы Android, такие как ART и HAL, являются построенный из собственного кода, для которого требуются собственные библиотеки, написанные на C и C ++.Платформа Android предоставляет API-интерфейсы Java framework для раскрытия функциональности некоторых из этих собственных библиотек для приложений. Например, вы можете получить доступ OpenGL ES через API Java OpenGL для Android framework для добавления поддержка рисования и управления 2D- и 3D-графикой в ​​вашем приложении.

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

Java API Framework

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

  • Богатый и расширяемый вид Система, которую вы можете использовать для создания пользовательского интерфейса приложения, включая списки, сетки, текст ящики, кнопки и даже встраиваемый веб-браузер
  • Менеджер ресурсов, предоставление доступа к ресурсам, не связанным с кодом, таким как локализованные строки, графика, и файлы макета
  • Уведомление Диспетчер, который позволяет всем приложениям отображать настраиваемые предупреждения в строке состояния
  • Менеджер деятельности, который управляет жизненным циклом приложений и предоставляет общий стек обратной навигации
  • Содержание Поставщики, которые позволяют приложениям получать доступ к данным из других приложений, например приложение «Контакты» или поделиться своими данными

Разработчики имеют полный доступ к тем же API-интерфейсам платформы, что и системные приложения Android.

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

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

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

Архитектура приложений Android | CodePath Android Cliffnotes

Обзор

При первом создании приложений для Android многие разработчики могут начать с использования шаблонов Model View Controller (MVC) и обычно заканчивают записью большей части основной бизнес-логики в действиях или фрагментах.Проблема в том, что написать тесты, которые могут проверить поведение приложения, сложно, потому что код часто очень тесно связан с платформой Android и различными событиями жизненного цикла. Хотя автоматическое тестирование пользовательского интерфейса может быть написано для проверки отдельных действий или фрагментов, поддерживать и выполнять их в долгосрочной перспективе часто бывает трудно.

Архитектурные узоры

В настоящее время существует три основных подхода к созданию архитектуры ваших приложений для Android:

  1. Стандартный Android (модель-представление-контроллер) — это подход «по умолчанию» с файлами макета, действиями / фрагментами, действующими в качестве контроллера, и моделями, используемыми для данных и сохраняемости.При таком подходе Activity несут ответственность за обработку данных и обновление представлений. Действия действуют как контроллер в MVC, но с некоторыми дополнительными обязанностями, которые должны быть частью представления. Проблема с этой стандартной архитектурой заключается в том, что действия и фрагменты могут становиться довольно большими и их очень трудно тестировать. Вы можете узнать больше в этом сообщении в блоге.

  2. Чистая архитектура (Презентатор представления модели) — при использовании MVP действия и фрагменты становятся частью уровня представления и делегируют большую часть работы объектам-докладчикам.У каждого Activity есть соответствующий ведущий, который обрабатывает весь доступ к модели. Докладчики также уведомляют Activity, когда данные готовы к отображению. Вы можете узнать больше в разделах ниже.

  3. Связывание данных MVVM (Model-View-ViewModel) — ViewModels извлекает данные из модели по запросу из представления через структуру привязки данных Android. Благодаря этому шаблону действия и фрагменты становятся очень легкими. Более того, писать модульные тесты становится проще, потому что модели представления отделены от представления.Вы можете узнать больше в этом сообщении в блоге.

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

Чистая архитектура

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

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

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

Чистая архитектура вводит больше абстракций и пытается применить принципы единой ответственности в разработке Android.Хотя могут возникнуть опасения по поводу того, что этот подход добавляет больше сложности, медленной производительности и плохой тестируемости, было показано, что он успешно работает в производственных приложениях (см. Этот доклад Droidcon или этот доклад Droidcon 2016).

Модель View Presenter

При разработке приложений для Android традиционный шаблон «Модель / Представление / Контроллер» часто отбрасывается в пользу шаблона «Модель / Представление / Презентатор». Архитектура Model-View-Presenter (MVP) включает:

  • Модель : уровень данных
  • View : слой пользовательского интерфейса, отображает данные, полученные от Presenter, реагирует на ввод пользователя.В Android мы рассматриваем Activity, Fragments и android.view.View как View from MVP.
  • Presenter : реагирует на действия, выполняемые на уровне пользовательского интерфейса, выполняет задачи для объектов модели (используя варианты использования), передает результаты этих задач в представления.

С помощью MVP мы хотим достичь более простых задач, меньших объектов и меньшего количества зависимостей между слоями модели и представлений. Это, в свою очередь, упрощает управление и тестирование нашего кода. Основные отличия от MVC:

  • Вид больше отделен от модели.Презентатор является посредником между моделью и представлением.
  • Проще создавать модульные тесты
  • Обычно между View и Presenter существует однозначное соответствие с возможностью использования нескольких Presenters для сложных представлений

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

Переход на чистую архитектуру

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

Шаблоны

Следующие шаблоны проектов созданы, чтобы выступить в качестве отправной точки для этой архитектуры:

Список литературы

Чистая архитектура:

Шаблон MVVM:

Другие обучающие статьи:

Архитектура приложений Android.Архитектура приложения — это… | доктора Танвира Хоссейна | Oceanize Geeks

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

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

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

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

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

Новое руководство по архитектуре Android определяет некоторые ключевые принципы, которым должно соответствовать хорошее приложение Android, а также предлагает разработчику безопасный путь для создания хорошего приложения. Согласно руководству, хорошее приложение Android должно обеспечивать четкое разделение проблем и управлять пользовательским интерфейсом из модели. Любой код, который не обрабатывает пользовательский интерфейс или взаимодействие с операционной системой, не должен находиться в Activity или Fragment, потому что поддержание их как можно более чистым позволит вам избежать многих проблем, связанных с жизненным циклом.В конце концов, система может уничтожить Активности или Фрагменты в любое время. Кроме того, данные должны обрабатываться моделями, которые изолированы от пользовательского интерфейса и, следовательно, от проблем жизненного цикла.

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

Чтобы понять, что предлагает команда Android, мы должны знать все элементы компонентов архитектуры, поскольку именно они будут делать за нас тяжелую работу. Существует четыре компонента, каждый из которых выполняет определенную роль: Room , ViewModel , LiveData и Lifecycle . У всех этих частей есть свои обязанности, и они работают вместе, чтобы создать прочную архитектуру.Давайте посмотрим на упрощенную схему предлагаемой архитектуры, чтобы лучше ее понять.

Как мы видим, у нас есть три основных элемента, каждый со своей ответственностью.

  1. Действие и Фрагмент представляют уровень View , который не занимается бизнес-логикой и сложными операциями. Он только настраивает представление, обрабатывает взаимодействие с пользователем и, что наиболее важно, наблюдает и отображает LiveData элементов, взятых из ViewModel .
  2. Модель просмотра автоматически наблюдает за состоянием представления Lifecycle , поддерживая согласованность во время изменений конфигурации и других событий жизненного цикла Android. Представление также требует выборки данных из репозитория , который предоставляется как наблюдаемый LiveData . Важно понимать, что ViewModel никогда не ссылается на View напрямую и что обновления данных всегда выполняются объектом LiveData .
  3. Репозиторий не является специальным компонентом Android. Это простой класс без какой-либо конкретной реализации, который отвечает за выборку данных из всех доступных источников, от базы данных до веб-служб. Он обрабатывает все эти данные, обычно преобразуя их в наблюдаемые LiveData и делая их доступными для ViewModel .
  4. База данных Room — это библиотека сопоставления SQLite, которая упрощает процесс работы с базой данных.Он автоматически записывает тонны шаблонов, проверяет ошибки во время компиляции и, что самое главное, может напрямую возвращать запросы с наблюдаемыми LiveData .

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

Еще одна рекомендация Android — консолидировать свою архитектуру с помощью системы внедрения зависимостей, такой как Google Dagger 2, или с помощью шаблона Service Locator.

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

Большинство компонентов приложения Android имеют прикрепленные к ним жизненные циклы, которые управляются непосредственно самой системой. До недавнего времени разработчик должен был следить за состоянием компонентов и действовать соответствующим образом, инициализируя и завершая задачи в нужное время. Однако запутаться и допустить ошибку, связанную с этим типом операций, было действительно легко.Но пакет android.arch.lifecycle все изменил.

Теперь к действиям и фрагментам прикреплен объект Lifecycle , который можно наблюдать с помощью классов LifecycleObserver , таких как модель ViewModel или любой объект, реализующий этот интерфейс. Это означает, что наблюдатель будет получать обновления об изменениях состояния наблюдаемого объекта, например, когда Activity приостановлено или когда оно запускается. Он также может проверить текущее состояние наблюдаемого объекта.Так что теперь намного проще выполнять операции, которые должны учитывать жизненные циклы фреймворка.

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

LifecycleObserver принимает события Lifecycle и может реагировать с помощью аннотаций.Переопределение метода не требуется.

Компонент LiveData — это хранилище данных, которое содержит значение, которое можно наблюдать. Учитывая, что наблюдатель предоставил жизненный цикл во время создания экземпляра LiveData , LiveData будет вести себя в соответствии с состоянием жизненного цикла . Если состояние Lifecycle наблюдателя — STARTED или RESUMED , наблюдатель — active ; в противном случае это неактивный .

LiveData знает, когда данные были изменены, а также является ли наблюдатель активным и должен получить обновление. Другой интересной характеристикой LiveData является то, что он способен удалять наблюдателя, если он находится в состоянии Lifecycle.State.DESTROYED , избегая утечек памяти при наблюдении с помощью Activity и Fragments.

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

Android с самого начала поддерживает SQLite; однако, чтобы это работало, всегда приходилось писать много шаблонов. Кроме того, SQLite не сохранял POJO (устаревшие объекты Java) и не проверял запросы во время компиляции.Для решения этих проблем идет Room ! Это библиотека отображения SQLite, способная сохранять объекты Java POJO, напрямую преобразовывать запросы в объекты, проверять ошибки во время компиляции и создавать LiveData наблюдаемых по результатам запросов. Room — это библиотека объектно-реляционного сопоставления с некоторыми интересными дополнениями для Android.

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

Для создания базы данных Room вам понадобится @Entity для сохранения, который может быть любым Java POJO, интерфейс @Dao для выполнения запросов и операций ввода / вывода и @Database абстрактный класс, который должен расширять RoomDatabase .

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

Архитектура Android — Tutlane

Архитектура Android — это программный стек компонентов для поддержки потребностей мобильных устройств. Стек программного обеспечения Android содержит ядро ​​Linux, набор библиотек c / c ++, которые предоставляются через службы инфраструктуры приложения, среду выполнения и приложение.

Ниже приведены основные компоненты архитектуры Android, а именно

.

  1. Приложения
  2. Android Framework
  3. Среда выполнения Android
  4. Библиотеки платформ
  5. Ядро Linux

В этих компонентах ядро ​​ Linux Kernel является основным компонентом в Android, обеспечивающим функции операционной системы для мобильных устройств, и Dalvik Virtual Machine ( DVM ), которая отвечает за запуск мобильного приложения.

Ниже приводится графическое представление архитектуры Android с различными компонентами.

Приложения

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

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

Инфраструктура приложений

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

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

Среда выполнения Android

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

Виртуальная машина Dalvik ( DVM ) — это виртуальная машина на основе регистров, такая как виртуальная машина Java (JVM).Он специально разработан и оптимизирован для Android, чтобы устройство могло эффективно запускать несколько экземпляров. Он полагается на ядро ​​Linux для потоковой передачи и низкоуровневого управления памятью.

Основные библиотеки в среде выполнения Android позволят нам реализовать приложения Android с использованием стандартного языка программирования JAVA.

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

Библиотеки платформы включает в себя различные базовые библиотеки C / C ++ и библиотеки на основе Java, такие как SSL, libc, Graphics, SQLite, Webkit, Media, Surface Manger, OpenGL и т. Д.для поддержки разработки под Android.

Ниже приведены краткие сведения о некоторых основных библиотеках Android, доступных для разработки под Android.

  • Медиа-библиотека для воспроизведения и записи аудио и видео форматов
  • Библиотека диспетчера поверхности для управления дисплеем
  • Графические библиотеки SGL и OpenGL для 2D и 3D графики
  • SQLite для поддержки базы данных и FreeType для поддержки шрифтов
  • Web-Kit для поддержки веб-браузера и SSL для безопасности в Интернете.

Ядро Linux

Ядро Linux — это нижний уровень и сердце архитектуры Android. Он управляет всеми драйверами, такими как драйверы дисплея, драйверы камеры, драйверы Bluetooth, аудиодрайверы, драйверы памяти и т. Д., Которые в основном требуются для устройства Android во время выполнения.

Ядро Linux обеспечивает уровень абстракции между оборудованием устройства и остальной частью стека. Он отвечает за управление памятью, управление питанием, управление устройствами, доступ к ресурсам и т. Д.

Рекомендуемая архитектура для приложений Android

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

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

Так что же Google рекомендует в качестве отправной точки для Android-приложения? Ну все просто. Вот несколько правил, которым нужно следовать:

  • Будьте реактивными
  • Используйте ViewModels с LiveData
  • Используйте репозиторий для выборки и кэширования данных

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

Что отличает хорошую архитектуру программного обеспечения?

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

  1. Простота
  2. Ремонтопригодность
  3. Гибкость

Давайте рассмотрим каждую точку более подробно.

Простота

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

Ремонтопригодность

Основная идея этого принципа — как можно меньше зависеть от «внешнего мира».Например, предположим, что приложение разработано с использованием MVP (Model View Presenter) с архитектурой шаблона репозитория. Если репозитории содержат прямую ссылку на что-то специфичное для Android, например AsyncLoaders, то каждый класс репозитория связан с классом AsyncLoader. Класс AsyncLoader — это поддерживаемая Google библиотека, которая в какой-то момент устарела, в результате чего все ее иждивенцы также в некотором роде устарели. Этот риск возрастает при использовании зависимостей без поддержки сильного сообщества, потому что более вероятно, что они станут устаревшими.Такое связывание является громоздким и может затруднить сопровождение проекта по мере того, как с течением времени начинают возникать проблемы совместимости.

Гибкость

Этот принцип в основном касается простоты изменения. Например, может ли уровень пользовательского интерфейса приложения развиваться независимо от его бизнес-логики или уровня данных? Если все втиснуто в Действия или Фрагменты, что сделало бы проект хрупким, и реализация новых функций потребовала бы редактирования огромных классов, что могло бы привести к появлению ошибок.Разделение вещей на слои окажется полезным в будущем, когда будут реализованы новые функции, такие как извлечение бизнес-логики из уровня пользовательского интерфейса (действия или фрагменты) и их перемещение во что-то вроде Presenter. Когда логика должным образом разделена, изменить одну часть приложения, не затрагивая другие, становится легко — хорошая архитектура должна это поддерживать.

Популярная архитектура для приложений Android

MVC (Модель — Вид — Контроллер)

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

MVP (модель — вид — докладчик)

Что-то, что сообщество Android начало использовать все больше и больше, было шаблоном MVP, где бизнес-логика была определена внутри классов, называемых Presenters.Activity будет расширять представление и взаимодействовать с Presenter, что будет информировать Presenter о действиях пользователя. Эта установка оказалась очень эффективной, поскольку бизнес-логика хорошо изолирована, а представление можно заменить независимо от докладчиков.

MVVM (Модель — Вид — Модель просмотра)

Возникла проблема с шаблоном MVP, он никак не реагировал. Чтобы связать обновления модели с представлением, пришлось написать много шаблонов. В поисках возможных ответов Android-сообщество осознало, что реактивный подход будет более простым и эффективным для мобильных архитектур.ViewModel — это модель, которую наблюдает View, и каждый раз, когда модель изменяется, View обновляется. Здесь на помощь приходит библиотека привязки данных Google — она ​​автоматически связывает LiveData из ViewModels с представлениями.

MVI (модель — вид — намерение)

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

Архитектура, рекомендованная Google для приложений Android

Что на самом деле рекомендует Google из всех этих вариантов? MVVM. Давайте углубимся в основные строительные блоки набора инструментов MVVM.

Реактивный ранец

Jetpack — это набор инструментов Google для создания и проектирования приложений для Android. Он состоит из множества различных библиотек, освобождающих разработчиков от бремени написания этих инструментов самостоятельно.Некоторые из наиболее часто используемых компонентов — это LiveData, ViewModel, Data Binding, Navigation Component и Room.

MVVM как реактивная архитектура для уровня пользовательского интерфейса

Из чего состоит типичный экран в настройке MVVM? На нижнем уровне есть View, Activity / Fragment и, кроме того, есть ViewModels, которые предоставляют LiveData. Выходя за рамки ViewModels, мы можем найти репозитории, которые обычно используют Room для локального хранения данных.

ViewModel как первый слой кэша

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

LiveData

Модели

ViewModels обычно открывают реактивные источники информации, известные как LiveData.LiveData — это специальный класс, который знает, как общаться с LifeCycleOwners, например с Activity и Fragments. Поскольку ViewModel переживает Activity и Fragments, при обновлении представлений необходимо проявлять особую осторожность, и ViewModel не должен напрямую содержать ссылки на какие-либо представления. Вот где LiveData может быть очень полезной; LiveData может содержать наблюдателей внутри и знать каждый раз, когда наблюдатель (представление) уничтожается. Когда представление уничтожается, оно удаляется из массива наблюдателей, что предотвращает любые исключения нулевого указателя, которые могут быть обычными в мире Android.

Репозиторий как источник данных и второй уровень кеширования

У репозиториев

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

Источник базы данных

Репозиторий может хранить свои данные локально.Когда дело доходит до локального упорства, есть множество вариантов; Jetpack рекомендует библиотеку Room, которая заботится о создании таблиц SQLight и предоставляет простые методы для хранения и извлечения данных

Сетевой источник

Когда дело доходит до получения данных из сети, Google рекомендует библиотеку Retrofit. Он работает с сопрограммами и несколькими типами ответов, такими как JSON, XML и т. Д.

Ресурс, связанный с сетью

Ресурс с привязкой к сети — это реализация логики репозитория, разработанная инженерами Google, где реализована логика локального кэширования данных с помощью Room.Базовый ресурс, возвращаемый классом NetworkBoundResource, является универсальным классом Resource, который может содержать данные, статусы или ошибки.

Чем хороша архитектура Google для приложений Android

Исправляет ошибки жизненного цикла и изменения конфигурации

С ViewModels, которые могут выжить при изменении конфигурации, исправлено большинство ошибок вращения. LiveData решает проблему обновления разрушенного представления, что, в свою очередь, упрощает логику получения новых данных. Разработчикам не нужно беспокоиться о жизненных циклах Views, они могут просто получить новые данные и сохранить их в LiveData.

Отделяет действия и фрагменты от бизнес-логики и логики пользовательского интерфейса

В качестве бонуса ViewModels теперь является центральным местом для получения данных в приложениях, и весь этот код перемещен из Activity и Fragments в ViewModels. Этот подход может очень хорошо масштабироваться, поскольку очень просто повторно использовать ViewModels для разных действий или фрагментов. Также очень просто использовать несколько ViewModels в Activity или Fragment; это очень важно, поскольку логику приложения можно разделить на несколько одноцелевых ViewModels.

Отрицательные рекомендации Google

В сочетании с платформой Android

Все упомянутые классы тесно связаны с Android Framework. Это особенно верно, когда речь идет о тестировании. Для тестирования LiveData и ViewModels необходимо использовать специальную среду тестирования, что не идеально. Если ViewModels связаны с бизнес-логикой приложения, может возникнуть проблема, если Google решит отказаться от ViewModels.

Хранилища плохо подходят для мобильных приложений

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

Чего не хватает в рекомендациях Google?

Зависимость впрыска

Хотя Google упоминает Dagger в документации Jetpack, я бы не рекомендовал его для простых приложений, потому что он очень раздутый, сложный и трудный для понимания.Google предпринял некоторые шаги по упрощению Dagger, представив Hilt. Dagger — это тяжеловес среди инструментов DI на Android, и его следует использовать с осторожностью. Рекомендую рассмотреть следующие альтернативы:

Монета

Koin — это простая и быстрая библиотека внедрения зависимостей для Kotlin со специальными помощниками для приложений Android. Он настолько простой, но настолько мощный, что я рекомендую Koin в качестве замены Dagger по умолчанию.

Кодеин

Подобно Koin, Kodein имеет больше функций и немного более сложный API.

Зубочистка

В то время как Koin и Kodein являются скорее локатором сервисов, ToothPick ближе к Dagger, потому что он использует аннотации для генерации кода во время компиляции, что приводит к повышению производительности, когда это необходимо.

А как насчет крупномасштабных приложений?

Чистая архитектура

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

Слои

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

Модули

Layers могут быть реализованы как отдельные модули в студии Android. Базовая установка будет представлять собой модуль представления для классов фреймворка Android, таких как Activity, Fragments и ViewModels. Модуль данных может использоваться там, где сетевой уровень реализован как репозитории и классы модернизации.Наконец, подходит модуль предметной области, где вся бизнес-логика реализована с использованием классов UseCase.

Заключение

Как мы уже говорили, для приложений Android существует множество архитектур. Основные рекомендации Google поддерживают MVVM, используя такие вещи, как LiveData и ViewModels, для решения двух наиболее распространенных проблем, с которыми сталкиваются приложения Android: ловушек жизненного цикла и изменения ротации. Правильное разделение логики и поведения позволяет приложениям быть гибкими и простыми в обслуживании.

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

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

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

MVVM с чистой архитектурой: приложения Android, которые масштабируются

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

Это не просто руководство по Android MVVM. В этой статье мы собираемся объединить MVVM (Model-View-ViewModel или иногда стилизованный «шаблон ViewModel») с чистой архитектурой.Мы увидим, как эту архитектуру можно использовать для написания несвязанного, тестируемого и поддерживаемого кода.

Почему MVVM с чистой архитектурой?

MVVM отделяет ваше представление (т.е. Activity s и Fragment s) от вашей бизнес-логики. MVVM достаточно для небольших проектов, но когда ваша кодовая база становится огромной, ваши ViewModel начинают раздуваться. Разделять обязанности становится сложно.

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

Примечание. Чистую архитектуру можно сочетать с архитектурой модель-представление-презентатор (MVP). Но поскольку компоненты архитектуры Android уже предоставляют встроенный класс ViewModel , мы будем использовать MVVM поверх MVP — инфраструктура MVVM не требуется!

Преимущества использования чистой архитектуры

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

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

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

Наш поток данных будет выглядеть так:

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

Пример, который мы собираемся увидеть, довольно прост. Он позволяет пользователям создавать новые сообщения и просматривать список созданных ими сообщений. Я не использую сторонние библиотеки (например, Dagger, RxJava и т. Д.) В этом примере для простоты.

Уровни MVVM с чистой архитектурой

Код разделен на три отдельных уровня:

  1. Уровень представления
  2. Уровень домена
  3. Уровень данных

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

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

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

Это включает наши Activity s, Fragment s и ViewModel s. Действие должно быть максимально тупым. Никогда не помещайте свою бизнес-логику в Activity s.

Activity будет взаимодействовать с ViewModel , а ViewModel будет взаимодействовать с уровнем домена для выполнения действий.Модель ViewModel никогда не обращается напрямую к уровню данных.

Здесь мы передаем UseCaseHandler и два UseCase нашим ViewModel . Мы скоро рассмотрим это более подробно, но в этой архитектуре UseCase - это действие, которое определяет, как ViewModel взаимодействует с уровнем данных.

Вот как выглядит наш код Kotlin:

  класс PostListViewModel (
        val useCaseHandler: UseCaseHandler,
        val getPosts: GetPosts,
        val savePost: SavePost): ViewModel () {


    весело getAllPosts (userId: Int, обратный вызов: PostDataSource.LoadPostsCallback) {
        val requestValue = GetPosts.RequestValues ​​(userId)
        useCaseHandler.execute (getPosts, requestValue, объект:
        UseCase.UseCaseCallback  {
            переопределить удовольствие onSuccess (response: GetPosts.ResponseValue) {
                callback.onPostsLoaded (response.posts)
            }

            override fun onError (t: Throwable) {
                callback.onError (t)
            }
        })
    }

    весело savePost (сообщение: Сообщение, обратный вызов: PostDataSource.SaveTaskCallback) {
        val requestValues ​​= SavePost.RequestValues ​​(сообщение)
        useCaseHandler.execute (savePost, requestValues, объект:
        UseCase.UseCaseCallback  {
            переопределить удовольствие onSuccess (response: SavePost.ResponseValue) {
                callback.onSaveSuccess ()
            }
            override fun onError (t: Throwable) {
                callback.onError (t)
            }
        })
    }

}
  

Уровень домена

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

  абстрактный класс UseCase  {

    var requestValues: Q? = ноль

    var useCaseCallback: UseCaseCallback 

? = ноль внутренний забег () { executeUseCase (requestValues) } защищенное абстрактное развлечение executeUseCase (requestValues: Q?) / ** * Данные, переданные в запрос. * / интерфейс RequestValues / ** * Данные получены из запроса.* / интерфейс ResponseValue interface UseCaseCallback { весело onSuccess (ответ: R) fun onError (t: Throwable) } }

и UseCaseHandler обрабатывает выполнение UseCase . Мы никогда не должны блокировать пользовательский интерфейс при получении данных из базы данных или с нашего удаленного сервера. Это место, где мы решаем выполнить наш UseCase в фоновом потоке и получить ответ в основном потоке.

  class UseCaseHandler (private val mUseCaseScheduler: UseCaseScheduler) {

    fun  выполнить (
            useCase: UseCase , значения: T, обратный вызов: UseCase.UseCaseCallback ) {
        useCase.requestValues ​​= значения
        useCase.useCaseCallback = UiCallbackWrapper (обратный вызов, это)

        mUseCaseScheduler.execute (Runnable {
            useCase.run ()
        })
    }

    частное развлечение  notifyResponse (ответ: V,
                                                   useCaseCallback: UseCase.UseCaseCallback ) {
        mUseCaseScheduler.notifyResponse (ответ, useCaseCallback)
    }

    частное развлечение  notifyError (
            useCaseCallback: UseCase.UseCaseCallback , t: Throwable) {
        mUseCaseScheduler.onError (useCaseCallback, t)
    }

    частный класс UiCallbackWrapper  (
    частный val mCallback: UseCase.UseCaseCallback ,
    private val mUseCaseHandler: UseCaseHandler): UseCase.UseCaseCallback  {

        переопределить удовольствие onSuccess (ответ: V) {
            mUseCaseHandler.notifyResponse (ответ, mCallback)
        }

        override fun onError (t: Throwable) {
            mUseCaseHandler.notifyError (mCallback, t)
        }
    }

    сопутствующий объект {

        private var INSTANCE: UseCaseHandler? = ноль
        fun getInstance (): UseCaseHandler {
            if (INSTANCE == null) {
                ЭКЗЕМПЛЯР = UseCaseHandler (UseCaseThreadPoolScheduler ())
            }
            вернуть ЭКЗЕМПЛЯР !!
        }
    }
}
  

Как следует из названия, GetPosts UseCase отвечает за получение всех сообщений пользователя.

 
класс GetPosts (частный val mDataSource: PostDataSource):
UseCase  () {

    защищенное переопределение весело executeUseCase (requestValues: GetPosts.RequestValues?) {
        mDataSource.getPosts (requestValues? .userId?: -1, объект:
        PostDataSource.LoadPostsCallback {
            переопределить веселье onPostsLoaded (posts: List ) {
                val responseValue = ResponseValue (сообщения)
                useCaseCallback? .onSuccess (responseValue)
            }
            override fun onError (t: Throwable) {
                // Никогда не используйте общие исключения.Создавайте правильные исключения. С
                // наш вариант использования отличается, мы будем использовать универсальный throwable
                useCaseCallback? .onError (Throwable («Данные не найдены»))
            }
        })
    }
    класс RequestValues ​​(val userId: Int): UseCase.RequestValues
    class ResponseValue (val сообщения: List ): UseCase.ResponseValue
}
  

Назначение UseCase s - быть посредником между вашими ViewModel s и Repository s.

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

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

Уровень данных

Здесь есть все репозитории, которые может использовать уровень домена. Этот уровень предоставляет API источника данных внешним классам:

  interface PostDataSource {

    interface LoadPostsCallback {
        весело onPostsLoaded (сообщения: Список )
        fun onError (t: Throwable)
    }

    interface SaveTaskCallback {
        весело onSaveSuccess ()
        fun onError (t: Throwable)
    }

    весело getPosts (userId: Int, обратный вызов: LoadPostsCallback)
    fun savePost (сообщение: Сообщение)
}
  

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

  частный конструктор класса PostDataRepository (
        частный val localDataSource: PostDataSource,
        private val remoteDataSource: PostDataSource): PostDataSource {

    сопутствующий объект {
        private var INSTANCE: PostDataRepository? = ноль
        весело getInstance (localDataSource: PostDataSource,
        remoteDataSource: PostDataSource): PostDataRepository {
            if (INSTANCE == null) {
                ЭКЗЕМПЛЯР = PostDataRepository (localDataSource, remoteDataSource)
            }
            вернуть ЭКЗЕМПЛЯР !!
        }
    }
    var isCacheDirty = ложь
    переопределить удовольствие getPosts (userId: Int, обратный вызов: PostDataSource.LoadPostsCallback) {
        if (isCacheDirty) {
            getPostsFromServer (userId, обратный вызов)
        } еще {
            localDataSource.getPosts (userId, объект: PostDataSource.LoadPostsCallback {
                переопределить веселье onPostsLoaded (posts: List ) {
                    refreshCache ()
                    callback.onPostsLoaded (сообщения)
                }
                override fun onError (t: Throwable) {
                    getPostsFromServer (userId, обратный вызов)
                }
            })
        }
    }
    override fun savePost (post: Post) {
        localDataSource.savePost (сообщение)
        remoteDataSource.savePost (сообщение)
    }
    приватное развлечение getPostsFromServer (userId: Int, callback: PostDataSource.LoadPostsCallback) {
        remoteDataSource.getPosts (userId, объект: PostDataSource.LoadPostsCallback {
            переопределить веселье onPostsLoaded (posts: List ) {
                refreshCache ()
                refreshLocalDataSource (сообщения)
                callback.onPostsLoaded (сообщения)
            }
            override fun onError (t: Throwable) {
                Перезвоните.onError (t)
            }
        })
    }
    частное развлечение refreshLocalDataSource (posts: List ) {
        posts.forEach {
            localDataSource.savePost (это)
        }
    }
    private fun refreshCache () {
        isCacheDirty = ложь
    }
}
  

Код в основном не требует пояснений. Этот класс имеет две переменные: localDataSource и remoteDataSource . Их тип - PostDataSource , поэтому нас не волнует, как они на самом деле реализованы под капотом.

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

Когда я это сделал, все, что мне нужно было сделать, это изменить реализацию в RemoteDataSource . Мне не пришлось прикасаться ни к одному другому классу даже после такого огромного изменения. В этом преимущество развязанного кода. Изменение любого данного класса не должно влиять на другие части вашего кода.

Некоторые из дополнительных классов, которые у нас есть:

  interface UseCaseScheduler {

    весело выполнить (runnable: Runnable)

    fun  notifyResponse (ответ: V,
                                                   useCaseCallback: UseCase.UseCaseCallback )

    fun  onError (
            useCaseCallback: UseCase.UseCaseCallback , t: Throwable)
}


class UseCaseThreadPoolScheduler: UseCaseScheduler {

    val POOL_SIZE = 2

    val MAX_POOL_SIZE = 4

    val TIMEOUT = 30

    приватный val mHandler = Обработчик ()

    внутренняя переменная mThreadPoolExecutor: ThreadPoolExecutor

    в этом {
        mThreadPoolExecutor = ThreadPoolExecutor (POOL_SIZE, MAX_POOL_SIZE, TIMEOUT.toLong (),
                TimeUnit.SECONDS, ArrayBlockingQueue (POOL_SIZE))
    }

    переопределить удовольствие выполнить (runnable: Runnable) {
        mThreadPoolExecutor.execute (запускаемый)
    }

    переопределить удовольствие  notifyResponse (ответ: V,
                                                   useCaseCallback: UseCase.UseCaseCallback ) {
        mHandler.post {useCaseCallback.onSuccess (ответ)}
    }

    переопределить удовольствие  onError (
            useCaseCallback: UseCase.UseCaseCallback , t: Throwable) {
        mHandler.post {useCaseCallback.onError (t)}
    }

}
  

UseCaseThreadPoolScheduler отвечает за асинхронное выполнение задач с использованием ThreadPoolExecuter .

 
class ViewModelFactory: ViewModelProvider.Factory {


    переопределить fun  create (modelClass: Class ): T {
        if (modelClass == PostListViewModel :: class.java) {
            вернуть PostListViewModel (
                    Инъекция.provideUseCaseHandler ()
                    , Injection.provideGetPosts (), Injection.provideSavePost ()) как T
        }
        throw IllegalArgumentException ("неизвестный класс модели $ modelClass")
    }

    сопутствующий объект {
        частный var INSTANCE: ViewModelFactory? = ноль
        весело getInstance (): ViewModelFactory {
            if (INSTANCE == null) {
                ЭКЗЕМПЛЯР = ViewModelFactory ()
            }
            вернуть ЭКЗЕМПЛЯР !!
        }
    }
}
  

Это наш ViewModelFactory .Вы должны создать это, чтобы передать аргументы в конструктор ViewModel .

Внедрение зависимостей

Я объясню внедрение зависимостей на примере. Если вы посмотрите на наш класс PostDataRepository , он имеет две зависимости: LocalDataSource и RemoteDataSource . Мы используем класс Injection , чтобы предоставить эти зависимости классу PostDataRepository .

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

  инъекция объекта {

    fun providePostDataRepository (): PostDataRepository {
        вернуть PostDataRepository.getInstance (provideLocalDataSource (), provideRemoteDataSource ())
    }

    весело provideViewModelFactory () = ViewModelFactory.getInstance ()

    весело provideLocalDataSource (): PostDataSource = LocalDataSource.getInstance ()

    весело provideRemoteDataSource (): PostDataSource = RemoteDataSource.getInstance ()

    весело provideGetPosts () = GetPosts (providePostDataRepository ())

    весело provideSavePost () = SavePost (providePostDataRepository ())

    весело provideUseCaseHandler () = UseCaseHandler.getInstance ()
}
  

Примечание. Я предпочитаю использовать Dagger 2 для внедрения зависимостей в сложных проектах.Но с его чрезвычайно крутой кривой обучения, это выходит за рамки этой статьи. Так что, если вы хотите углубиться, я настоятельно рекомендую введение Хари Виньеша Джаяпалана в Dagger 2.

MVVM с чистой архитектурой: надежное сочетание

Наша цель в этом проекте состояла в том, чтобы понять MVVM с чистой архитектурой, поэтому мы пропустили несколько вещей, которые вы можете попытаться улучшить дальше:

  1. Используйте LiveData или RxJava, чтобы удалить обратные вызовы и сделать его немного более аккуратным.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *