【譯】kotlin Android Frameworks 使用注釋處理(五)

本文轉(zhuǎn)自本人blog,更多關(guān)于kotlin詳情點(diǎn)擊本人blog

本教程介紹如何在Kotlin流行的Android框架和庫中使用依賴注釋處理。

Android世界有許多流行的框架,簡(jiǎn)化了開發(fā)媒役。如果你在Kotlin開發(fā)脚翘,你可以使用相同的框架再层,就像在Java中一樣容易跷坝。本教程提供了示例并突出顯示了設(shè)置中的差異倦蚪。

我們將看Dagger侣诵,Butterknife痢法,Data Binding,Auto-parcel和DBFlow(其他框架可以類似的設(shè)置)杜顺。所有這些框架通過注釋處理工作:您注釋代碼以為您生成樣板代碼财搁。注釋允許隱藏所有的詳細(xì)信息并保持簡(jiǎn)單的代碼,如果您需要了解運(yùn)行時(shí)實(shí)際發(fā)生的情況躬络,可以查看生成的代碼尖奔。請(qǐng)注意,所有這些框架都是用Java生成源代碼穷当,而不是Kotlin提茁。

在Kotlin中,使用Kotlin Annotation處理工具(kapt)而不是annotationProcessor來指定類似于Java的依賴關(guān)系馁菜。


Dagger(匕首)

Dagger是一個(gè)依賴注入框架茴扁。 如果您還不熟悉,可以閱讀其用戶指南汪疮。 我們已經(jīng)將本指南中介紹的 the coffee example示例轉(zhuǎn)換為Kotlin峭火,您可以在這里找到結(jié)果。 Kotlin代碼看起來幾乎相同; 您可以在一個(gè)文件中瀏覽整個(gè)示例智嚷。

和Java一樣卖丸,你使用@Inject來注釋Dagger使用的構(gòu)造函數(shù)來創(chuàng)建一個(gè)類的實(shí)例。 Kotlin有一個(gè)簡(jiǎn)短的語法來同時(shí)聲明一個(gè)屬性和一個(gè)構(gòu)造函數(shù)參數(shù)盏道。 要注釋構(gòu)造函數(shù)稍浆,請(qǐng)明確使用構(gòu)造函數(shù)關(guān)鍵字,并在其之前放置@Inject注釋:

class Thermosiphon 
@Inject constructor(
        private val heater: Heater
) : Pump {
    // ...
}    

注釋方法看起來完全一樣猜嘱。 在下面的示例中衅枫,@Binds確定在需要Pump時(shí)使用Thermosiphon對(duì)象,@Provides指定構(gòu)建Heater 的方式泉坐,@Singleton指出應(yīng)在整個(gè)位置使用相同的Heater :

@Module
abstract class PumpModule {
    @Binds
    abstract fun providePump(pump: Thermosiphon): Pump
}

@Module(includes = arrayOf(PumpModule::class))
class DripCoffeeModule {
    @Provides @Singleton
    fun provideHeater(): Heater = ElectricHeater()
}

@Module-annotated類定義了如何提供不同的對(duì)象为鳄。 請(qǐng)注意,當(dāng)您將注釋參數(shù)作為可變參數(shù)傳遞時(shí)腕让,必須將其顯式包裝到arrayOf中孤钦,如上面的@Module(includes = arrayOf(PumpModule :: class))歧斟。

要為該類型生成一個(gè)依賴注入的實(shí)現(xiàn),請(qǐng)使用@Component為其注釋偏形。 生成的類將具有此類型的名稱静袖,如DaggerCoffeeShop所示:

@Singleton
@Component(modules = arrayOf(DripCoffeeModule::class))
interface CoffeeShop {
    fun maker(): CoffeeMaker
}

fun main(args: Array<String>) {
    val coffee = DaggerCoffeeShop.builder().build()
    coffee.maker().brew()
}

Dagger生成一個(gè)CoffeeShop的實(shí)現(xiàn),允許你獲得一個(gè)完全注入的CoffeeMaker俊扭。 如果您在IDE中打開項(xiàng)目队橙,則可以導(dǎo)航并查看DaggerCoffeeShop的實(shí)現(xiàn)。

