告別Dagger2阴幌,Android的Kotlin項(xiàng)目中使用Kodein進(jìn)行依賴注入

前言:依賴注入淺談


Dagger2的困境

對(duì)于依賴注入(Dependency Injection饿肺,簡(jiǎn)稱DI)來講蒋困,它并非是一個(gè)新鮮的詞匯,實(shí)際上敬辣,它很早就被提出并且應(yīng)用在了企業(yè)級(jí)的web應(yīng)用開發(fā)當(dāng)中雪标,比如Spring。

在Android開發(fā)領(lǐng)域內(nèi)溉跃,毫無疑問村刨,Google大名鼎鼎的 Dagger2 是依賴注入框架的首選工具庫(kù),它非常優(yōu)秀撰茎,Github上數(shù)以萬(wàn)計(jì)的star是最強(qiáng)力的佐證嵌牺,但是缺點(diǎn)也很明顯,那就是:

復(fù)雜龄糊。

這最直接導(dǎo)致了 極為高昂的學(xué)習(xí)成本——你可以不認(rèn)為它難逆粹,但你要承認(rèn)學(xué)習(xí)Dagger2比你學(xué)習(xí)其他庫(kù)所花費(fèi)的時(shí)間要多得多。

我的感受

我是卻把清梅嗅炫惩,一個(gè)Android開發(fā)者僻弹,此外在業(yè)余時(shí)間,我喜歡學(xué)習(xí)總結(jié)分享他嚷。

