koin
Koin
can bring easy dependency injection to android, standalone or web application projects. This tutorial will show how to use Koin
on your Android project.
Koin
可以为Android,独立或Web应用程序项目带来轻松的依赖项注入。 本教程将展示如何在您的Android项目中使用Koin
。
If you just want to download the code and check it by yourself, the repository is : https://github.com/lluzalves/KoinApp
如果您只想下载代码并自己检查,则存储库为: https : //github.com/lluzalves/KoinApp
But what is dependency injection anyways?
但是,依赖注入到底是什么?
An application can be composed of many objects that will eventually have to collaborate to perform tasks. When this happens, we call it a dependency.
一个应用程序可以由许多对象组成,这些对象最终将不得不协作来执行任务。 发生这种情况时,我们称其为依赖项。
Because of its dependencies, a class will have to obtain references to the required dependents objects and that will make it committed to a particular set of objects. Most of the time these required dependencies are passed through the constructor.
由于其依赖关系,一个类将必须获取对所需依赖对象的引用,这将使其提交给一组特定的对象。 大多数情况下,这些必需的依赖项通过构造函数传递。
When an application becomes more complex and grows in size it might have issues related to code reusability and an increasing need to apply refactor. To handle this kind of issue, we can make use of the dependency injection technique, by diverging usage and creation of dependencies.
当应用程序变得更加复杂且规模不断增长时,它可能会遇到与代码可重用性相关的问题,以及对应用重构的需求不断增加。 为了处理这种问题,我们可以使用依赖注入技术, 通过 发散依赖性的使用和创造。
Let’s add Koin to our project!
让我们将Koin添加到我们的项目中!
To add Koin to the project, open the build.gradle
file and add the following Koin dependency
and sync :
要将Koin添加到项目中,请打开build.gradle
文件并添加以下Koin dependency
并进行同步:
implementation "org.koin:koin-android:2.0.1"
testImplementation "org.koin:koin-test:2.0.1"
testImplementation "com.nhaarman:mockito-kotlin:1.5.0"
If the application is modularized, change implementation
to api
so you can access Koin
it in other modules, if required.
如果应用程序模块化,变更implementation
到api
,所以你可以访问Koin
它在其他模块中,如果需要的话。
Let’s write our first Koin module!
让我们编写第一个Koin模块!
Now, create a Kotlin file named ApplicationModules
, it will contain the dependencies we want to inject.
现在,创建一个名为ApplicationModules
的Kotlin文件,它将包含我们要注入的依赖项。
A Koin
module is a space to declare components. It allows you to provide a collection of objects that will persist with the entire context lifetime that it belongs to.
Koin
模块是用于声明组件的空间。 它允许您提供一个对象集合,这些对象将在其所属的整个上下文生命周期内保持不变。
To define a module use the module
block and declare the dependencies inside of it:
要定义模块,请使用module
块并在其中声明依赖项:
Now let’s initialize Koin!
现在让我们初始化Koin!
In case the project don’t have a custom Application class
, you will need to create one and declare it in your manifest file.
如果项目没有自定义Application class
,则需要创建一个并在清单文件中声明它。
Your custom App class should override OnCreate()
method and call startKoin()
, it is responsible to return a KoinApplication
instance that represents a Koin Container
configuration with all the modules and definitions that was declared in startKoin()
. Attention, startKoin()
should only be called once.
您的自定义App类应重写OnCreate()
方法并调用startKoin()
,它负责返回一个KoinApplication
实例,该实例代表具有在startKoin()
声明的所有模块和定义的Koin Container
配置。 注意, startKoin()
仅应调用一次。
androidLogger()
is used to set the Koin log level.
androidLogger()
用于设置Koin日志级别。
androidContext()
can be used to set the application Context
to the Koin Container
but also to inject it when necessary.
androidContext()
可以用于将application Context
设置为Koin Container
,也可以在必要时进行注入。
modules()
is used to set the modules that we have declared to be attached to the container.
modules()
用于设置我们声明要附加到容器的模块。
To finish this process open the Manifest
, and set the value of the property android:name
to your custom App class.
要完成此过程,请打开Manifest
,然后将android:name
属性的值设置为自定义App类。
Injection time!
注射时间!
For Koin
we can have 3 types of scope
. We can define it as:
对于Koin
我们可以使用3种类型的scope
。 我们可以将其定义为:
single
, which is a singleton
, this type of scope
will persist for the entire application lifetime.
single
,这是一个singleton
,这种类型的scope
将在整个应用程序生存期内持续存在。
factory
, for this type, Koin
will provide a new object for each time we require an instance.
factory
,对于这种类型,每次需要实例时, Koin
都会提供一个新对象。
scoped
, in this case, Koin
will provides an object that will persist as long the associated scope
lifetime exists.
scoped
,在这种情况下, Koin
将提供一个对象,只要关联的scope
生存期存在,该对象将持续存在。
In this tutorial, we will only talk about single
and factory
.
在本教程中,我们仅讨论single
和factory
。
Use case as single
.
用例为 single
。
The most common ways to resolve dependencies with Koin
is by: implementing the KoinComponent
interface or providing the required dependency through constructor injection
.
解决Koin
依赖关系的最常见方法是:实现KoinComponent
接口或通过constructor injection
提供所需的依赖关系。
Implementing the KoinComponent
interface is faster, it gives the class access to Koin
features like get()
and inject()
but we can solve this with a better approach by using constructor injection
.
实现KoinComponent
接口的速度更快,它使类可以访问Koin
功能,如get()
和inject()
但是我们可以使用constructor injection
使用更好的方法来解决此问题。
The constructor injection
approach makes the process simpler, because the class will only care about the dependencies it needs and not about who will satisfy them.
constructor injection
方法使过程更简单, 因为该类仅关心其所需的依赖关系,而不关心谁将满足它们。
It offers a more robust way to have consistent state, as soon as the class is constructed and not only when the field injection is completed and it makes testing easier because we are declaring the dependencies in the constructor.
一旦构造了类,它不仅提供了字段注入完成后的状态,而且提供了一种更可靠的状态一致状态,这使测试更加容易,因为我们在构造函数中声明了依赖项。
Approach 1 - Constructor injection.
方法1-构造函数注入。
Let’s say your application needs to store and provide data using SharedPreferences
, this class should be able to provide a way to put and retrieve data from a SharedPreferences
and it will also have a Context
dependency.
假设您的应用程序需要使用SharedPreferences
来存储和提供数据,该类应该能够提供一种方法来从SharedPreferences
放置和检索数据,并且它还具有Context
依赖性。
When we started Koin
, the Context
was injected and now can be retrieved by calling androidContext()
.
当我们启动Koin
, Context
被注入,现在可以通过调用androidContext()
进行检索。
We are going to declare a single
, and pass the Context
to the object constructor, that way Koin
can provide the AppPreferences
dependency that we want to use.
我们将声明一个single
,并将Context
传递给对象构造函数,这样Koin
可以提供我们要使用的AppPreferences
依赖项。
In our Activity
, Fragment
or Service
classes, it is easy to retrieve declared instances from Koin
modules. Theses classes have access to Koin
features like:
在我们的Activity
, Fragment
或Service
类中,很容易从Koin
模块中检索声明的实例。 这些类可以访问Koin
功能,例如:
by inject()
— lazy injected
by inject()
—延迟注入
get()
— eager injected
get()
—渴望注入
release()
— release module’s instance
release()
—释放模块的实例
getProperty() / setProperty()
— get/set property
getProperty() / setProperty()
—获取/设置属性
In this case, we will make use of by inject()
, to lazy inject the AppPreferences
.
在这种情况下,我们将使用by inject()
来延迟注入AppPreferences
。
Now it is possible to inject the AppPreferences
instance and use it to put or retrieve data from SharedPreferences
.
现在可以注入AppPreferences
实例,并使用它来放置或从SharedPreferences
检索数据。
Approach 2 - Implementing the KoinComponent
.
方法2-实现 KoinComponent
。
If AppPreferences
implements the KoinComponent
, it gives the class access to the same Koin
features that we have in the MainActivity
, so it is possible to use by inject()
to inject the Context
lazily in AppPreferences
.
如果AppPreferences
实现了KoinComponent
,它将为类提供与MainActivity
相同的Koin
功能的访问权限,因此可以by inject()
来将Context
延迟地注入AppPreferences
。
Use case as factory
.
用例作为 factory
。
While single
provides a unique instance during the entire application lifecycle, factory
will provide a new instance of the requested dependency every time it needs to be injected.
尽管single
在整个应用程序生命周期中提供了唯一的实例,但是factory
每次需要注入时都会提供所请求依赖的新实例。
Let’s improve our previous example by applying the MVP design pattern to make use of factory
.
让我们通过应用MVP设计模式来使用factory
来改进前面的示例。
Now that things are more clear, the Presenter
will be responsible to return the profile name and should notify the View
when everything is completed, the MainActivity
that implements the View
will be responsible to show the data and it will have the presenter lazily injected by Koin
.
现在,一切变得更清楚了, Presenter
将负责返回配置文件名称,并在完成所有操作后通知View
,实现View
的MainActivity
将负责显示数据,并且演示者将由Koin
延迟注入。
To declare the presenter dependency, we will use factory.
As long as a MainContract.MainView
is given, the factory
will return a new MainPresenter
instance. Add the following code to applicationModule
file :
要声明presenter依赖项,我们将使用factory.
只要给出MainContract.MainView
, factory
将返回一个新的MainPresenter
实例。 将以下代码添加到applicationModule
文件:
MainView
needs to be provided so Koin
can be able to inject the presenter in the MainActivity
, to do this use paramentersOf()
expression available in Koin
, it allows to indicate input arguments for the object constructor and it will pass the required view to the presenter factory
.
需要提供MainView
以便Koin
能够在MainActivity
注入演示者,为此可以使用Koin
可用的Koin
paramentersOf()
表达式,它可以指示对象构造函数的输入参数,并将所需的视图传递给演示者factory
。
Let’s Test.
让我们测试一下。
Let’s see if the presenter is behaving as expected. The test class should implement KoinTest
interface, so we can do our injections properly. Koin needs to be started before we run the tests.
让我们看看演示者的行为是否符合预期。 测试类应该实现KoinTest
接口,因此我们可以正确地进行注入。 在运行测试之前,需要启动Koin。
By using Mockito.verify()
we are checking if after calling presenter.onComplete()
any interaction happened to our view mock, specifically the showData()
method.
通过使用Mockito.verify()
我们检查调用presenter.onComplete()
之后是否对我们的视图模拟发生了任何交互,特别是showData()
方法。
Now just run the test and see the result. That’s it for now. Thanks for reading. :)
现在只需运行测试并查看结果。 现在就这样。 谢谢阅读。 :)
翻译自: https://medium.com/swlh/dependency-injection-with-koin-for-android-43dda4d800d1
koin