Mvvm android kotlin hilt

Android Architecture: MVVM with Coroutines + Retrofit + Hilt + Kotlin Flow + Room

Nov 2, 2020 · 5 min read

W hen you are starting a new Android App the first thing you want is to have the best architecture for your App. Ideally, you would want your architecture to be built with the best development trends in Android.

The quest for the perfect architecture starts by researching and learning top architecture trends. As the MVVM being the officially recommended architecture, it is an ideal choice to make.

There are many articles on buildi n g MVVM architecture using LiveData, ViewModel, Room, Retrofit, Coroutines, Dagger, RxJava, repository pattern, and the single source of truth strategy, etc. But, when it comes to putting all pieces together, things get tricky.

The official Android Architecture blueprint provides excellent MVVM architecture samples. But, the samples don’t use any networking library(e.g. Retrofit) library and instead mocks the Remote Data sources.

Also, I came across this article on why you should not be using LivData in repositories ‘No more LiveData in your repository’. And the architecture blueprint samples use the LiveData in repositories.

Motivation

Ideally, you would want to build something very scalable yet very concise, easy to understand, and simple to implement. Thanks to the introduction of the new official members, Kotlin Coroutines, Kotlin Flow, and Android Hilt.

We will build an MVVM architecture using all these concepts. Let’s get started.

The best way to learn is to implement it. So, we will be building a sample app that uses the TMDB API to show a list of trending movies. The detail screen shows details about the selected movie.

You can find the complete source code of this sample project on GitHub.

devnarendra08/DemoTMDB

You can’t perform that action at this time. You signed in with another tab or window. You signed out in another tab or…

Let’s get started!

Model

We don’t need to create the Factory for our ListingViewModel. Hilt handles all the pain. Refer.

The listing screen observes the viewModel.movieList LiveData exposed by the ViewModel and feeds the data to the MovieAdapter to display.

Every response is wrapped inside the Result class, which holds the 3 different statuses of the response(SUCCESS, ERROR, LOADING).
The data property holds the success value in case of successful response and error and message properties in case of failure.

ViewModel

The Hilt will inject movieRepository dependency in the constructor.

Kotlin Coroutines makes it super easy to do the IO work. fetchMovies() launches a Coroutine in the viewModelScope provided by the ViewModel KTX library.
You don’t need to worry about canceling the operations when ViewModel goes away. The viewModelScope will manage everything for you. You can read more about Coroutines scopes and Structured concurrency here.

movieRepository.fetchTrendingMovies() returns the Flow object. The collect will be invoked when any value is emitted to the Flow. We will learn more about Flow in the next section.

Repository
The repository will be responsible to provide the data either from the Remote or Local data sources.

flow<> builder constructs the Flow object. The Flow exposes the data as a stream like RxJava. The flowOn(Dispatchers.IO) specifies the Coroutine context for the execution. The emit() will invoke the collect() in our ViewModel. Here we will emit 3 different values.

emit(fetchTrendingMoviesCached()) emits the cached values from the Room database data.
emit(Result.loading()) emits the loading state.
emit(result) emits the result from the API response.

Читайте также:  Удаленное управление андроид с компьютера через teamviewer

We will cache the data when the response is successful.

Note: The approach that we have used in this sample app is less complex and gives you control over the Loading status. But, If you really want a more clean Single Source of the Truth strategy for your data, you can easily do that by returning Flow from your Room Dao. The Room library already has nice support for this.

Remote Data Source
The remote data source will be responsible for fetching the data from the REST APIs.

The MovieRemoteDataSource uses the Retrofit library to fetch the data from the TMDB REST APIs. Hilt injects the required dependency in the constructor.

getResponse is the Higher-Order Kotlin function which accepts the Retrofit Service function as the method parameter and returns the Retrofit response wrapped inside Result class. This makes our code concise.

Retrofit already has Coroutine suspend function support. We just need to add the suspend keyword in our service interface.

Local Data Source: Room Dao
The local data source will be responsible for storing and fetching the data from the Room Database.

Hilt: Dependency Injection
The Hilt is built on top of the Dagger and it is very less boilerplate and it is part of the Jetpack libraries and easily integrates with other Jetpack libraries.