對(duì)于Dagger奢方,我曾經(jīng)花了6篇博客的篇幅去闡述了我的理解(請(qǐng)參考https://blog.csdn.net/column/details/17168.html),于現(xiàn)在來看爸舒,這些文章還存在一些瑕疵蟋字,但對(duì)于當(dāng)時(shí)的我來說,我盡力去闡述了扭勉,但是還有問多評(píng)論這樣說:

文章講的很好鹊奖,但是我還是遇到了一些問題,請(qǐng)教博主........

Dagger太復(fù)雜了涂炎!一方面它意味著更靈活忠聚,更多強(qiáng)大功能的支持,同時(shí)它也意味著——不夠簡(jiǎn)潔唱捣。

你需要配置Module两蟀,需要配置Component,然后在你需要注入的容器中震缭,初始化Component然后注入進(jìn)來——這一切的前提是赂毯,你還得先正確地進(jìn)行依賴配置,以保證編譯器不會(huì)爆出一大串Error。

當(dāng)然党涕,對(duì)于其中的一些問題烦感,世界上那些最頂尖的工程師們已經(jīng)做出了更好的解決方案,dagger.android橫空出世膛堤,對(duì)于Activity和Fragment容器手趣,開發(fā)者再也不需要初始化Component的模版代碼了。

dagger.android并沒有解決 學(xué)習(xí)成本高昂的問題——相反肥荔,它需要在熟悉Dagger2的基礎(chǔ)上繼續(xù)深入绿渣,反而增加了學(xué)習(xí)成本;但不可否認(rèn)燕耿,它依然是目前Android開發(fā)中依賴注入工具選型中的首選怯晕。

我也曾經(jīng)一度這樣想過,對(duì)于依賴注入這個(gè)技能點(diǎn)缸棵,熟練dagger.android的使用舟茶,閱讀源碼并掌握原理 已經(jīng)足夠。

——但Kotlin時(shí)代到來了堵第。

Kotlin時(shí)代

最近個(gè)人在嘗試構(gòu)建適合自己的 Kotlin的 MVVM 吧凉,在依賴注入框架的選型上,我最終選擇了 Kodein 踏志。

這是一個(gè)非常輕量級(jí)的DI框架阀捅,同樣,它也被《Kotlin in Action》一書所推薦针余,相比于配置繁瑣的Dagger饲鄙,它的配置過程更清晰且簡(jiǎn)單,并且圆雁,這個(gè)庫(kù)的源碼也是 Kotlin 的忍级。

有同學(xué)說,雖然 Dagger 配置很繁瑣伪朽,但 dagger.android 已經(jīng)大大減少了模板代碼轴咱,為什么不使用它呢?

確實(shí)如此烈涮,但Dagger終究是通過編譯器自動(dòng)生成 Java 代碼的庫(kù)朴肺,這實(shí)在不夠 Kotlin,于我個(gè)人來講坚洽,Dagger并非最優(yōu)選戈稿。

Kodein:入門篇


雖然 Kodein 全名為 KOtlin DEpendency INjection,但Kodein并不是一個(gè)真正的依賴注入框架讶舰。 他們的官方文檔將其稱作依賴檢索容器鞍盗。

下面是Kodein的官方文檔:

Kodein官方文檔:Getting started with Kodein DI
Kodein官方文檔 for Android:Kodein DI on Android

本文的主旨是需了,讓開發(fā)者更快入門Kodein和理解其思想,如果您想更深入學(xué)習(xí)它橡疼,官方文檔是你不二的選擇。

在這里我推薦官方文檔的原因庐舟,一是欣除,英文文檔對(duì)一些專業(yè)詞匯和思想,描述的更清晰準(zhǔn)確挪略;第二就是历帚,關(guān)于Kodein目前國(guó)內(nèi)還沒有任何相關(guān)中文資料....

讓我們開始使用它吧。

1.添加依賴

在Module級(jí)別的build.gradle中添加Kodein最新的依賴:

// 基礎(chǔ)組件
implementation 'org.kodein.di:kodein-di-generic-jvm:5.2.0'
// Android擴(kuò)展組件
implementation 'org.kodein.di:kodein-di-framework-android-core:5.2.0'
// support擴(kuò)展組件杠娱,我的項(xiàng)目中用到了v4包的Fragment挽牢,因此我需要它
implementation 'org.kodein.di:kodein-di-framework-android-support:5.2.0'

如果依賴不成功,你需要把kodein的maven倉(cāng)庫(kù)顯式地聲明出來:

allprojects {
    repositories {
        google()
        jcenter()
        // maven for kodein
        maven { url 'https://dl.bintray.com/kodein-framework/Kodein-DI/' }
    }
}

2.如何使用它摊求?

我們必須嘗試回憶Dagger2的核心思想:它是將依賴通過Module管理提供禽拔,然后交給Component注入給Activity等容器。

Kodein并不是一個(gè)真正的依賴注入框架室叉。 他們的官方文檔將其稱作依賴檢索容器睹栖。

這是我文中第二次聲明,Kodein和Dagger的核心思想有所不同茧痕,其原理是——將依賴交給一個(gè) Kodein容器野来,然后將Kodein容器交給ActivityActivity中所需要的依賴通過委托Kodein容器注入踪旷。

好了好了曼氛,我知道這段話很抽象,我們來看一個(gè)案例令野,它將展示如何把一個(gè)SQLiteDatabase對(duì)象通過Kodein進(jìn)行依賴注入舀患。

首先我們先聲明一個(gè)Kodein容器:

val kodein = Kodein {
    bind<Database>() with singleton { SQLiteDatabase() }
}

Kodein提供了對(duì)DSL的強(qiáng)大支持,正如你所看到的气破,我們可以將SQLiteDatabase對(duì)象的實(shí)例化過程构舟,放在Kodein 開頭的{ }中。

bind<T>()意味著你聲明將一個(gè)類型為T的依賴放入了Kodein容器進(jìn)行綁定(bind)堵幽。作為一個(gè)非常重的對(duì)象狗超,SQLiteDatabase更應(yīng)該保持單例,所以我們?cè)趯?duì)其實(shí)例化的方式上朴下,選擇了singleton { }努咐。

相比于dagger,這種配置方式實(shí)在太清晰了——沒有@Inject,沒有@Providers殴胧,沒有@Component,你只需要通過Kotlin所支持的DSL渗稍,就能輕松完成各種方式依賴的綁定佩迟。

3.有哪些綁定方式呢?

本文不是Kodein的文檔竿屹,但是我認(rèn)為花一些篇幅講解這些是有必要的报强,Kodein共提供了provider, singleton, factory, multiton, instance等等多種方式的綁定。

