對(duì)于強(qiáng)大的注解框架,Dagger2的編譯特點(diǎn)一直都讓我覺得不舒服践宴,強(qiáng)行學(xué)完Dagger2的使用和大體原理后哨查,也一直沒有將它投入生產(chǎn)中。后來在瀏覽博客的時(shí)候弦疮,發(fā)現(xiàn)Koin:
適用于Kotlin開發(fā)人員的實(shí)用輕量級(jí)依賴注入框架夹攒。僅使用功能分辨率編寫的純Kotlin:無代理,無代碼生成胁塞,無反射咏尝!
這個(gè)三無產(chǎn)品一下子就吸引了我,不要998啸罢,不要9.8编检,什么都不要,來試試看~
添加依賴
當(dāng)前最新版本
koin_version = '2.0.1'
Android
// Koin for Android
implementation "org.koin:koin-android:$koin_version"
// Koin Android Scope features
implementation "org.koin:koin-android-scope:$koin_version"
// Koin Android ViewModel features
implementation "org.koin:koin-android-viewmodel:$koin_version"
// Koin Android Experimental features
implementation "org.koin:koin-android-ext:$koin_version"
AndroidX
// Koin AndroidX Scope features
implementation "org.koin:koin-androidx-scope:$koin_version"
// Koin AndroidX ViewModel features
implementation "org.koin:koin-androidx-viewmodel:$koin_version"
// Koin AndroidX Experimental features
implementation "org.koin:koin-androidx-ext:$koin_version"
開始使用
1.初始化Koin
三無產(chǎn)品特點(diǎn)扰才,就是需要我們告訴它有哪些對(duì)象要注解允懂,如何生成。
class App : Application() {
override fun onCreate() {
super.onCreate()
/*
開啟Koin训桶,這里需要將所有需要注解生成的對(duì)象添加進(jìn)來
*/
startKoin {
//給Koin框架添加ApplicationContext
androidContext(this@App)
/*
這里設(shè)置Koin的日志打印
Koin提供了三種實(shí)現(xiàn):
AndroidLogger:使用Android的Log.e/i/d()打印日志
PrintLogger:使用System.err/out打印日志
EmptyLogger:不打印日志累驮,默認(rèn)就是該實(shí)現(xiàn)
*/
logger(AndroidLogger())
/*
設(shè)置Koin配置文件,需要放在assets文件夾中
默認(rèn)名稱為:koin.propreties
可以快速獲取配置文件中的內(nèi)容舵揭,文件名可以修改谤专,但是需要在這里保持一致
[getKoin().getProperty<String>("name")]
*/
androidFileProperties("koin.properties")
modules(
/*
添加Module對(duì)象
*/
module {
/*
實(shí)例工廠,每次獲取都是新的實(shí)例對(duì)象
*/
factory { FactoryModel() }
/*
獲取的實(shí)例為單例
*/
single { SingleModel() }
single { MainModel() }
/*
獲取的實(shí)例為ViewModel,并且具有ViewModel的功能
*/
viewModel { MainViewModel(get()) }
}
)
}
}
}
2.獲取對(duì)象
2.1 獲取factory對(duì)象
為了演示清晰午绳,這里創(chuàng)建FactoryActivity
類
class FactoryActivity : AppCompatActivity() {
/**
* 使用[inject]獲取FactoryModel實(shí)例
* 其他教程中有說也可以使用[get]獲取
* 并且[inject]知識(shí)[lazy]版的[get]
* 可以點(diǎn)開[inject]看到源碼確實(shí)如此置侍,但我這里是用[get]時(shí)提示
* `Missing 'getValue(FactoryActivity, KProperty<*>)' method on delegate of type 'FactoryModel'`
* 推薦:
* 獲取實(shí)例時(shí)使用[inject],初始化Koin時(shí)使用[get]
*/
private val mModelOne: FactoryModel by inject()
private val mModelTwo: FactoryModel by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_factory)
btn_show.setOnClickListener {
val msg = "one model is:\n $mModelOne\ntwo model is:\n $mModelTwo"
loge(msg)
tv.text = msg
}
}
companion object {
fun start(context: Context) {
context.startActivity(Intent(context, FactoryActivity::class.java))
}
}
}
用gif來看下效果
可以看到獲取的對(duì)象是不同的,并且每次重新進(jìn)入獲取拦焚,都是新的對(duì)象蜡坊。
2.2 獲取single對(duì)象
SingleActivity
的代碼與FactoryActivity
代碼相似,這里獲取的SingleModel
每次獲取的對(duì)象都是同一個(gè)赎败,關(guān)閉Activity重新進(jìn)來獲取依然是相同的秕衙。
2.3 獲取ViewModel對(duì)象
獲取方式與上面兩個(gè)不同,這里需要用viewModel
來獲取
class ViewModelActivity : AppCompatActivity() {
/**
* 這里區(qū)分開來:
* mViewModel由Koin的[viewModel]來生成
* mViewModel2由[ViewModelProvider]生成
* mModel由本身構(gòu)造函數(shù)生成
* 旋轉(zhuǎn)屏幕后看是否具有原生ViewModel的功能
*/
private val mViewModel: VmViewModel by viewModel()
private val mViewModel2: VmViewModel by lazy {
ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(VmViewModel::class.java)
}
private val mModel: VmViewModel by lazy { VmViewModel() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view_model)
mViewModel.count.observe(this, Observer {
tv_vm.text = it.toString()
})
mViewModel2.count.observe(this, Observer {
tv_vm2.text = it.toString()
})
mModel.count.observe(this, Observer {
tv_m.text = it.toString()
})
btn_vm.setOnClickListener {
mViewModel.addCount()
}
btn_vm2.setOnClickListener {
mViewModel2.addCount()
}
btn_m.setOnClickListener {
mModel.addCount()
}
}
companion object {
fun start(context: Context) {
context.startActivity(Intent(context, ViewModelActivity::class.java))
}
}
}
VMViewModel
代碼
class VmViewModel : ViewModel() {
val count = MutableLiveData<Int>()
init {
count.value = 0
}
fun addCount() {
count.value = count.value!!.toInt() + 1
}
}
Gif效果
前兩個(gè)數(shù)據(jù)同步改變僵刮,說明Koin和ViewModelProvider獲取的對(duì)象是同一個(gè)据忘,這個(gè)涉及到ViewModel的實(shí)現(xiàn)原理鹦牛,這里不做闡述。也就證明Koin獲取的ViewModel真實(shí)可用勇吊。
由于橫豎屏切換不方便GIF錄制曼追,這里就口述一下子,只有第三個(gè)數(shù)字會(huì)在切換時(shí)歸0汉规,所以礼殊,木有問題。
實(shí)現(xiàn)MVVM
框架再好针史,終究是要配合咱們實(shí)現(xiàn)功能的晶伦,這里以MVVM來做個(gè)示例:
Model
class MvvmModel {
/**
* 模擬獲取消息
*/
fun getMsg(): String {
return "這是一個(gè)普通的消息"
}
fun getError(): String {
return "這是一個(gè)錯(cuò)誤提醒"
}
}
ViewModel
class MvvmViewModel(private val mView: IMvvmView) : ViewModel(),
/**
* 標(biāo)示該類為Koin的組件,這樣就可以在該類自由的使用 get()/inject()
* 當(dāng)然悟民,如果你是個(gè)狼人坝辫,就喜歡不按套路走篷就,也可以不實(shí)現(xiàn)該接口射亏,使用
* GlobalContext.get().koin.get()
* GlobalContext.get().koin.inject()
*/
KoinComponent {
private val mModel: MvvmModel by inject()
fun show() {
mView.showMsg(mModel.getMsg())
}
fun error() {
mView.showError(mModel.getError())
}
}
View
interface IMvvmView{
fun showMsg(msg:String)
fun showError(error:String)
}
class MvvmActivity : AppCompatActivity(), IMvvmView {
/**
* 用Koin獲取ViewModel
* 因?yàn)閇MvvmViewModel]的構(gòu)造函數(shù)有IMvvmView,且Koin無法提供其實(shí)現(xiàn)
* 這里需要手動(dòng)添加該參數(shù),配合App中的[viewModel { (view: IMvvmView) -> MvvmViewModel(view) }]
*/
private val mViewModel: MvvmViewModel by viewModel {
parametersOf(this)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding =
DataBindingUtil.setContentView<ActivityMvvmBinding>(this, R.layout.activity_mvvm)
binding.viewModel = mViewModel
}
override fun showMsg(msg: String) {
showSnack(msg, btn_show)
}
override fun showError(error: String) {
showSnack(error, btn_error)
}
companion object {
fun start(context: Context) {
context.startActivity(Intent(context, MvvmActivity::class.java))
}
}
}
將MvvmModel和MvvmViewModel添加到Koin中
/*
這里MVVMViewModel需要傳入IMvvmView對(duì)象
這個(gè)接口是Activity來實(shí)現(xiàn)的,沒有辦法在Koin中注明實(shí)例竭业,所以需要以此方式
*/
viewModel { (view: IMvvmView) -> MvvmViewModel(view) }
single { MvvmModel() }
這個(gè)示例主要展示如何將View對(duì)象在Koin框架中傳遞給ViewModel智润,ViewModel中又如何使用Koin獲取對(duì)象。
結(jié)語
相較于Dagger2的每次生成都需要重新編譯未辆,Koin給我的感覺真的超清新窟绷,對(duì)ViewModel的支持也讓我在選擇框架時(shí)有更多的選擇,強(qiáng)力推薦各位同道中人嘗試一下Koin框架咐柜。
Demo地址