我們觀察到萨惑,當(dāng)您切換到Kotlin時(shí)捐康,注釋您的代碼幾乎沒有改變。 現(xiàn)在讓我們看看應(yīng)該對(duì)構(gòu)建腳本進(jìn)行哪些更改庸蔼。

在Java中解总,您將Dagger指定為annotationProcessor(或apt)依賴項(xiàng):

dependencies {
  ...
  annotationProcessor "com.google.dagger:dagger-compiler:$dagger-version"
}

在Kotlin中,您必須添加kotlin-kapt插件來啟用kapt姐仅,然后用kapt替換annotationProcessor:

apply plugin: 'kotlin-kapt'
dependencies {
    ...
    kapt "com.google.dagger:dagger-compiler:$dagger-version"
}

就這樣花枫! 請(qǐng)注意,kapt也會(huì)處理您的Java文件掏膏,所以您不需要保留annotationProcessor依賴關(guān)系劳翰。

示例項(xiàng)目的完整構(gòu)建腳本可以在這里找到。 您也可以查看Android示例的轉(zhuǎn)換后的代碼馒疹。


ButterKnife

ButterKnife允許將視圖直接綁定到字段佳簸,而不是調(diào)用findViewById。

請(qǐng)注意颖变, Kotlin Android Extensions插件(自動(dòng)捆綁到Android Studio中的Kotlin插件)解決了同樣的問題:用簡(jiǎn)潔直接的代碼替換findViewById溺蕉。 考慮使用它,除非你已經(jīng)使用ButterKnife并且不想遷移悼做。

您可以像使用Java一樣使用Kotlin的ButterKnife。 首先看看Gradle構(gòu)建腳本中的更改哗魂,然后突出顯示代碼中的一些差異肛走。

在您使用的Gradle依賴關(guān)系中添加kotlin-kapt插件并用kapt替換annotationProcessor:

apply plugin: 'kotlin-kapt'

dependencies {
    ...
    compile "com.jakewharton:butterknife:$butterknife-version"
    kapt "com.jakewharton:butterknife-compiler:$butterknife-version"
}

我們已經(jīng)將ButterKnife樣本轉(zhuǎn)換為Kotlin。 結(jié)果代碼可以在這里找到录别。

讓我們來看看發(fā)生了什么變化朽色。 在Java中,您注釋了該字段组题,并將其與相應(yīng)的視圖綁定:

@BindView(R2.id.title) TextView title;

在Kotlin你不能直接使用字段葫男,你使用屬性。 您注釋該屬性:

@BindView(R2.id.title)
lateinit var title: TextView

@BindView注釋被定義為只應(yīng)用于字段崔列,但是Kotlin編譯器理解并注釋了在注釋應(yīng)用于整個(gè)屬性時(shí)引擎蓋下的相應(yīng)字段梢褐。

請(qǐng)注意旺遮,lateinit修飾符如何在創(chuàng)建對(duì)象之后(在構(gòu)造函數(shù)調(diào)用之后)聲明一個(gè)已初始化的非null類型。 沒有l(wèi)ateinit 盈咳,你必須聲明一個(gè) nullable type 的類型并添加額外的可空性檢查耿眉。

您還可以使用ButterKnife注釋將方法配置為偵聽器:

@OnClick(R2.id.hello)
internal fun sayHello() {
    Toast.makeText(this, "Hello, views!", LENGTH_SHORT).show()
}

此代碼指定要在“hello”按鈕單擊上執(zhí)行的操作。 請(qǐng)注意鱼响,對(duì)于lambdas鸣剪,這段代碼看起來相當(dāng)簡(jiǎn)潔直接寫在Kotlin中:

hello.setOnClickListener {
    toast("Hello, views!")
}

toast函數(shù)在Anko庫中定義。



Data Binding(數(shù)據(jù)綁定)

Data Binding Library允許您以簡(jiǎn)明的方式將應(yīng)用程序數(shù)據(jù)綁定到布局丈积。

使用與Java中相同的配置啟用該庫:

android {
    ...
    dataBinding {
        enabled = true
    }
}

要使它與Kotlin類一起工作筐骇,請(qǐng)?zhí)砑觡apt依賴項(xiàng):

apply plugin: 'kotlin-kapt'
dependencies {
    kapt "com.android.databinding:compiler:$android_plugin_version"
}  