singleton

正如上文描述過的拱燃,這種方式會(huì)實(shí)例化一個(gè)單例對(duì)象秉溉,該單例對(duì)象將會(huì)在第一次使用時(shí)通過單例函數(shù)進(jìn)行創(chuàng)建,該函數(shù)不帶參數(shù)并返回綁定類型的對(duì)象(例如()→T)碗誉。

示例代碼召嘶,該對(duì)象將會(huì)在第一次被調(diào)用時(shí),通過調(diào)用該函數(shù)哮缺,將對(duì)象進(jìn)行生成并返回弄跌,該函數(shù)有且僅會(huì)有一次被調(diào)用:

val kodein = Kodein {
    bind<DataSource>() with singleton { SqliteDS.open("path/to/file") }
}

provider

和singleton不同,該函數(shù)每次都會(huì)被調(diào)用并返回對(duì)應(yīng)的依賴尝苇。

示例代碼,每次都會(huì)調(diào)用該函數(shù)铛只,返回一個(gè)新生成的對(duì)象:

val kodein = Kodein {
    bind<Die>() with provider { RandomDie(6) }
}

factory

provider很相似,每次都會(huì)調(diào)用該函數(shù)糠溜,返回一個(gè)新生成的對(duì)象格仲,不同的是,factory函數(shù)接受已定義類型的參數(shù)并返回綁定類型的對(duì)象(例如诵冒,(A)→T)凯肋。

示例代碼,根據(jù)參數(shù)sides的不同,每次都會(huì)返回一個(gè)新的Die

val kodein = Kodein {
    bind<Die>() with factory { sides: Int -> RandomDie(sides) }
}

還有更多...

還有更多汽馋,請(qǐng)參考官方文檔中關(guān)于綁定聲明方式的說明侮东。

4.進(jìn)行依賴注入

我們已經(jīng)通過不同的方式,完成了依賴綁定豹芯,接下來悄雅,我們就可以進(jìn)行依賴注入了。

還記得我已經(jīng)提了兩遍的話嗎铁蹈,Kodein是一個(gè)依賴檢索容器宽闲。

我們把綁定的依賴交給Kodein容器,然后我們把這個(gè)容器交給Activity握牧,Activity就可以從容器中取出這些依賴了容诬。

稍微有點(diǎn)不同的是,取出依賴的方式是通過kotlin的屬性委托

class Presenter(val kodein: Kodein) {  // presenter擁有kodein容器
    private val db: Database by kodein.instance()  // 通過屬性委托沿腰,即可依賴注入
    private val rnd: Random by kodein.instance()
}

現(xiàn)在览徒,我們就可以直接對(duì)這些對(duì)象進(jìn)行引用了,have fun颂龙!

Kodein:實(shí)戰(zhàn)篇


拋開項(xiàng)目架構(gòu)談工具都是刷流氓习蓬。

上述內(nèi)容僅僅提供了對(duì)Kodein的簡(jiǎn)單了解纽什,實(shí)際上,無論是是dagger2還是kodein躲叼,會(huì)寫demo在項(xiàng)目中應(yīng)用 完全是天差地別芦缰。

如果只是簡(jiǎn)單的API介紹,這篇文章也許更早就出來了枫慷,事實(shí)上让蕾,我在嘗試構(gòu)建 Kotlin的 MVVM 項(xiàng)目時(shí),將Kodein加了進(jìn)去流礁,并不斷進(jìn)行調(diào)整——直到現(xiàn)在涕俗,我對(duì)它有了更清晰的一些認(rèn)識(shí)以及理解罗丰。

當(dāng)然神帅,它們不一定就是對(duì)的,或者說萌抵,不一定就是適合你的找御,但我希望,我的這次實(shí)踐绍填,能夠讓你對(duì)Kodein有更深度的了解:

MVVM-Rhine:The MVVM Architecture in Android.

