Koin實(shí)戰(zhàn)

對(duì)于強(qiáng)大的注解框架,Dagger2的編譯特點(diǎn)一直都讓我覺得不舒服践宴,強(qiáng)行學(xué)完Dagger2的使用和大體原理后哨查,也一直沒有將它投入生產(chǎn)中。后來在瀏覽博客的時(shí)候弦疮,發(fā)現(xiàn)Koin:

適用于Kotlin開發(fā)人員的實(shí)用輕量級(jí)依賴注入框架夹攒。僅使用功能分辨率編寫的純Kotlin:無代理,無代碼生成胁塞,無反射咏尝!

Koin官網(wǎng)

這個(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來看下效果


factory Gif

可以看到獲取的對(duì)象是不同的,并且每次重新進(jìn)入獲取拦焚,都是新的對(duì)象蜡坊。

2.2 獲取single對(duì)象

SingleActivity的代碼與FactoryActivity代碼相似,這里獲取的SingleModel

single GIF

每次獲取的對(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效果


ViewModel 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地址

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末兼蜈,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子拙友,更是在濱河造成了極大的恐慌为狸,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件遗契,死亡現(xiàn)場離奇詭異辐棒,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)牍蜂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門漾根,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鲫竞,你說我怎么就攤上這事辐怕。” “怎么了从绘?”我有些...
    開封第一講書人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵寄疏,是天一觀的道長其做。 經(jīng)常有香客問我,道長赁还,這世上最難降的妖魔是什么妖泄? 我笑而不...
    開封第一講書人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮艘策,結(jié)果婚禮上蹈胡,老公的妹妹穿的比我還像新娘。我一直安慰自己朋蔫,他們只是感情好罚渐,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著驯妄,像睡著了一般荷并。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上青扔,一...
    開封第一講書人閱讀 49,079評(píng)論 1 285
  • 那天源织,我揣著相機(jī)與錄音,去河邊找鬼微猖。 笑死谈息,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的凛剥。 我是一名探鬼主播侠仇,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼犁珠!你這毒婦竟也來了逻炊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤犁享,失蹤者是張志新(化名)和其女友劉穎余素,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饼疙,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡溺森,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了窑眯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屏积。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖磅甩,靈堂內(nèi)的尸體忽然破棺而出炊林,到底是詐尸還是另有隱情,我是刑警寧澤卷要,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布渣聚,位于F島的核電站独榴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏奕枝。R本人自食惡果不足惜棺榔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望隘道。 院中可真熱鬧症歇,春花似錦、人聲如沸谭梗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽激捏。三九已至设塔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間远舅,已是汗流浹背闰蛔。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留表谊,地道東北人钞护。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓盖喷,卻偏偏與公主長得像爆办,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子课梳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容