當(dāng)你切換到Kotlin時(shí),你的xml布局文件根本不會(huì)改變江滨。 例如铛纬,您可以使用數(shù)據(jù)中的變量來描述可能在布局中使用的變量。 你可以聲明一個(gè)Kotlin類型的變量:

<data>
    <variable name="data" type="org.example.kotlin.databinding.WeatherData"/>
</data>

您使用@ {}語法來編寫表達(dá)式牙寞,現(xiàn)在可以引用Kotlin屬性

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@{data.imageUrl}"
    android:contentDescription="@string/image" />

請(qǐng)注意饺鹃,數(shù)據(jù)綁定表達(dá)式語言使用與Kotlin:data.imageUrl相同的語法來引用屬性。 在Kotlin中间雀,即使getProp()是Java方法悔详,您也可以編寫v.prop而不是v.getProp()。 同樣惹挟,不要直接調(diào)用setter茄螃,您可以使用一個(gè)賦值:

class MainActivity : AppCompatActivity() {
    // ...
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding: ActivityMainBinding =
                DataBindingUtil.setContentView(this, R.layout.activity_main)

        binding.data = weather
        // the same as
        // binding.setData(weather)
    }
}

您可以綁定偵聽器以在發(fā)生特定事件時(shí)運(yùn)行操作:

<Button
    android:text="@string/next"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:onClick="startOtherActivity" />

這里startOtherActivity是我們的MainActivity中定義的一個(gè)方法:


class MainActivity : AppCompatActivity() {
    // ...
    fun startOtherActivity(view: View) = startActivity<OtherActivity>()
}

此示例使用實(shí)用程序函數(shù)startActivity創(chuàng)建一個(gè)沒有數(shù)據(jù)的意圖,并啟動(dòng)一個(gè)來自Anko庫的新活動(dòng)连锯。 要傳遞一些數(shù)據(jù)归苍,你可以說startActivity <OtherActivity>(“KEY”到“VALUE”)。

請(qǐng)注意运怖,不像以下示例中那樣在xml中聲明lambda表達(dá)式拼弃,您可以直接在代碼中綁定動(dòng)作:

<Button 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content"
    android:onClick="@{() -> presenter.onSaveClick(task)}" />
// the same logic written in Kotlin code
button.setOnClickListener { presenter.onSaveClick(task) }

在最后一行中,button由使用Kotlin Android Extensions插件的id引用摇展。 考慮使用這個(gè)插件作為替代方案吻氧,它允許您在代碼中保留綁定邏輯并同時(shí)具有簡(jiǎn)潔的語法。

你可以在這里找到一個(gè)示例項(xiàng)目咏连。



DBFlow

DBFlow是一個(gè)SQLite庫盯孙,它簡(jiǎn)化了與數(shù)據(jù)庫的交互。 它很大程度上依賴于注釋處理祟滴。

要在Kotlin中使用它振惰,請(qǐng)使用kapt配置注釋處理依賴關(guān)系:

apply plugin: 'kotlin-kapt'

dependencies {
    kapt "com.github.raizlabs.dbflow:dbflow-processor:$dbflow_version"
    compile "com.github.raizlabs.dbflow:dbflow-core:$dbflow_version"
    compile "com.github.raizlabs.dbflow:dbflow:$dbflow_version"
}

以下是如何配置DBFlow的詳細(xì)指南。

如果您的應(yīng)用程序已經(jīng)使用DBFlow垄懂,則可以安全地將Kotlin引入您的項(xiàng)目骑晶。 您可以逐漸將現(xiàn)有代碼轉(zhuǎn)換為Kotlin(確保一路編譯)痛垛。 轉(zhuǎn)換后的代碼與Java沒有多大差別。 例如,聲明一個(gè)表與Java非常相似,但有一點(diǎn)區(qū)別橘沥,即必須明確指定屬性的默認(rèn)值:

@Table(name="users", database = AppDatabase::class)
class User : BaseModel() {

    @PrimaryKey(autoincrement = true)
    @Column(name = "id")
    var id: Long = 0

    @Column
    var name: String? = null
}

除了將現(xiàn)有功能轉(zhuǎn)換為Kotlin外炮赦,您還可以享受Kotlin特定的支持。 例如,您可以將表聲明為數(shù)據(jù)類

