本文轉(zhuǎn)自本人blog,更多關(guān)于kotlin詳情點(diǎn)擊本人blog
本教程介紹如何在Kotlin流行的Android框架和庫(kù)中使用依賴注釋處理。
Android世界有許多流行的框架,簡(jiǎn)化了開(kāi)發(fā)。如果你在Kotlin開(kāi)發(fā),你可以使用相同的框架,就像在Java中一樣容易。本教程提供了示例并突出顯示了設(shè)置中的差異。
我們將看Dagger,Butterknife,Data Binding,Auto-parcel和DBFlow(其他框架可以類似的設(shè)置)。所有這些框架通過(guò)注釋處理工作:您注釋代碼以為您生成樣板代碼。注釋允許隱藏所有的詳細(xì)信息并保持簡(jiǎn)單的代碼,如果您需要了解運(yùn)行時(shí)實(shí)際發(fā)生的情況,可以查看生成的代碼。請(qǐng)注意,所有這些框架都是用Java生成源代碼,而不是Kotlin。
在Kotlin中,使用Kotlin Annotation處理工具(kapt)而不是annotationProcessor來(lái)指定類似于Java的依賴關(guān)系。
Dagger(匕首)
Dagger是一個(gè)依賴注入框架。 如果您還不熟悉,可以閱讀其用戶指南。 我們已經(jīng)將本指南中介紹的 the coffee example示例轉(zhuǎn)換為Kotlin,您可以在這里找到結(jié)果。 Kotlin代碼看起來(lái)幾乎相同; 您可以在一個(gè)文件中瀏覽整個(gè)示例。
和Java一樣,你使用@Inject來(lái)注釋Dagger使用的構(gòu)造函數(shù)來(lái)創(chuàng)建一個(gè)類的實(shí)例。 Kotlin有一個(gè)簡(jiǎn)短的語(yǔ)法來(lái)同時(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 {
// ...
}
注釋方法看起來(lái)完全一樣。 在下面的示例中,@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中打開(kāi)項(xiàng)目,則可以導(dǎo)航并查看DaggerCoffeeShop的實(shí)現(xiàn)。
我們觀察到,當(dāng)您切換到Kotlin時(shí),注釋您的代碼幾乎沒(méi)有改變。 現(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插件來(lái)啟用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插件)解決了同樣的問(wèn)題:用簡(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é)果代碼可以在這里找到。
讓我們來(lái)看看發(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類型。 沒(méi)有l(wèi)ateinit ,你必須聲明一個(gè) nullable type 的類型并添加額外的可空性檢查。
您還可以使用ButterKnife注釋將方法配置為偵聽(tīng)器:
@OnClick(R2.id.hello)
internal fun sayHello() {
Toast.makeText(this, "Hello, views!", LENGTH_SHORT).show()
}
此代碼指定要在“hello”按鈕單擊上執(zhí)行的操作。 請(qǐng)注意,對(duì)于lambdas,這段代碼看起來(lái)相當(dāng)簡(jiǎn)潔直接寫在Kotlin中:
hello.setOnClickListener {
toast("Hello, views!")
}
toast函數(shù)在Anko庫(kù)中定義。
Data Binding(數(shù)據(jù)綁定)
Data Binding Library允許您以簡(jiǎn)明的方式將應(yīng)用程序數(shù)據(jù)綁定到布局。
使用與Java中相同的配置啟用該庫(kù):
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ù)中的變量來(lái)描述可能在布局中使用的變量。 你可以聲明一個(gè)Kotlin類型的變量:
<data>
<variable name="data" type="org.example.kotlin.databinding.WeatherData"/>
</data>
您使用@ {}語(yǔ)法來(lái)編寫表達(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á)式語(yǔ)言使用與Kotlin:data.imageUrl相同的語(yǔ)法來(lái)引用屬性。 在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)
}
}
您可以綁定偵聽(tīng)器以在發(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è)沒(méi)有數(shù)據(jù)的意圖,并啟動(dòng)一個(gè)來(lái)自Anko庫(kù)的新活動(dòng)。 要傳遞一些數(shù)據(jù),你可以說(shuō)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)潔的語(yǔ)法。
你可以在這里找到一個(gè)示例項(xiàng)目。
DBFlow
DBFlow是一個(gè)SQLite庫(kù),它簡(jiǎn)化了與數(shù)據(jù)庫(kù)的交互。 它很大程度上依賴于注釋處理。
要在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沒(méi)有多大差別。 例如,聲明一個(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"
}
這使您可以通過(guò)類似LINQ的C#語(yǔ)法來(lái)表達(dá)查詢,使用lambdas為異步計(jì)算編寫更簡(jiǎn)單的代碼等等。 在這里閱讀所有細(xì)節(jié)。
您可以瀏覽轉(zhuǎn)換后的示例應(yīng)用程序。
Auto-Parcel
Auto-Parcel允許為使用@AutoValue注解的類生成Parcelable值。
當(dāng)您指定依賴項(xiàng)時(shí),您再次使用kapt作為注釋處理程序來(lái)處理Kotlin文件:
apply plugin: 'kotlin-kapt'
dependencies {
...
kapt "frankiesardo:auto-parcel:$latest-version"
}
您可以使用@AutoValue注釋Kotlin類。 我們來(lái)看看為其生成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沒(méi)有靜態(tài)方法,所以它們應(yīng)該放置在伴隨對(duì)象中companion object。 如果您仍然想從Java代碼中使用它們,請(qǐng)使用@JvmStatic對(duì)其進(jìn)行注釋。
如果您需要訪問(wèn)Kotlin中名稱不是有效標(biāo)識(shí)符的Java類或方法,則可以使用反引號(hào)(`)字符來(lái)轉(zhuǎn)義該名稱,就像訪問(wèn)生成的類“$ AutoValue_Address”一樣。
總體而言,轉(zhuǎn)換后的代碼看起來(lái)與原始Java代碼非常相似。