@martin0207
2019-09-05T02:40:17.000000Z
字数 5317
阅读 1363
android
对于强大的注解框架,Dagger2的编译特点一直都让我觉得不舒服,强行学完Dagger2的使用和大体原理后,也一直没有将它投入生产中。后来在浏览博客的时候,发现Koin:
适用于Kotlin开发人员的实用轻量级依赖注入框架。仅使用功能分辨率编写的纯Kotlin:无代理,无代码生成,无反射!
这个三无产品一下子就吸引了我,不要998,不要9.8,什么都不要,来试试看~
koin_version = '2.0.1'
// Koin for Androidimplementation "org.koin:koin-android:$koin_version"// Koin Android Scope featuresimplementation "org.koin:koin-android-scope:$koin_version"// Koin Android ViewModel featuresimplementation "org.koin:koin-android-viewmodel:$koin_version"// Koin Android Experimental featuresimplementation "org.koin:koin-android-ext:$koin_version"
// Koin AndroidX Scope featuresimplementation "org.koin:koin-androidx-scope:$koin_version"// Koin AndroidX ViewModel featuresimplementation "org.koin:koin-androidx-viewmodel:$koin_version"// Koin AndroidX Experimental featuresimplementation "org.koin:koin-androidx-ext:$koin_version"
三无产品特点,就是需要我们告诉它有哪些对象要注解,如何生成。
class App : Application() {override fun onCreate() {super.onCreate()/*开启Koin,这里需要将所有需要注解生成的对象添加进来*/startKoin {//给Koin框架添加ApplicationContextandroidContext(this@App)/*这里设置Koin的日志打印Koin提供了三种实现:AndroidLogger:使用Android的Log.e/i/d()打印日志PrintLogger:使用System.err/out打印日志EmptyLogger:不打印日志,默认就是该实现*/logger(AndroidLogger())/*设置Koin配置文件,需要放在assets文件夹中默认名称为:koin.propreties可以快速获取配置文件中的内容,文件名可以修改,但是需要在这里保持一致[getKoin().getProperty<String>("name")]*/androidFileProperties("koin.properties")modules(/*添加Module对象*/module {/*实例工厂,每次获取都是新的实例对象*/factory { FactoryModel() }/*获取的实例为单例*/single { SingleModel() }single { MainModel() }/*获取的实例为ViewModel,并且具有ViewModel的功能*/viewModel { MainViewModel(get()) }})}}}
为了演示清晰,这里创建FactoryActivity类
class FactoryActivity : AppCompatActivity() {/*** 使用[inject]获取FactoryModel实例* 其他教程中有说也可以使用[get]获取* 并且[inject]知识[lazy]版的[get]* 可以点开[inject]看到源码确实如此,但我这里是用[get]时提示* `Missing 'getValue(FactoryActivity, KProperty<*>)' method on delegate of type 'FactoryModel'`* 推荐:* 获取实例时使用[inject],初始化Koin时使用[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来看下效果

可以看到获取的对象是不同的,并且每次重新进入获取,都是新的对象。
SingleActivity的代码与FactoryActivity代码相似,这里获取的SingleModel
每次获取的对象都是同一个,关闭Activity重新进来获取依然是相同的。
获取方式与上面两个不同,这里需要用viewModel来获取
class ViewModelActivity : AppCompatActivity() {/*** 这里区分开来:* mViewModel由Koin的[viewModel]来生成* mViewModel2由[ViewModelProvider]生成* mModel由本身构造函数生成* 旋转屏幕后看是否具有原生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效果
前两个数据同步改变,说明Koin和ViewModelProvider获取的对象是同一个,这个涉及到ViewModel的实现原理,这里不做阐述。也就证明Koin获取的ViewModel真实可用。
由于横竖屏切换不方便GIF录制,这里就口述一下子,只有第三个数字会在切换时归0,所以,木有问题。
框架再好,终究是要配合咱们实现功能的,这里以MVVM来做个示例:
class MvvmModel {/*** 模拟获取消息*/fun getMsg(): String {return "这是一个普通的消息"}fun getError(): String {return "这是一个错误提醒"}}
class MvvmViewModel(private val mView: IMvvmView) : ViewModel(),/*** 标示该类为Koin的组件,这样就可以在该类自由的使用 get()/inject()* 当然,如果你是个狼人,就喜欢不按套路走,也可以不实现该接口,使用* 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())}}
interface IMvvmView{fun showMsg(msg:String)fun showError(error:String)}class MvvmActivity : AppCompatActivity(), IMvvmView {/*** 用Koin获取ViewModel* 因为[MvvmViewModel]的构造函数有IMvvmView,且Koin无法提供其实现* 这里需要手动添加该参数,配合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对象这个接口是Activity来实现的,没有办法在Koin中注明实例,所以需要以此方式*/viewModel { (view: IMvvmView) -> MvvmViewModel(view) }single { MvvmModel() }
这个示例主要展示如何将View对象在Koin框架中传递给ViewModel,ViewModel中又如何使用Koin获取对象。
相较于Dagger2的每次生成都需要重新编译,Koin给我的感觉真的超清新,对ViewModel的支持也让我在选择框架时有更多的选择,强力推荐各位同道中人尝试一下Koin框架。
Demo地址