@Table(database = KotlinDatabase::class)
data class User(@PrimaryKey var id: Long = 0, @Column var name: String? = null)

DBFlow定義了一系列擴(kuò)展,使其在Kotlin中的使用更具慣用性,您可以在您的依賴中包含這些擴(kuò)展:

dependencies {
    compile "com.github.raizlabs.dbflow:dbflow-kotlinextensions:$dbflow_version"
}

這使您可以通過類似LINQ的C#語法來表達(dá)查詢识窿,使用lambdas為異步計(jì)算編寫更簡(jiǎn)單的代碼等等。 在這里閱讀所有細(xì)節(jié)脑融。

您可以瀏覽轉(zhuǎn)換后的示例應(yīng)用程序喻频。



Auto-Parcel

Auto-Parcel允許為使用@AutoValue注解的類生成Parcelable值。

當(dāng)您指定依賴項(xiàng)時(shí)肘迎,您再次使用kapt作為注釋處理程序來處理Kotlin文件:

apply plugin: 'kotlin-kapt'

dependencies {
    ...
    kapt "frankiesardo:auto-parcel:$latest-version"
}


轉(zhuǎn)換后的樣本可以在這里找到甥温。

您可以使用@AutoValue注釋Kotlin類。 我們來看看為其生成Parcelable實(shí)現(xiàn)的轉(zhuǎn)換后的Address類:


@AutoValue
abstract class Address : Parcelable {
    abstract fun coordinates(): DoubleArray
    abstract fun cityName(): String

    companion object {
        fun create(coordinates: DoubleArray, cityName: String): Address {
            return builder().coordinates(coordinates).cityName(cityName).build()
        }
        
        fun builder(): Builder = `$AutoValue_Address`.Builder()
    }
    
    @AutoValue.Builder
    interface Builder {
        fun coordinates(x: DoubleArray): Builder
        fun cityName(x: String): Builder
        fun build(): Address
    }
}

Kotlin沒有靜態(tài)方法妓布,所以它們應(yīng)該放置在伴隨對(duì)象中companion object姻蚓。 如果您仍然想從Java代碼中使用它們,請(qǐng)使用@JvmStatic對(duì)其進(jìn)行注釋匣沼。

如果您需要訪問Kotlin中名稱不是有效標(biāo)識(shí)符的Java類或方法狰挡,則可以使用反引號(hào)(`)字符來轉(zhuǎn)義該名稱,就像訪問生成的類“$ AutoValue_Address”一樣释涛。

總體而言加叁,轉(zhuǎn)換后的代碼看起來與原始Java代碼非常相似。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末唇撬,一起剝皮案震驚了整個(gè)濱河市它匕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌窖认,老刑警劉巖超凳,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異耀态,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)暂雹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門首装,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人杭跪,你說我怎么就攤上這事仙逻〕巯牛” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵系奉,是天一觀的道長(zhǎng)檬贰。 經(jīng)常有香客問我,道長(zhǎng)缺亮,這世上最難降的妖魔是什么翁涤? 我笑而不...
    開封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮萌踱,結(jié)果婚禮上葵礼,老公的妹妹穿的比我還像新娘。我一直安慰自己并鸵,他們只是感情好鸳粉,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著园担,像睡著了一般届谈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上弯汰,一...
    開封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天艰山,我揣著相機(jī)與錄音,去河邊找鬼蝙泼。 笑死程剥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的汤踏。 我是一名探鬼主播织鲸,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼溪胶!你這毒婦竟也來了搂擦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤哗脖,失蹤者是張志新(化名)和其女友劉穎瀑踢,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體才避,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡橱夭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了桑逝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棘劣。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖楞遏,靈堂內(nèi)的尸體忽然破棺而出茬暇,到底是詐尸還是另有隱情首昔,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布糙俗,位于F島的核電站勒奇,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏巧骚。R本人自食惡果不足惜赊颠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望网缝。 院中可真熱鬧巨税,春花似錦、人聲如沸粉臊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扼仲。三九已至远寸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間屠凶,已是汗流浹背驰后。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留矗愧,地道東北人灶芝。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像唉韭,于是被迫代替她去往敵國和親夜涕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

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