- a full stack enthusiast interested in Javascript / Typescript / Kotlin / Python / React / React Native / (Android & iOS) native / Cloud
- Dependency injection on Android with dagger-android and Kotlin
- 1. The big picture
- 2. Add dependencies
- 3. Let’s create our modules
- 4. Create an App component
- 5. Let’s create our custom application class
- 6. Time for connecting activity with this @Module
- 7. Let’s inject MainActivity
- 8. What about Activity scope dependency
- 9. Optimization
- 10. Repo
- 11. End
- koin android example — DI In Android
- Why Koin?
- How Koin Works ?
- Lines of code
- Here are the key functions.
- Koin Android Example
- 1. Add Dependencies
- 2. create network module
- create a retrofit instance
- Koin – это Dependency Injection или Service Locator?
- Dependency Injection
- Service Locator
- Koin — библиотека для внедрения зависимостей, написанная на чистом Kotlin
- Как управлять внедрением зависимостей с помощью механизма временной области (scope)
- О чем эта статья
- Введение
- Области в Koin
- Настраиваемая область
- Koin-Android
- Дополнительные материалы
a full stack enthusiast interested in Javascript / Typescript / Kotlin / Python / React / React Native / (Android & iOS) native / Cloud
Dependency injection on Android with dagger-android and Kotlin
In previous blog, we used plain dagger to do the dependency injection on Android, but there is another package from Google named dagger-android , and it’s tailed for Android. Things get more interesting here. Let’s see.
Here is another blog for mocking dagger in Espresso test if you need it.
If you don’t know what dagger is as well as dependency injection, and the some basic dagger terms like module , component seem secret to you. I strongly suggest you to read my other blog first, which is an implementation with plain dagger. Because that is easier to understand and just take you minutes. Then you can come back and see this blog for a more Android approach and to see which pattern you like most.
1. The big picture
When Google writes dagger-android , they want to reduce the boilerplate code you need to write with plain dagger . So they introduce some new abstraction. And it’s very easy to get lost here. So I think it might be better that we review this base pattern first. As I said in the previous blog. In order to do DI, you need to prepare these initialization of dependencies somewhere before you can use them. So here, in dagger’s terms:
- You declare how to generate these dependencies in @Module .
- You use @Component to connect the dependencies with their consumers.
- Then inside the consumer class. You @inject these dependencies. dagger will create the instances from the @Module
2. Add dependencies
3. Let’s create our modules
First, let’s create an application wide @Module .
This @Provides is the provider for any consumer which has the @Inject decorator. Dagger will match the type for you. Which means when a @Inject asks for the type SharedPreferences , dagger will find through all @Provide and find a method which return type matches and get that instance from this method.
But something interesting here is that who will give that app parameter when calling this provideSharedPreference() method and where does it get it. Well, we will see that soon.
4. Create an App component
Now consider this is an application wide dependency, we will connect this @Module to the android application with a @Component . For those who has React or Vue experiences, it’s not that component at all. 😀
Here, something different from the plain dagger is that this interface is based on another interface : AndroidInjector . And needs to declare its Builder as well. The App here is your custome application class. We will create that later on.
This Builder is for dagger, so it knows how to create() it. For instance, in our example, we need to the caller to pass app when create() . No magic here, as the signature indicates, you have to pass it when you first invoke this method.
@BindsInstance is where it shines. It will take this incoming app parameter, and save it for using later, such that, in our previous AppModule , you can invoke provideApplication() with a parameter called app . And this is how it gets that parameter. Because the app has been @BindsIntance when the AppComponent.Builder first create() .
AndroidSupportInjectionModule::class is from dagger to inject into other types other than your App .
And good question here will be why this is an interface , well, because overall, dagger is more like a code generation than a library. It’s an interface because dagger will generate the actual implementation for you as long as you declare all the things they want and in their way.
5. Let’s create our custom application class
This is your normal custom Application class.
Don’t forget to add this App to the AndroidManifests.xml with android:name= attribute to enable it.
Something interesting is that we extend the App from DaggerApplication to reduce some boilerplate like before, the only thing you need to do is to override that applicationInjector method, initialize and return your AppComponent there.
then you call the create() method which you created in that Builder interface, passing this which just fits the signature: fun create(app: Application):Builder .
The DaggerAppComponent will be unresolved until you run Make Project from the Build menu.
If you don’t want to inherit from DaggerApplication , you have to implement the HasActivityInjector interface:
You need to know that DaggerApplication does much more things for you other than the several lines of boilerplate above. It handles things like Service and BroadCastReceiver to make your life easier in the future.
6. Time for connecting activity with this @Module
Create a new file ActivitiesBindingModule.kt with the following code:
If you want to add more activities, just add them here use the same pattern.
Now connect the ActivitiesBindingModule into the AppModule .
If you went through my previous blog, you will notice this part is different. You no longer declare a method: fun inject(activity: MainActivity) . You need to use a ActivitiesBindingModule to do the trick. But still you can write component for that activity. this is just easier.
7. Let’s inject MainActivity
It’s more clean than before, you use @Inject , then you get the it. No more (application as MyApp).myAppComponent.inject(this) .
Run the app, you should see something like this in the console:
The magic could happen only because we inherited from DaggerAppCompatActivity , other wise you need to call AndroidInjection.inject(this) in the onCreate by yourself.
8. What about Activity scope dependency
Let’s see that you need some dependencies that is only for one activity. Here for example, we need one such thing for the MainActivity .
Then we just inject in and use it in MainActivity.kt
Where to get this abcKey initialized? Well, we create a MainActivityModule :
And connect it with the ActivitiesBindingModule :
Run the app, you should see value of abcKey: false printed in the console.
Highlight
In that provideABCKey(preference:SharedPreferences) , it needs a SharedPreferences . How could dagger get it?
Well, with all the setup, dagger has a graph of all your dependencies. And every time it needs a parameter in a @Provides function, it will check other @Provides functions to look for that type. In our case, it will find it from the provideSharedPreference() and get it from there. Much better, it’s a singleton! No new instance created!
And this is a very important feature, remember the moments where you need to create an instance A in order to create instance B. And order for initialization matters only because there is a dependency chain? Well, they are all on dagger now. 🙂
9. Optimization
When we wrote that MainActivityModule class. Anytime when dagger tries to get that instance, it needs to create an instance of MainActivityModule first. That is not very good. We can make it better by make that @Provides function static. In Kotlin, the syntax would be:
Now, the dagger could invoke this method like this: MainActivityModule.provideABCKey() without the need of a new instance.
Why make the class abstract ? Well, this is for a better check, because now if any @Provides in this module is non-static, dagger will give you a compile time warning.
Furthermore, you can move this code into MainActivityModule class as well:
Then, you can remove that ActivitiesBindingModule.kt . But add this MainActivityModule to the AppComponent . Such that, everything belongs to MainActivity DI now resides in one place.
Which approach do you like? 🙂 Sometimes, some activities may only want an application-wide injection. Which seems too much to create a @Module for it. Maybe a combination of both? But that means sometimes, something is here and sometimes something is there. So, might be better just use the current setup. 🙂 But making methods static . What do you think? 🙂
10. Repo
You can find the repo here
11. End
Thanks for reading!
Follow me (albertgao) on twitter, if you want to hear more about my interesting ideas.
Источник
koin android example — DI In Android
Working with dependency injection is one complex concept in android dependency injection. Because Dagger-2 had complicated the concept for me when I came into android.
In this tutorial,I have explained about another dependency injection framework that I recently adopted and how it has really simplified the process for me. That is Koin.
A pragmatic lightweight dependency injection framework for Kotlin developers. Written in pure Kotlin using functional resolution only: no proxy, no code generation, no reflection!
Office Website — https://insert-koin.io/
Why Koin?
Koin is easy, simple & well documented. It is perfect for encapsulated feature/library modules.
How Koin Works ?
The koin works on simple DSL model. Here we have to create module first. In this module all the dependent objects are there, then we have to load one or more modules in koin. Then, we are ready to use this object. Generally we are loading module in to koin in application class by calling startKoin method,then we can inject the object wherever we want,this how the Koin works.
With Koin, setting up dependency injection is kind of a manual process, you essentially plug your services into the module graphs and those dependencies will later get resolved at runtime.
Lines of code
Here are the key functions.
get() — is used inside constructors to resolve needed instances. I advise using named parameters to achieve a better definition & improve readability.
factory — is used to indicate that a new instance must be created each time it is injected.
single — indicates that a unique instance will be created at the beginning and shared on every future injection.
repositoryModule — contains examples of binding interfaces (domain’s repo interface with its actual implementation) you can read more here.
name — is used to name definitions. This is required when you want to have multiple instances of the same class with different types.
bind — additional Kotlin type binding for given bean definition
getProperty() — resolve a property
module — create a Koin Module or a submodule (inside a module)
viewModel — definition, and this enables you to define a viewModel dependency.
Scope — Koin’s scope allows you to define local scoped-dependencies that are short-lived, like a Presenter, View Adapter, or other service components that are meant to be short-lived within a defined scope, like an Activity’s scope.
Koin Android Example
1. Add Dependencies
Add dependencies to app build.gradle file.
2. create network module
create a retrofit instance
To learn more about Retrofit please check this.
Источник
Koin – это Dependency Injection или Service Locator?
В Android-разработке для DI традиционно используют Dagger 2, очень мощный фреймворк с кодогенерацией. Но есть проблема: новичкам сложно его использовать. Сами принципы DI просты и понятны, но Dagger усложняет их. Можно жаловаться на поголовное падение грамотности программистов, но от этого проблема не исчезнет.
С появлением Kotlin появилась возможность писать удобные вещи, которые были бы практически невозможны с использованием Java. Одной из таких вещей стал Koin, который является не DI, а Service Locator, который многими трактуется как anti-pattern, из-за чего многие принципиально его не используют. А зря, ведь у него очень лаконичный API, упрощающий написание и поддержку кода.
В данной статье я хочу помочь новичкам разобраться с разграничением понятий Dependency Injection и Service Locator, но не с самим Koin.
Dependency Injection
Прежде всего, что такое Dependency Injection? Простыми словами, это когда объект принимает зависимости извне, а не создаёт или добывает их сам. Приведу пример. Предположим, у нас есть интерфейс Engine , его реализация, а также класс Car , который зависит от Engine . Без использования DI это может выглядеть вот так
Если переписать класс Car с использованием подхода DI, то может получиться вот это:
Всё просто – класс Car не знает, откуда приходит реализация Engine , при этом заменить эту самую реализацию легко, достаточно передать её в конструктор.
Service Locator
Попробуем разобраться с Service Locator. Тут тоже ничего сложного – это некий реестр, который по запросу может предоставить нужный объект. Пока я предлагаю отойти в сторону от Koin и представить некий абстрактный ServiceLocator, без деталей реализации, но с простым и понятным API.
У нас есть возможность добавить в наш реестр некую зависимость, а также получить эту зависимость. Вот пример использования с нашими двигателями и машинами:
Это отдалённо похоже на DI, ведь класс Car получает зависимость извне, не зная о реализации, но у данного подхода есть проблема – мы ничего не знаем о зависимостях класса Car , есть ли они вообще. Можно попробовать такой подход:
Теперь мы знаем, что у Car есть зависимости, но всё ещё не знаем какие. Т.е. это не решение нашей проблемы. Но есть ещё один вариант:
Это и есть Dependency Injection в чистом виде. С Koin это бы выглядело вот так:
Увы, нам всё ещё необходимо обращаться к Koin для получения зависимостей, но это никоим образом не противоречит принципам Dependency Injection.
UPDATE. По просьбе kranid приведу максимально простой пример на Dagger 2.
Источник
Koin — библиотека для внедрения зависимостей, написанная на чистом Kotlin
Как управлять внедрением зависимостей с помощью механизма временной области (scope)
Для будущих студентов курса «Android Developer. Professional» подготовили перевод полезной статьи.
Также приглашаем принять участие в открытом вебинаре на тему «Пишем Gradle plugin»
О чем эта статья
Вы узнаете, как с помощью модулей Koin ограничивать область живучести зависимостей, относящихся к конкретному компоненту. Вы также познакомитесь со стандартными областями Koin и способами работы с настраиваемыми областями.
Введение
Разработчики ОС Android не рекомендуют использовать внедрение зависимостей (Dependency Injection, DI (англ.)), если в вашем приложении три экрана или меньше. Но если их больше, лучше применить DI.
Популярный способ реализации DI в Android-приложениях основан на фреймворке Dagger. Но он требует глубокого изучения. Одна из лучших альтернатив этому фреймворку — Koin, библиотека, написанная на чистом Kotlin.
Если вы уже пользовались Dagger или любой другой библиотекой для DI, то, скорее всего, знаете, насколько важен в этом процессе механизм временной области (scope). Он позволяет определять, в каких случаях нужно получать один и тот же зависимый объект, а в каких — новый. Он также помогает освобождать невостребованные ресурсы и память.
Области в Koin
Концепция области в Koin аналогична таковой в Android. Она позволяет, например, ограничить область живучести модели представления (ViewModel) до определенной активности и использовать эту модель во фрагментах, которыми наполняется активность.
Как правило, в Koin три вида временных областей.
single (одиночный объект) — создается объект, который сохраняется в течение всего периода существования контейнера (аналогично синглтону);
factory (фабрика объектов) — каждый раз создается новый объект, без сохранения в контейнере (совместное использование невозможно);
scoped (объект в области) — создается объект, который сохраняется в рамках периода существования связанной временной области.
Одиночный объект(single). Фабрика объектов(factory)
Область вида single при каждом запросе возвращает один и тот же экземпляр, а factory каждый раз возвращает новый экземпляр.
Настраиваемая область
Стандартные области single и factory в Koin живут в течение жизненного цикла модулей Koin. Однако в реальных сценариях использования требования к внедрению зависимостей будут отличаться.
Зависимости обычно нужны только на определенный период времени. Например, репозиторий OnBoardRepository в Android-приложении требуется только при регистрации пользователя. Как только пользователь авторизуется, удержание этого репозитория в памяти станет лишней тратой ресурсов.
Чтобы добиться нужного поведения в Koin, можно воспользоваться API для работы с временными областями. В модуле Koin можно создать область со строковым квалификатором и объявить зависимости внутри нее с помощью уникальных квалификаторов. Давайте сделаем это шаг за шагом.
Шаг 1
Сначала создадим модуль, объявим пустую область и присвоим ей имя. В данном случае мы дали области имя CustomScope . Вы можете назвать ее в соответствии со своими требованиями. Вот как это выглядит:
Шаг 2
Следующим шагом объявим необходимые зависимости с использованием областей single и factory в соответствии с требованиями проекта. Ключевой момент заключается в присвоении областям уникальных квалификаторов. Вот так:
Шаг 3
Мы закончили настройку в модуле Koin. На этом шаге нам нужно создать область из того компонента, из которого мы импортируем нужные зависимости. Обычно области создаются из Android-компонентов, например Activity, Fragment и т. п.
Чтобы создать область, сначала нам нужно получить существующий экземпляр компонента Koin, а затем вызвать функцию createScope, передав ей идентификатор и имя области.
Получив CustomScope как значение параметра имени, Koin будет искать область, которую мы объявили под этим именем в модулях Koin. ScopeNameID — это идентификатор, который мы применяем, чтобы отличать одну область от другой. Он используется на внутреннем уровне в качестве ключа для поиска этой области.
Если вы обращаетесь к областям или создаете их из нескольких Android-компонентов, то вместо функции createScope рекомендуется использовать функцию getOrCreateScope . Из названий этих функций очевидно, что они делают.
Шаг 4
Наконец, создаем экземпляр зависимости, которую хотим использовать. Мы сделали это с помощью созданной нами области. Вот что получилось.
scopedName и factoryName — это квалификаторы, которые мы объявили внутри модуля Koin на шаге 2.
Шаг 5
Чтобы избавиться от зависимостей, созданных посредством stringQualifiedScope, в частности sampleclass , необходимо вызвать функцию close . Например, если вы хотите избавиться от созданных в этой области зависимостей при уничтожении активности, то нужно вызвать функцию close в рамках соответствующего метода onDestroy . Вот так:
Koin-Android
Выше описан общий подход к ограничению живучести зависимостей определенной временной областью. Его можно применять на любой платформе, поддерживаемой Koin. Будучи Android-разработчиком, теперь я бы хотел совместить механизмы области Koin и области жизненного цикла, чтобы свести к минимуму работу, которую мне приходится делать при каждом создании активности.
Для этого необходимо импортировать библиотеки Koin-Android. Добавьте следующие строки в узел dependencies файла build.gradle уровня приложения:
Теперь с целью сократить шаблонный код мы хотим, например, автоматически закрывать область в рамках метода onDestroy компонента Android. Это можно сделать путем привязки Koin к импорту зависимостей посредством lifecyclescope .
Для начала необходимо создать в модуле Koin область для зависимостей с компонентами Android. Как это сделать:
scoping dependency with android activity
Затем нужно выполнить внедрение зависимости в активности при помощи lifecyclescope :
Это позволит закрывать область при уничтожении активности, избавив нас от ручных операций. Вот так выглядит наш код:
Такой подход позволит автоматизировать работу по созданию областей, назначению квалификаторов и уничтожению областей. Кажется, что это простые действия, их можно выполнить и вручную. Но повторяющуюся работу важно автоматизировать, и по мере развития приложения это становится очевидно.
Дополнительные материалы
Подробнее о внедрении зависимостей читайте в другой статье о библиотеке Koin.
На этом все. Надеюсь, вы узнали что-то полезное для себя. Спасибо за внимание!
Подробнее о курсе «Android Developer. Professional». Записаться на открытый урок «Пишем Gradle plugin» можно здесь.
Источник