今年呆在家中实在无聊,外面太危险了,还是在家学习比较安全可持续。

过年期间,我又复习了几遍依赖注入框架Dagger.

诶,什么是依赖注入?

说白了就是降低跟类对象之间的耦合,当需要修改类对象的时候,能不修改被依赖的实例.其实我们平常就用到了很多的依赖注入,比如一个类的构造函数有一个参数,你new该类时要传一个参数,这就是一个注入,又比如类中常用的set()方法,这也是一个注入.

这时就会有同学问了:那既然我们平常都有用到,为什么还要引用第三方的注入控件?

那如果某一天,该类的构造函数中,需要再加一个参数怎么办,少量该类的实例修改比较方便,如果你持有该类的实例有100个呢,那如果一个个去修改地话,就非常不现实.又或者几个月后,该类的构造参数又增加了一个怎么办?所以我们就引用了依赖注入控件.

说回Dagger,了解过Dagger的同学都知道,其实Dagger本身并不难(java适用部分)(狗头),Dagger当初就是为了方便Java开发而设计的,但是在Android中的适用就不太友善了,后来为了更加符合Android的风格,又有了DaggerAndroid部分的api(主要新增了很多标签),这两者结合,学习起来就很难,而且成本高,而且难。我废了九牛二虎之力终于弄懂Dagger之后(狗头),无意间发现了Koin这个框架,我的天,感觉像打开了新世界的大门,那么多天Dagger的东西都白学了。

好了废话不多说,开始进入主题。代替Dagger的注入框架-----Koin(点我看官网地址)

什么是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!

(这年头没点英文也配叫专业(狗头))

Koin是适用于Kotlin的轻量级注入工具,它的特点是:无代理,无代码生成,无反射。(Dagger时不时就要build,而且会产生大量复杂的代码)

(我一开始以为koin是Kotlin团队开发的,一看koin跟Kotlin这么像(狗头))

那接下来,怎么使用koin这个库,官网上也写的很清楚。


如果你是支持AndroidX的,那就使用

这边对这些地址大致介绍下,里面有个坑

koin-android:这个是核心
koin-androidx-scope:这个是作用域相关,通俗点讲就是数据D在A界面有用,在B界面无效
koin-androidx-viewmodel:这个是viewmodel相关的
koin-androidx-fragment:fragment相关的,不过这边有点问题,官网上说在2.1.0-alpha-3才加入这个库,但是我依赖之后发现该库报错

接下来再附上我demo的github地址和图片,东西很简单,一条条列出来,大家可以看得全面清晰点.

https://github.com/CaesarShao/CSKoin

1.初始化

2.Factory用法

3.Single用法

4.viewModel用法

5.带参的构造函数使用

–5.1带多个参数的常规使用

–5.2 qualifier–限定符使用

–5.3 get()中使用qualifier

–5.4声明进样参数—构造参数从外面传入

6.KoinComponent接口,普通类中怎么使用注入对象

7.跨Module模块注入依赖

8.Scope–作用域的使用

9.fragment模式地使用

10.其他

–10.1Properties–调用资产中的数值

–10.2在startkoin之外,加载module(适用于组件化开发)

1.在Application中初始化

开始,接下来我们来看一下用法.在调用之前,我们需要在Application中,初始化一下koin.