MVVM-Rhine霎桅,是我目前在嘗試探索的mvvm開發(fā)架構(gòu)(Rhine:萊茵河),目前處于摸索和開發(fā)期讨永,歡迎參考并提出建議滔驶。

1.定制Application

在一個(gè)Android項(xiàng)目中,很多依賴都需要保持單例卿闹,這樣能夠保證合理的資源規(guī)劃揭糕,比如,Retrofit的實(shí)例化锻霎,比如Gson對(duì)象的實(shí)例化著角,這里我們直接在Application中進(jìn)行配置:

open class RhineApplication : Application(), KodeinAware {

    override val kodein: Kodein = Kodein.lazy {
       
    }
}

KodeinAware是一個(gè)接口,它意味著旋恼,實(shí)現(xiàn)該接口的對(duì)象都會(huì)持有一個(gè)Kodein容器:

interface KodeinAware {
    /**
     * A Kodein Aware class must be within reach of a [Kodein] object.
     */
    val kodein: Kodein
    
    val kodeinContext: KodeinContext<*> get() = AnyKodeinContext

    val kodeinTrigger: KodeinTrigger? get() = null
}

RhineApplication實(shí)現(xiàn)了KodeinAware接口吏口,并實(shí)例化了一個(gè)Kodein容器,接下來我們要做的冰更,就是把對(duì)應(yīng)的依賴裝進(jìn)Kodein容器中产徊。

我們定義了一個(gè)httpClientModule頂層屬性以聲明Retrofit相關(guān):

const val HTTP_CLIENT_MODULE_TAG = "httpClientModule"

const val TIME_OUT_SECONDS = 20

val httpClientModule = Kodein.Module(HTTP_CLIENT_MODULE_TAG) {
    
    bind<Retrofit.Builder>() with singleton { Retrofit.Builder() }

    bind<OkHttpClient.Builder>() with singleton { OkHttpClient.Builder() }

    bind<Retrofit>() with singleton {
        instance<Retrofit.Builder>()   // 委托給了bind<Retrofit.Builder>()函數(shù)
                .baseUrl(APIConstants.BASE_API)
                .client(instance())    // 委托給了 bind<OkHttpClient>() 函數(shù)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build()
    }

    bind<OkHttpClient>() with singleton {
        instance<OkHttpClient.Builder>()  // 委托給bind<OkHttpClient.Builder>()函數(shù)
                .connectTimeout(
                        TIME_OUT_SECONDS.toLong(),
                        TimeUnit.SECONDS)
                .readTimeout(
                        TIME_OUT_SECONDS.toLong(),
                        TimeUnit.SECONDS)
                .addInterceptor(HttpLoggingInterceptor()
                        .setLevel(HttpLoggingInterceptor.Level.BODY))
                .build()
    }

    bind<Gson>() with singleton { Gson() }
}

Retrofit聲明好了,我們?cè)俾暶鲗?duì)應(yīng)的APIService:

const val SERVICE_MODULE_TAG = "serviceModule"

val serviceModule = Kodein.Module(SERVICE_MODULE_TAG) {

    bind<UserService>() with singleton {
        // Retrofit對(duì)象的獲取已經(jīng)在httpClientModule中聲明好了
        instance<Retrofit>().create(UserService::class.java)
    }

    bind<ServiceManager>() with singleton {
        ServiceManager(instance())  // userService的獲取方式已經(jīng)聲明
    }
}

// 目前ServiceManager只有User相關(guān)的API接口蜀细,可后續(xù)慢慢追加
data class ServiceManager(val userService: UserService)

當(dāng)然囚痴,這些依賴的綁定都依賴于項(xiàng)目架構(gòu),比如审葬,我的項(xiàng)目用到了RxCache深滚,我也聲明了對(duì)應(yīng)的cacheModule

val CACHE_MODULE_TAG = "CacheModule"

val cacheModule = Kodein.Module(CACHE_MODULE_TAG) {

    bind<RxCache>() with singleton {
        RxCache.Builder()
                .persistence(ContextCompat.getExternalCacheDirs(instance())[0], GsonSpeaker())
    }
}