The only dependencies we need to construct is the Retrofit and the Room database. Which is pretty straight forward.

You can read more about using Hilt in your App from here.

That’s it. We are done!
You can find the complete source code of the sample project on GitHub.

devnarendra08/DemoTMDB

You can’t perform that action at this time. You signed in with another tab or window. You signed out in another tab or…

I haven’t implemented pagination in the listing screen to keep the sample app very small and simple to understand.

We returned everything as Flow from our Repository. But, if you just need one shot operation, then the same can be achieved by simply using the suspend function. You can learn more from here.
In our case, Flow helps us to emit the statuses along with the success and error response.

Disclaimer
We must first understand the core concept of any new approach or library before randomly putting everything together.

Summary

  • We learned how to put all new Android concepts together and make it work seamlessly.
  • We learned how we can use Kotlin Flow/suspend function to make your app concise and readable.
  • We learned how Hilt Dependency Injection makes your code less boilerplate and testable.
  • We learned how to use Retrofit with Coroutines and wrap the response with success, failure, and loading states.

Please let me know your valuable inputs in the comments.

I would appreciate Claps if you find this article useful. Your Claps will keep me motivated to continue doing the good work.

Источник

Mvvm android kotlin hilt

Android Architecture MVVM Kotlin Room Dagger2 + Hilt and Pagging 3

This example demonstrates MVVM architecture implementation in Android using different architecture components and DI framworks like Dagger and Hilt. In its different branches you will find the different implementation with different libraries and frameworks.

  • Kotlin Coroutines for background operations.
  • The Navigation component to manage fragment operations.
  • MVVM architecture to saperate presentation layer (View) and business logic (ViewModel)
  • Reactive UIs using LiveData observables and Data Binding.
  • A data layer with a repository and data sources.

This project hosts different sample app in separate repository branches.

Sample Description
master A sample MVVM app that using architecture components.
Uses Kotlin, Architecture Components, Data Binding, Room, etc. Example follows SOLID principles and includes management of local and remote data through DataManager class.
feature-di-hilt An application using MVVM architacture to demonstrate paging 3 library. API:- rick and morty api. It uses Kotlin, Coroutines, LiveData, Retrofit, Navigation Components, Room for page caching etc. Application display page from network and database using RemoteMediator. For more information check this.

About

Android MVVM architecture example in kotlin, pagination example using paging 3 lib with Hilt as DI framework. data-binding library,Room and dagger-2 example in master branch.

Источник

Практическое руководство по использованию Hilt с Kotlin


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

Простой способ использовать внедрение зависимостей в приложениях для Android

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

Настройка Hilt

Чтобы настроить Hilt в своем приложении, сначала выполните указания из руководства: Установка Gradle Build.

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

Определение и внедрение зависимостей

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

Классы, имеющие зависимости, которые вы собираетесь внедрить.

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

Они не являются взаимоисключающими: во многих случаях класс одновременно является внедряемым и имеет зависимости.

Как сделать зависимость внедряемой

Чтобы в Hilt сделать объект внедряемым, необходимо указать для Hilt способ создания экземпляра этого объекта. Такие инструкции называются привязками.

Есть три способа определения привязки в Hilt.

Добавить к конструктору аннотацию @Inject

Использовать @Binds в модуле

Использовать @Provides в модуле

Добавление к конструктору аннотации @Inject

У любого класса может быть конструктор с аннотацией @Inject , что позволяет использовать его в качестве зависимости в любом месте проекта.

Использование модуля

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

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

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

Модули устанавливаются в компонент Hilt, который указывается с помощью аннотации @InstallIn . Я дам более подробное объяснение ниже.

Вариант 1: использовать @Binds, чтобы создать привязку для интерфейса

Если вы хотите использовать в своем коде OatMilk, когда требуется Milk, создайте абстрактный метод внутри модуля и задайте ему аннотацию @Binds . Обращаю внимание, чтобы этот вариант работал, сам OatMilk должен быть внедряемым. Для этого его конструктору необходимо задать аннотацию @Inject .

Вариант 2: создать функцию-фабрику с помощью @Provides