class MyApp : Application() {override fun onCreate() {super.onCreate()startKoin {//开始启动koinandroidContext(this@MyApp)//这边传Application对象,这样你注入的类中,需要app对象的时候,可以直接使用modules(appModule)//这里面传各种被注入的模块对象,支持多模块注入}}val appModule = module {//里面添加各种注入对象}
}

通过调用startKoin来启动koin,里面填注入对象.

接下来,我们来看一下,在实际项目中怎么使用,主要分为3大类,Factory,Single,viewmodel,我们来一个个了解

2.普通注入使用方式–Factory注入

Factory注入方式跟普通new一个对象一毛一样.

class Person {fun speak() {CSKoinLog.I("武汉加油,中国加油")}
}

首先我们创建一个Person类,然后在我们的MyApp中的appModule中,将该Person类注入一下

class MyApp : Application() {...val appModule = module {//里面添加各种注入对象factory {//普通的注入方式Person()}}
}

大家可以看到就很像new了一个新的对象一样,好,注入完了之后,就可以在Activity中调用了,我在FactoryActivity中调用它.

class FactoryActivity : AppCompatActivity() {//调用方式有大致下面几种,后面会再说到val person: Person by inject()//方法一val person2 by inject<Person>()//方法二override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_factory)val person3 = get<Person>()//方法三person.speak()person2.speak()person3.speak()CSKoinLog.I(person.hashCode().toString())CSKoinLog.I(person2.hashCode().toString())CSKoinLog.I(person3.hashCode().toString())}
}

通过by inject(),get<>()这几种方法就可以获取被注入的对象,从日志上就可以看到,每次调用之后,都会生成一个新的对象,是不是很简单

3.单例模式–Single用法

Koin支持调用单例的方法,而且调用起来非常简单,也是在MyApp中的appModule中注入,不过这次注入方式为single

class UserData {var userName: String? = nullvar age: Int? = nullfun info() {CSKoinLog.I("用户名:" + userName + "年龄:" + age)}
}

我新建了一个UserData类,该类中有几个属性,和一个输出所有属性的方法,在application的appModule中,将该类注入一下.

val appModule = module {//里面添加各种注入对象...single {//单例的注入方式UserData()}}

然后我们在SingleActivity中调用

class SingleActivity : AppCompatActivity() {val userData: UserData by inject()val userData2: UserData by inject()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_single)CSKoinLog.I(userData.hashCode().toString())CSKoinLog.I(userData2.hashCode().toString())userData.userName = "张飞"userData.age = 17userData2.info()userData2.userName = "关羽"userData2.age = 18userData.info()}
}

从打印的日志中,我们可以看到,2个UserDate的对象的地址位是同一个,然后一个属性改变之后,另一个也对应地改变.

4.viewModel用法(官方原文)

our declared component must at least extends the android.arch.lifecycle.ViewModel class. You can specify how you inject the constructor of the class and use the get() function to inject dependencies.(时不时来句英文,尽显专业)

koin还对MVVM模式中的viewModel封装,调用起来更加方便了,你的viewModel要继承lifecycle的ViewModel,然后可以通过get()方法,调用其他的注入对象(这个后面详细解说)

class MyViewModel:ViewModel() {var NumData :Int = 0override fun onCleared() {super.onCleared()CSKoinLog.I("调用了销毁方法")}
}

我这边创建了一个MyViewModel类,该类继承lifecycle的ViewModel,在Application中的appModule中,将该类注册下:

val appModule = module {//里面添加各种注入对象...viewModel {MyViewModel()}}
class ViewModelActivity : AppCompatActivity() {val myViewModel: MyViewModel by viewModel()val myViewModel2 by viewModel<MyViewModel>()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_view_model)CSKoinLog.I(myViewModel.hashCode().toString())CSKoinLog.I(myViewModel2.hashCode().toString())CSKoinLog.I(myViewModel.NumData.toString())findViewById<Button>(R.id.btn_change).setOnClickListener {myViewModel.NumData = 1CSKoinLog.I(myViewModel.NumData.toString())}}
}

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200216220109759.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NoYW9qaWhhbg==,size_16,color_FFFFFF,t_70 =500)

在ViewModelActivity中,我获取了2个viewModel对象,里面写了个按钮,点击可以改变viewModel中的属性,大家可以看到,这2个viewModel都是同一个,然后我修改了里面的值,接着我将手机屏幕变成横屏(这个看实际操作),再变成竖屏,Activity重新调用生命周期,但是viewModel仍旧是那个viewModel(地址不变),接着退出该界面,发现viewModel中的clear回调被调用了.
好了到此,Koin的三种注入方式都讲完了,这时,就会有同学问:哎呀,古诚欺啊,如果我的构造函数中有参数,那应该如何调用呢?别急,请继续往下看

5.1带参数的构造函数使用

class AppData(var mApp:Application)

大家请看,我现在有一个AppData类,该类有一个Application属性,那么该如何去注入这个类

val appModule = module {...factory {AppData(get())}}

这时,就会有同学疑问,这个get()又是个啥?

还记得最开始初始化Koin的时候,不是传了一个androidContext(this@MyApp),将appLication对象传了进去,Koin中,就已经记录了这个application,所以在你需要用到application对象的时候,直接通过get()方法调用就可以了.接着在NormalActivity中调用一下,看一下日志结果.

class NormalActivity : AppCompatActivity() {val appData by inject<AppData>()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_normal)CSKoinLog.I("application是否为空:" + (appData.mApp == null))}
}

5.2通过限定符标记构造方法–qualifier