這些依賴最終都統(tǒng)一交給RhineApplication,在我的項(xiàng)目中奕谭,它大概是這樣的:

open class RhineApplication : Application(), KodeinAware {

    override val kodein: Kodein = Kodein.lazy {
        bind<Context>() with singleton { this@RhineApplication }
        import(androidModule(this@RhineApplication))
        import(androidSupportModule(this@RhineApplication))

        import(serviceModule)
        import(cacheModule)
        import(rxModule)
        import(httpClientModule)
    }
}

2.定制Activity或者Fragment

全局的依賴交給了RhineApplication,如果對(duì)于一個(gè)Activity,它可能還有其他的依賴需要注入痴荐,這意味著血柳,我們需要:

  • 1.RhineApplication級(jí)別的Kodein容器
  • 2.Activity級(jí)別的Kodein容器,它包含僅Activity所需依賴

這很簡(jiǎn)單生兆,我們只需要extendimport

class MainActivity : BaseActivity() 难捌,KodeinAware {
    
    private val parentKodein by closestKodein()  // 1

    override val kodein: Kodein by retainedKodein {
        extend(parentKodein, copy = Copy.All)    // 2
        import(mainKodeinModule)     // 3
        bind<MainActivity>() with instance(this@MainActivity)  // 4
    }
    // 注入MainNavigator控制Activity的視圖導(dǎo)航
    private val navigator: MainNavigator by instance()   
    // 注入MainViewModel管理業(yè)務(wù)數(shù)據(jù)
    private val mainViewModel: MainViewModel by instance()  
}

這里的Activity代碼僅方便讀者理解,實(shí)際代碼因架構(gòu)設(shè)計(jì)有一定偏差:

1.closestKodein()函數(shù)返回了相鄰上層的一個(gè)Kodein容器鸦难,對(duì)于Activity來說根吁,它返回的是Application層級(jí)的Kodein容器。
2.通過extend()函數(shù)合蔽,我們將Application層級(jí)的Kodein容器也放在了Activity的kodein容器中击敌,這樣Activity就能從上層的Kodein容器取出對(duì)應(yīng)依賴(俄羅斯套娃?)拴事,比如網(wǎng)絡(luò)請(qǐng)求的service相關(guān)等等沃斤。
3.類似Application的注入方式一樣,我定義了一個(gè)mainKodeinModule,以存放MainActiviy所需依賴的綁定函數(shù),類似dagger中的@Scope,其中scoped(AndroidComponentsWeakScope)保證了Activity級(jí)別的局部單例刃宵,:

val MAIN_MODULE_TAG = "MAIN_MODULE_TAG"

val mainKodeinModule = Kodein.Module(MAIN_MODULE_TAG) {
    // 省略很多代碼...
    bind<HomeFragment>() with scoped(AndroidComponentsWeakScope).singleton {
        HomeFragment()
    }

    bind<MainViewModel>() with scoped(AndroidComponentsWeakScope).singleton {
        // 這里需要MainActivity衡瓶,請(qǐng)參考下文中4的講解
        instance<MainActivity>().viewModel(MainViewModel::class.java)
    }
}

4.正如上文看到的,一些對(duì)象的實(shí)例化需要Context的上下文對(duì)象牲证,我們通過bind<MainActivity>() with instance(this@MainActivity)完成MainActivity的綁定哮针。

Kodein:小結(jié)


能看到這里的,基本都是真愛了坦袍,實(shí)際上十厢,相比于Dagger,Kodein的學(xué)習(xí)成本更低键闺,代碼更簡(jiǎn)潔寿烟,配置更簡(jiǎn)單。

我不認(rèn)為這樣一篇博客就能 Kodein從入門到精通辛燥,所謂實(shí)踐出真知筛武,我更建議您參考實(shí)際的項(xiàng)目,去了解它在實(shí)際項(xiàng)目中的應(yīng)用:

https://github.com/qingmei2/MVVM-Rhine

最后列一下相關(guān)學(xué)習(xí)資料(筆者寫稿的此時(shí)挎塌,國(guó)內(nèi)尚未有任何Kodein的中文學(xué)習(xí)資料徘六,實(shí)在遺憾),以供大家參考:

Kodein官方文檔:Getting started with Kodein DI
Kodein官方文檔 for Android:Kodein DI on Android

2018/11/30補(bǔ)充

感謝 o動(dòng)感超人o 兄弟花了很久總結(jié)的圖榴都,將Kodein官方文檔晦澀的文字總結(jié)了出來待锈,經(jīng)過他的同意,我把這張圖也轉(zhuǎn)載過來嘴高,以供大家參考:

image.png

原文鏈接:《使用Kodein作為Dagger2的升級(jí)版替代品》 by o動(dòng)感超人o
原圖鏈接:http://naotu.baidu.com/file/7f5ea2ba8e7fa2820973d11b7c66a98a

--------------------------廣告分割線------------------------------

關(guān)于我

Hello竿音,我是卻把清梅嗅和屎,如果您覺得文章對(duì)您有價(jià)值,歡迎 ??春瞬,也歡迎關(guān)注我的博客或者Github柴信。

如果您覺得文章還差了那么點(diǎn)東西,也請(qǐng)通過關(guān)注督促我寫出更好的文章——萬(wàn)一哪天我進(jìn)步了呢宽气?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末随常,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子萄涯,更是在濱河造成了極大的恐慌绪氛,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涝影,死亡現(xiàn)場(chǎng)離奇詭異枣察,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)袄琳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門询件,熙熙樓的掌柜王于貴愁眉苦臉地迎上來燃乍,“玉大人唆樊,你說我怎么就攤上這事】绦罚” “怎么了逗旁?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)舆瘪。 經(jīng)常有香客問我片效,道長(zhǎng),這世上最難降的妖魔是什么英古? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任淀衣,我火速辦了婚禮,結(jié)果婚禮上召调,老公的妹妹穿的比我還像新娘膨桥。我一直安慰自己,他們只是感情好唠叛,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布只嚣。 她就那樣靜靜地躺著,像睡著了一般艺沼。 火紅的嫁衣襯著肌膚如雪册舞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天障般,我揣著相機(jī)與錄音调鲸,去河邊找鬼盛杰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛藐石,可吹牛的內(nèi)容都是我干的饶唤。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼贯钩,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼募狂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起角雷,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤祸穷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后勺三,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體雷滚,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年吗坚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了祈远。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡商源,死狀恐怖车份,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情牡彻,我是刑警寧澤扫沼,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站庄吼,受9級(jí)特大地震影響缎除,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜总寻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一器罐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧渐行,春花似錦轰坊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至旁理,卻和暖如春樊零,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工驻襟, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留夺艰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓沉衣,卻偏偏與公主長(zhǎng)得像郁副,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子豌习,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,527評(píng)論 25 707
  • 目錄 Dagger簡(jiǎn)介 Dagger1.x飞主,Dagger2.x簡(jiǎn)單對(duì)比 Dagger中的注解@Inject@Pro...
    zero_sr閱讀 3,257評(píng)論 0 5
  • Dagger2 轉(zhuǎn)載請(qǐng)注明原作者啡专,如果你覺得這篇文章對(duì)你有幫助或啟發(fā),可以關(guān)注打賞。 前言本文翻譯自Google ...
    輕云時(shí)解被占用了閱讀 6,661評(píng)論 4 31
  • 目前我們項(xiàng)目代碼注釋少穆律、代碼風(fēng)格差異大鸣皂,會(huì)導(dǎo)致交接時(shí)間長(zhǎng)恢准、交接后故障多的問題戳气。針對(duì)這種情況,我們應(yīng)該統(tǒng)一代碼風(fēng)格吸占,...
    腩啵兔子閱讀 254評(píng)論 0 1
  • 那天晚上我正好在看笑來老師的微博晴叨,正好看到他發(fā)了一條說說,是這樣的一句話看了我不是藥神淚漰矾屯,當(dāng)時(shí)我就在心里想什么電...
    mynameis洪香蘭閱讀 383評(píng)論 2 2