Когда экземпляр нельзя сконструировать напрямую, можно создать поставщик. Поставщик — это функция-фабрика, которая возвращает экземпляр объекта.

В качестве примера можно привести системную службу, скажем ConnectivityManager, которую необходимо получить из контекста.

Объект Context является внедряемым по умолчанию, если задать ему аннотацию @ApplicationContext либо @ActivityContext .

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

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

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

⮕ Как параметры конструктора

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

⮕ Как поля

Если класс является точкой входа, указанной с использованием аннотации @AndroidEntryPoint (подробнее об этом рассказано в следующем разделе), внедрены будут все поля, помеченные аннотацией @Inject .

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

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

Прочие важные понятия

Точка входа

Помните, я сказал, что во многих случаях класс создается путем внедрения и имеет зависимости, внедренные в него? В некоторых случаях у вас будет класс, который не создается путем внедрения зависимости, но имеет зависимости, внедренные в него. Хорошим примером этого являются активности, которые в стандартной ситуации создаются платформой Android, а не библиотекой Hilt.

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

⮕ Точка входа Android

Большинство ваших точек входа будут так называемыми точками входа Android:

Если это так, такой точке входа следует задать аннотацию @AndroidEntryPoint .

⮕ Другие точки входа

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

ViewModel

ViewModel — это особый случай: экземпляры этого класса не создаются напрямую, так как они должны создаваться платформой, при этом он также не является точкой входа Android. Вместо этого с классами ViewModel используют специальную аннотацию @ViewModelInject , которая позволяет Hilt внедрять в них зависимости, когда они создаются с помощью выражения by viewModels() . Это похоже на то, как @Inject работает для других классов.

Если вам требуется доступ к состоянию, сохраняемому в классе ViewModel , внедрите SavedStateHandle в качестве параметра конструктора, добавив аннотацию @Assisted .

Чтобы использовать @ViewModelInject , вам нужно будет добавить еще несколько зависимостей. Дополнительные сведения см. в статье: Интеграции Hilt и Jetpack.

Компоненты

Каждый модуль устанавливается внутри компонента Hilt, который указывается с помощью аннотации @InstallIn ( ). Компонент модуля используется главным образом для предотвращения случайного внедрения зависимости не в том месте. Например, аннотация @InstallIn ( ServiceComponent.class ) не позволит использовать привязки и поставщики, имеющиеся в соответствующем модуле, внутри активности.

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

Области

По умолчанию у привязок нет областей. В приведенном выше примере это означает, что каждый раз, когда вы внедряете Milk, вы получаете новый экземпляр OatMilk. Если добавить аннотацию @ActivityScoped , область использования привязки будет ограничена пределами ActivityComponent .

Теперь, когда у модуля есть область действия, Hilt будет создавать только один OatMilk на экземпляр активности. Кроме того, этот экземпляр OatMilk будет привязан к жизненному циклу этой активности — он будет создаваться при вызове onCreate() активности и уничтожаться при вызове onDestroy() активности.

В этом случае и milk, и moreMilk будут указывать на один и тот же экземпляр OatMilk. Однако, если у вас несколько экземпляров LatteActivity, у каждого из них будет собственный экземпляр OatMilk.

Соответственно, у других зависимостей, внедренных в эту активность, будет та же область, поэтому они тоже будут использовать тот же экземпляр OatMilk:

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

Область также определяет жизненный цикл внедренных экземпляров: в данном случае одиночный экземпляр Milk, используемый Fridge и LatteActivity, создается, когда onCreate() вызывается для LatteActivity, — и уничтожается в его onDestroy() . Это также означает, что наш Milk не «переживет» изменение конфигурации, поскольку при этом вызывается onDestroy() для активности. Преодолеть это можно путем использования области с более длительным жизненным циклом, например @ActivityRetainedScope .

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

Внедрение поставщика

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

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

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

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

Прямо сейчас в OTUS стартовала новогодняя распродажа. Скидка распространяется абсолютно на все курсы. Сделайте подарок себе или близким — переходите на сайт и забирайте курс со скидкой. А в качестве бонуса предлагаем зарегистрироваться на абсолютно бесплатные демо-уроки :

Источник

Читайте также:  Google now для android
Оцените статью