class NormalData {var numData: Int = 0var userName: String = ""var mApp: Application? = nullvar appData: AppData? = nullconstructor(userName: String, numData: Int) {this.userName = userNamethis.numData = numData}constructor(appData: AppData) {this.appData = appData}constructor(mApp: Application) {this.mApp = mApp}fun printInfo(str: String) {//打印里面的信息CSKoinLog.I(str + "的信息    numData:" + numData + "///userName:" + userName + "///application是否为空:" + (mApp == null) + "///appData是否为空:" + (appData == null))}
}

现在我有一个NormalData类,里面是有三个构造函数,而且需要的参数都不同,那这种情况下应该怎么办,单独地注入方式已经无法满足我们了,这个时候,就需要用到qualifier限定符了.

那什么是qualifier限定符,你可以理解为标签,就是给一个注入的构造函数贴个标签,用的时候,通过标签获取.首先我们来看一下factory注入的源码:

inline fun <reified T> factory(qualifier: Qualifier? = null,override: Boolean = false,noinline definition: Definition<T>): BeanDefinition<T> {val beanDefinition = DefinitionFactory.createFactory(qualifier, definition = definition)declareDefinition(beanDefinition, Options(override = override))return beanDefinition}

(时不时来点源码,专业无疑)

大家可以看到,factory注入方式中有一个Qualifier参数(single和viewModel这2个注入方式用法跟factory都一样),默认为null,这个就是要填写的限定符,然后我们再来看Qualifier的源码.

interface Qualifier/*** Give a String qualifier*/
fun named(name: String) = StringQualifier(name)/*** Give a Type based qualifier*/
inline fun <reified T> named() = TypeQualifier(T::class)

就是一个接口,通过named(string)的方法来标记.哦!原来里面填一个字符串啊,那so easy.

好我们来看一下实际的用法,该类中有3个不同的构造函数,然后有一个方法,能够打印出该类中所有的属性

class NormalData {var numData: Int = 0var userName: String = ""var mApp: Application? = nullvar appData: AppData? = nullconstructor(userName: String, numData: Int) {//构造方法1this.userName = userNamethis.numData = numData}constructor(appData: AppData) {//构造方法2this.appData = appData}constructor(mApp: Application) {//构造方法3this.mApp = mApp}fun printInfo(str: String) {//打印里面的信息CSKoinLog.I(str + "的信息    numData:" + numData + "///userName:" + userName + "///application是否为空:" + (mApp == null) + "///appData是否为空:" + (appData == null))}
}

这3种不同的构造函数,在application中分别注入

val appModule = module {//里面添加各种注入对象...factory {AppData(get())}factory(named("nameAnum")) {//该限定符的构造方法中包含字符串和数字NormalData("曹老板", 12)}factory(named("app")) {//该限定符定义构造方法中有appliaction的NormalData(get<Application>())}factory(named("appData")){//该限定符定义构造方法中有AppData的NormalData(get<AppData>())}}

注入方式就是这样,通过named方法添加标签.其中,有2个构造函数需要各传一个参数,其中一个是application对象,另一个构造函数需要传AppData这个对象,这个对象在上面我们已经是有注入过的.(如果没注入过怎么办,我们下面会说).所以可以通过get()方式获取,但是获取哪一个呢,在get()中,我们有个泛型,通过泛型,就能够判断需要传哪一个(get这个方法也有个限定符named功能,后续会讲到).好了,注入完成之后,我们来看一下在NormalActivity中如何调用并且结果咋么样.

class NormalActivity : AppCompatActivity() {val appData by inject<AppData>()val norData1 by inject<NormalData>(named("nameAnum"))//限定符1val norData2: NormalData by inject(named("app"))//限定符2override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_normal)CSKoinLog.I("application是否为空:" + (appData.mApp == null))val norData3 = get<NormalData>(named("appData"))//限定符3norData1.printInfo("norData1")norData2.printInfo("norData2")norData3.printInfo("norData3")}
}
I/caesarLogkoin: norData1的信息    numData:12///userName:曹老板///application是否为空:true///appData是否为空:true
I/caesarLogkoin: norData2的信息    numData:0///userName:///application是否为空:false///appData是否为空:true
I/caesarLogkoin: norData3的信息    numData:0///userName:///application是否为空:true///appData是否为空:false

从打印的结果看,是不是就是我们所需要的,'nameAnum’标签的类中,只有字符串和数字,'app’标签的类中,只有application对象,'appData’标签的类中,只有AppData,是不是就是我们所预期的.

5.3引用其他注入时的Qualifier使用—在get()中使用Qualifier

我上面也说过,在注入的时候,如果你的构造方法中有参数,通过get()方法可以直接传你注入过的对象,但是如果对象有多种的话(例如我上面的NormalData),那你可以通过在get()中,加入Qualifier限定符来获取你所需要的对象

class WeatherData(val normalData: NormalData) {fun printData(string: String) {normalData.printInfo(string)}
}

现在有一个WeatherData类,该类的构造方法需要传上面的NormalData,里面有一个输出传入的NormalData的属性的方法.然后我们看application类中的注入方式.

val appModule = module {//里面添加各种注入对象...factory(named("nameAnum")) {//该限定符的构造方法中包含字符串和数字NormalData("曹老板", 12)}factory(named("app")) {//该限定符定义构造方法中有appliaction的NormalData(get<Application>())}factory(named("appData")) {//该限定符定义构造方法中有AppData的NormalData(get<AppData>())}factory(named("wea_name")) {WeatherData(get<NormalData>(named("nameAnum")))//这边get方法中有一个泛型,可以指定传入的对象的类型,因为我构造函数只有一个,所以会智能输入,可以省略掉}factory(named("wea_app")) {WeatherData(get(named("app")))//这边就智能省略掉泛型了}factory(named("wea_appData")) {WeatherData(get(named("appData")))}}

大家可以看下面的3个注入,在get的方法中,我传了限定符标签,便签里的值是上面的normalData注入的限定符的值,然后我在NormalTwoActivity中,分别获取这3个对象,看打印的日志

class NormalTwoActivity : AppCompatActivity() {val weatherData by inject<WeatherData>(named("wea_name"))val weatherData2 by inject<WeatherData>(named("wea_app"))val weatherData3 by inject<WeatherData>(named("wea_appData"))override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_normal_two)weatherData.printData("weather1")weatherData2.printData("weather2")weatherData3.printData("weather3")}
}
I/caesarLogkoin: weather1的信息    numData:12///userName:曹老板///application是否为空:true///appData是否为空:true
I/caesarLogkoin: weather2的信息    numData:0///userName:///application是否为空:false///appData是否为空:true
I/caesarLogkoin: weather3的信息    numData:0///userName:///application是否为空:true///appData是否为空:false

是不是也是符合我们所需要的,根据限定符的不同,获取不同的对象.

5.4声明进样参数—构造参数从外面传入

这时聪明的同学会问:哎呀,古诚欺啊,如果构造函数的参数,我想要自己从外面传入,比如说构造函数的参数是一个View对象,那我怎么调用呢?

比如我有一个类ViewData,它的构造参数是View,里面有一个方法输出id

class ViewData(val view: View) {fun prinId() {CSKoinLog.I(view.id.toString())}
}

像比如这种类的构造函数,我们无法通过注入获取,只能通过外部方式来传入参数.如何注入与调用.首先我们来看Application中的appModule

val appModule = module {//里面添加各种注入对象...factory {(view: View) -> ViewData(view)//外部调用的方式,如果是多参数也一样,聪明的同学么应该要学会举一反三了}}

然后我们在NormalTwoActivity中这样调用:

class NormalTwoActivity : AppCompatActivity() {...var btnShow: Button? = nullval viewData by inject<ViewData> { parametersOf(btnShow) }override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_normal_two)...btnShow = findViewById(R.id.btn_show)//这边要注意,btn的初始化要在ViewData的调用之前,否则会报空指针.koin的注入是懒加载模式的,只有在调用对象的时候,才会实例化对象viewData.prinId()CSKoinLog.I("这个是直接获取按钮id" + btnShow?.id.toString())}
}

通过parametersOf()函数来传参,打印的日志如下

I/caesarLogkoin: 获取ViewData的按钮id2131165254
I/caesarLogkoin: 这个是直接获取按钮id2131165254

大家可以看到,ViewData中我们成功获取到了View视图对象.

6实现KoinComponent,普通类中使用注入对象

在一般的类中,我们如何依赖注入?

class CompontData : KoinComponent {val appD1 by inject<AppData>()//懒加载模式val appD2 = get<AppData>()//非懒加载模式fun priInfo() {CSKoinLog.I("CompontData中appD1地址:" + appD1.hashCode() + "appD2地址:" + appD2.hashCode())}
}

我们创建一个CompontData,该类实现了KoinComponent,在该类中,我们就可以通过by inject和get来过去被注入过的对象了.

class NormalTwoActivity : AppCompatActivity() {...override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_normal_two)...CompontData().priInfo()//这边直接new对象,看里面注入的对象信息}
}

在NormalTwoActivity中,我这边new了一个NormalTwoActivity对象,然后直接打印里面的属性,结果OK

I/caesarLogkoin: CompontData中appD1地址:228506535appD2地址:871168084

7.跨Module模块注入依赖

大家的项目中肯定是有多个Module依赖,那么如何调用其他Module中注入的对象.

我在项目中创建了一个mylibrary模块,该模块中有一个LibData这个空类

class LibData {
}
object libModule {val theLibModule = module {//koin支持多个module注入single { LibData() }//这边用single方式注入}
}

然后又有一个libModule的object类,里面有一个module的方法,这个方法是不是跟我们application中的appModule是不是很像,其实一毛一样.
然后我们在application中,将这个theLibModule添加进去.

class MyApp : Application() {override fun onCreate() {super.onCreate()startKoin {//开始启动koinandroidContext(this@MyApp)//这边传Application对象,这样你注入的类中,需要app对象的时候,可以直接使用modules(appModule,libModule.theLibModule)//这里面传各种被注入的模块对象,支持多模块注入,在2.0.1之后才支持vararg调用,之前可以使用集合来调用}}

大家可以看到,我添加了一个libModule.theLibModule对象,好,这样之后,我们就可以使用libModule.theLibModule下被注入的对象了,使用方式跟上面的一样.

然后我的app模块中,有一个ModuleData类,该构造函数需要传一个LibData对象

class ModuleData(val libData:LibData) {
}

然后将ModuleData注入

val appModule = module {//里面添加各种注入对象...        factory { ModuleData(get())}}

其他都一样,然后我创建了一个LibModuleActivity界面,在里面获取LibData和ModuleData对象,并且打印出LibData

class LibModuleActivity : AppCompatActivity() {val libData by inject<LibData>()val moduleData by inject<ModuleData>()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_lib_module)CSKoinLog.I("直接依赖的libData:"+libData.hashCode().toString())CSKoinLog.I("moduleData中的libData:"+moduleData.libData.hashCode().toString())}
}
I/caesarLogkoin: 直接依赖的libData:410923983
I/caesarLogkoin: moduleData中的libData:410923983

是不是符合我们的预想

8.Scope—作用域的使用

什么是Scope作用域,这个东西其实跟viewModel有点相似,scope下的对象可以跟一个视图绑定起来,并且该被绑定的对象是单例的模式,其他界面通过scopeId可以获取这个对象.当该视图被销毁的时候,被绑定的对象也会被销毁.其他界面也就获取不到这个scope对象了.

class ScopeData {
}

现在我有一个ScopeData这个类.然后在appModule中,用scope方式注入.scope注入方式有2种,先讲第一种

val appModule = module {//里面添加各种注入对象...        scope(named("myScope")) {//scope类型的注入方式一,通过标签的方式scoped {ScopeData()}}}

然后我在ScopeCurentActivity中,将该scope与该视图绑定起来.

class ScopeCurentActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_scope_curent)val scope = getKoin().createScope("scopeId1", named("myScope"))//创建scope方式一bindScope(scope)//scope与界面绑定,只有这边创建绑定了之后,其他地方才能获取到这个作用域val scopeData = scope.get<ScopeData>()//获取作用域下的类CSKoinLog.I("ScopeCurentActivity中的ScopeData是否为空:" + (scopeData == null))CSKoinLog.I("ScopeCurentActivity中的ScopeData地址:" + (scopeData.hashCode()))}
}

大家可以看到,通过标签,我创建了一个id为"scopeId1"的作用域,该作用域的标签为myScope,在application中该标签注入的对象为ScopeData.接着,就可以通过get方式来获取这个对象了,注意,这边必须要先通过createScope创建了作用域之后,然后绑定视图,这样你其他的地方才能获取到这个作用域.接着就打印这个对象的地址.

我在MainActivity中也有个方法,当点击跳转到ScopeCurentActivity时,会调用doScope方法,获取下作用域,进行一个比较,然后等2秒之后Scope跟ScopeCurentActivity绑定了之后,再获取下,下面是MainActivity的代码(注意getScopeOrNull这个方法)

class MainActivity : AppCompatActivity() {...fun doScope() {val scopeData = getKoin().getScopeOrNull("scopeId1")注意这边获取一个可空的方法,直接getScope获取到的可能为空,会报空指针CSKoinLog.I("MainActivity中获取scope是否为空" + (scopeData == null))Thread {Thread.sleep(2000)val scopeData2 = getKoin().getScopeOrNull("scopeId1")val data = scopeData2?.get<ScopeData>()CSKoinLog.I("MainActivity中延迟2秒获取的scope是否为空" + (scopeData2 == null))CSKoinLog.I("MainActivity中延迟2秒获取的data的地址" + (data.hashCode()))}.start()}override fun onResume() {super.onResume()Thread {Thread.sleep(2000)val scopeData2 = getKoin().getScopeOrNull("scopeId1")CSKoinLog.I("MainActivity的onResume中获取scope是否为空" + (scopeData2 == null))}.start()}
}
I/caesarLogkoin: MainActivity的onResume中获取scope是否为空true
//这边点击跳转之后
I/caesarLogkoin: MainActivity中获取scope是否为空true
I/caesarLogkoin: ScopeCurentActivity中的ScopeData是否为空:false
I/caesarLogkoin: ScopeCurentActivity中的ScopeData地址:380234974
I/caesarLogkoin: MainActivity中延迟2秒获取的scope是否为空false
I/caesarLogkoin: MainActivity中延迟2秒获取的data的地址380234974
//这边ScopeCurentActivity按了返回键之后
I/caesarLogkoin: MainActivity的onResume中获取scope是否为空true

是不是从日志中,就可以看出结果.然后上面也说过了,scope的获取方式有2种,接着是第二种方式,第二种方式我感觉有点鸡肋,虽然官网上的demo就是这样的写法,大家请往下看.

class ScopeTypeTwo {
}

我这边有一个ScopeTypeTwo类,然后我在appModule中,将该类注入一下

val appModule = module {//里面添加各种注入对象...scope(named<ScopeCurentActivity>()){scoped{ScopeTypeTwo()}}}

这种注入方式,可以直接传一个Activity的泛型标签,就是在注入的时候,就已经跟对应的视图绑定起来,这样的话,你在ScopeCurentActivity中,可以直接通过currentScope.inject()的方式,获取到该对象.

class ScopeCurentActivity : AppCompatActivity() {val csopeTypeW: ScopeTypeTwo by currentScope.inject()//直接获取了ScopeTypeTwo对象override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_scope_curent)...CSKoinLog.I("ScopeCurentActivity中的ScopeTypeTwo的scopeid值:" + currentScope.id)CSKoinLog.scopeId = currentScope.idCSKoinLog.I("ScopeCurentActivity中的ScopeTypeTwo的地址位" + (csopeTypeW.hashCode()))}
}

不过这种方式,他其实已经帮你创建好了scopeid的值,该scopeid的值就是你当前视图的地址,但是有个问题,就是其他视图要通过scope方式获取对象的话,必须有一个scopeid,如果你ScopeCurentActivity没有运行的话,该地址位是为空的,而且scope对象也没有,所以我这边将ScopeCurentActivity的地址位保存在一个地方.其他视图要用的时候,直接去获取就可以了.

object CSKoinLog {...var scopeId :ScopeID = ""//保存Scopeid
}

接着我在MainActivity中,再去获取ScopeTypeTwo这个对象

class MainActivity : AppCompatActivity() {fun doScope() {...val typeScope = getKoin().getScopeOrNull(CSKoinLog.scopeId)CSKoinLog.I("MainActivity获取的ScopeTypeTwo是否为空:" + (typeScope == null))Thread {Thread.sleep(2000)...val typeScope2 = getKoin().getScopeOrNull(CSKoinLog.scopeId)val typeTwo = typeScope2?.get<ScopeTypeTwo>()CSKoinLog.I("MainActivity中延迟2秒获取的ScopeTypeTwo是否为空" + (typeScope2 == null))CSKoinLog.I("MainActivity中延迟2秒获取的ScopeTypeTwo地址位:" + (typeTwo.hashCode()))}.start()}
}
//点击跳转按钮,到ScopeCurentActivity
MainActivity获取的ScopeTypeTwo是否为空:true
ScopeCurentActivity中的ScopeTypeTwo的scopeid值:com.caesar.cskoin.scope.ScopeCurentActivity@703993064
ScopeCurentActivity中的ScopeTypeTwo的地址位528591061
//2秒之后
MainActivity中延迟2秒获取的ScopeTypeTwo是否为空false
MainActivity中延迟2秒获取的ScopeTypeTwo地址位:528591061

从日志中,就可以看到我们验证的结果.

9.fragment使用

在2.1.0-alpha-3版本开始,Koin支持对fragment的注入了(官网说的,但是我调试的时候有问题),但是我调试的时候,发现fragment相关的api在当前最新的版本及之前的版本中都有问题,api没有相关的内容,估计作者还在调试中,但是官网上已经给出了fragment的使用方法,之后的版本应该会修复好.接下来我们来看看怎么使用.(无验证下面的调用,但是可以通过作用域的方式来调用,我demo中调试过ok)

class MyFragment(val str: String) : Fragment() {override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {// Inflate the layout for this fragmentreturn inflater.inflate(R.layout.fragment_my, container, false)}
}

我创建了一个MyFragment这样的碎片.该构造方法中有一个字符串的参数,我们在application中,需要将该对象注入.

class MyApp : Application() {override fun onCreate() {super.onCreate()startKoin {//开始启动koin...
//            fragmentFactory() 暂时2.1.0-alpha-3这个版本之前有问题,引用不到,估计作者还在调试,文档上的用法就是这样的modules(appModule, libModule.theLibModule)//这里面传各种被注入的模块对象,支持多模块注入,在2.0.1之后才支持vararg调用}}val appModule = module {//里面添加各种注入对象...//        fragment { MyFragment("张三") }//暂时2.1.0-alpha-3这个版本之前有问题,引用不到,估计作者还在调试,文档上的用法就是这样的}
}

首先在startKoin中,我们需要传入fragmentFactory()这个工厂类,然后在appModule中,通过fragment注入类型来注入我们的MyFragment.
好接下来就是在界面中调用我们的碎片了

class FragActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {setupKoinFragmentFactory()//要在调用父类的方法之前调用super.onCreate(savedInstanceState)setContentView(R.layout.activity_frag)supportFragmentManager.beginTransaction().replace(R.id.mvvm_frame, MyFragment::class.java, null, null).commit()}
}

就是这么简单调用.目前fragment的注入方式有问题,我无法验证.官网还有一种碎片跟作用域结合的用法,我这边也一并给大家写出来,也不验证了.

val appModule = module {//里面添加各种注入对象scope(named<FragActivity>()){scoped {MyFragment("张三")}}
}
class FragActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {
//        setupKoinFragmentFactory()//要在调用父类的方法之前调用
//        setupKoinFragmentFactory(currentScope)//碎片跟作用域的用法super.onCreate(savedInstanceState)setContentView(R.layout.activity_frag)
//        supportFragmentManager.beginTransaction()
//            .replace(R.id.mvvm_frame, MyFragment::class.java, null, null)
//            .commit()}
}

就是在作用域中,注入碎片,然后在界面中,用setupKoinFragmentFactory(currentScope)方法绑定界面,其实这种做法直接用我上面讲到的Scope的方式去获取碎片就可以用了(大家都是成熟的程序员,要自己去举一反三了)

10.1.Properties–调用资产中的数值

Koin还能直接调用资产文件中的内容.我在assets资产文件夹下,创建了一个koin.properties(默认的名字,你也可以自己命名),里面就一个键值对

userName = "abc123"

然后在application的初始化中,加上

class MyApp : Application() {override fun onCreate() {super.onCreate()startKoin {//开始启动koin...androidFileProperties()//默认名字为koin.properties,你也可以直接重新设置名称modules(appModule, libModule.theLibModule)//这里面传各种被注入的模块对象,支持多模块注入,在2.0.1之后才支持vararg调用}}
}

调用一下androidFileProperties()就可以了,接着就是如何使用了.

class ProData(val string: String) {
}

我创建了一个ProData类,构造函数传一个字符串,然后在application中注入

val appModule = module {//里面添加各种注入对象...factory {ProData(getProperty("userName"))//该方法可以设置泛型对象,你已经是一个成熟的程序员了,要学会自己举一反三}
}

通过getProperty方法,里面传键的值,接着再调用下就可以了.

class OtherActivity : AppCompatActivity() {val proData :ProData by inject()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_other)CSKoinLog.I("通过Property方式获取:"+proData.string)}
}
I/caesarLogkoin: 通过Property方式获取:abc123

10.2.在startkoin之外,加载module

如果你的项目是用组件化开发的,那这个方式很适合。

class TimeData(val ProD:ProData) {
}

我这边有一个TimeData类,该类的构造参数是ProData,上面文章中,我将ProData在appModule中注入了,接下来我再创建一个注入modul,将TimeData在新的module中注入,然后再去加载这个module.

class MyApp : Application() {override fun onCreate() {super.onCreate()startKoin {...modules(appModule, libModule.theLibModule)//这里面传各种被注入的模块对象,支持多模块注入,在2.0.1之后才支持vararg调用}loadKoinModules(otherModule)}val otherModule = module {factory {TimeData(get())}}

大家请看,我这边新创了一个otherModule,然后在里面,将TimeData注入,可以看到,参数ProData我传了一个get(),因为ProData在上面中,我已经注入过了,所以通过get()方式,就可以获取到。然后我在OtherActivity中调用。

class OtherActivity : AppCompatActivity() {val proData :ProData by inject()val timeData :TimeData by inject()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_other)CSKoinLog.I("通过Property方式获取:"+proData.string)CSKoinLog.I("timeData里面的属性:"+timeData.ProD.string)}
}
I/caesarLogkoin: 通过Property方式获取:abc123
I/caesarLogkoin: timeData里面的属性:abc123

从日志中,我们验证了。

至此,Koin的使用方法我都已经给大家详解了(其实还有几个小东西,但是在实际项目中用不到)(觉得写得还不错的,给个

Koin--适用于Kotlin的超好用依赖注入框架,Dagger替代者,Koin史上最详细解说,一篇就够了,妈妈再也不用担心我不会依赖注入了相关推荐

  1. 使用EXCEL绘制三维地图(超简单的五分钟绘制地图方法,妈妈再也不用担心我不会画地图啦~)

    博主为从区域规划转行地图学的小学渣一枚,最近处理数据希望对结果进行三维可视化,意外发现从小用到大的EXCEL可以绘制地图且功能非常强大,在这里做一下简单介绍,希望可以给看官提供些许帮助.那下面就开始吧 ...

  2. 【逗老师带你学IT】Zoom动态授权用户Pro License妈妈再也不用担心预算超标了

    Zoom是个好东西,但是License也不便宜.免费的用户主持的会议,又会存在45分钟限制. 本文介绍,如何通过后台脚本,定时对用户进行授权和回收授权操作.列出Zoom内所有用户的所有会议,到点了自动 ...

  3. 超酷的傅里叶变换视频+动图解说,妈妈再也不用担心我不懂傅里叶变换了!

    刚接触傅里叶变换,都会对他感到头痛,之前 分享过知乎大神的文章<傅里叶变换-通彻理解>,是我的启蒙文章,这次找到了一个更加通彻的视频讲解,绝对让你"恍然大悟"!  En ...

  4. 超简单的位运算---再也不用担心看不懂题解了

    超简单的位运算---再也不用担心看不懂题解了 写在前面 1.原码.反码与补码------整形在计算机中的储存 2.移位操作符 3.位操作符 4.小练手 写在最后 写在前面 大家好,这里是风扇的小小笔记 ...

  5. 【超全汇总】学习数据结构与算法,计算机基础知识,看这篇就够了【ZT帅地】2020-3-7

    https://blog.csdn.net/m0_37907797/article/details/104029002 由于文章有点多,并且发的文章也不是一个系列一个系列发的,不过我的文章大部分都是围 ...

  6. Kotlin | 一份10万字的随查笔记,妈妈再也不担心我不会Ctrl+F了

    标题党? 看起来可能有点标题党的意思,但我知道,不这样,你们可能看不到这篇. 关于Kotlin相关记录,如果有意查看我的github,其超10w字(其中8w是代码吗,哈哈).. 背景 学习Kotlin ...

  7. 超准中医体质测试 源码_可能是史上“最准”的抑郁症测试,试试你有没有患上抑郁症...

    越来越多的公众人物被爆因抑郁症自杀,很多人对这种疾病产生了恐惧感.并且从抑郁出现到如今,随着各方面压力不断增加,患抑郁症的人越来越多,所以我们要时刻做好预防,下面的测试题你敢试试吗? 最准的抑郁症心理 ...

  8. python学习 -女神或者男神把微信消息撤回后好慌,有了这个妈妈再也不担心你看不到女神或者男神撤回的消息了(超详解)...

    简介 有时候在忙工作,女朋友发了一个消息,就撤回了,但是人天生的都有一颗好奇心,而且在当今这个时代找个女朋友不容易,一个程序猿找一个女朋友更是不容易的.人家好不容易跟你,你还不得把人家当老佛爷侍候着, ...

  9. 超5k+stars,给大家推荐两个ChatGPT自动化论文阅读网站和插件,从此搞科研再也不用愁!...

    点击上方"Python与机器智能",选择"星标"公众号 第一时间获取价值内容 ChatPaper 项目地址: https://github.com/kaixin ...

最新文章

  1. html中设置文本框长度,Html的文本框怎样限制录入文本框的字节长度
  2. 说说重试的那些实现方式
  3. OO第三次博客作业---透过代码看设计
  4. pads导出坐标文件html,【教程】PADS如何导出SMT贴片机用的坐标文件
  5. [密码学基础][每个信息安全博士生应该知道的52件事][Bristol Cryptography][第22篇]如何用蒙哥马利算法表示一个数字和多个相乘的数字
  6. RT-Thread中堆和栈内存的分配
  7. day20/FileDemo1.java
  8. python许可证过期_x-pack许可证过期问题解决
  9. dom4j解析xml
  10. Linux进程管理软件supervisord使用心得
  11. afn原理 ios_iOS AFNetworking网络框架详解
  12. linux系统切换输入法,linux系统切换输入法
  13. NLP词向量模型总结:从Elmo到GPT,再到Bert
  14. 基于51单片机定时器计数+2片74HC595联级+8位数码管时钟+按键修改时间
  15. 【转】MUD教程--巫师入门教程2
  16. 【转】db_domain参数设置影响dblink迁移问题
  17. centos7 搭建深度学习环境
  18. 5个技巧打造令人印象深刻的LOGO
  19. 异步复位同步释放 打两拍
  20. 找优秀项目很好的网站推荐

热门文章

  1. js加mysql写邮箱找回密码_邮箱找回密码实现
  2. 手机safari导入html书签,iPhone手机Safari浏览器书签如何同步至电脑?
  3. 微信小程序之拓展篇——weui-wxss
  4. 使用scrapy时,没有crawl命令
  5. 疫情相关的出行提示都在这了!百度地图上线“疫情管控消息速报”功能
  6. 【noip2012】 文化之旅 floyd
  7. IT技术开发论坛大全
  8. Mac电脑使用自然码双拼
  9. 平面设计:创建精美的书虫图标
  10. 大学生计算机应用能力达标测试主观操作题,计算机应用能力测试题(13页)-原创力文档...