在Kotlin中使用注释处理Android框架 kapt

本教程介绍如何在 Kotlin 中使用依赖于注释处理的流行的 Android 框架和库。

在日常 Android 开发中,流行着数以千计的框架帮助我们提升开发效率。 使用 Kotlin 开发时仍然可以沿用这些框架,而且和使用 Java 同样简单。 本章教程将提供相关示例并重点介绍配置的差异。

教程以 Dagger、 Butterknife、 Data Binding、 Auto-parcel 以及 DBFlow 为例(其它框架配置基本类似)。 以上框架均基于注解处理方式工作:通过对代码注解自动生成模板代码。 注解有助于减少冗余代码,让代码清晰可读,想要了解运行时的代码,可以直接阅读自动生成的源代码。 但所有生成的代码均为 Java 代码而非 Kotlin。

在 Kotlin 中添加依赖与 Java 中类似,仅需要使用 Kotlin 注解处理工具(Kotlin Annotation processing tool,kapt)替代 annotationProcessor 即可。

Dagger

Dagger 是著名的依赖注入框架。 如果对它还不了解,可以查阅用户手册。 我们已经将整个咖啡示例 使用 Kotlin 重写,详细代码在这里。 Kotlin 代码与 Java 非常相似,所有示例代码可在同一个文件内查看。

查看示例项目的完整构建脚本, 以及转换后的 Android 示例代码

与 Java 一样,Dagger 通过 @Inject 对构造函数注解,进而创建类的实例。 而 Kotlin 使用更简洁的语法同时声明属性和构造函数参数。 在 Kotlin 中对构造函数进行注解,必须显式使用 constructor 关键字,并在关键字前声明 @Inject。

  1. class Thermosiphon 
  2. @Inject constructor(
  3.         private val heater: Heater
  4. ) : Pump {
  5.     // ……
  6. }    

注解方法看上去完全相同。 在下面的示例中,@Binds 决定了无论何时需要 Pump,使用都是 Thermosiphon 对象,@Provides 指定了 Heater 的构造方式,@Singleton 则表示 Heater 是全局单例:

  1. @Module
  2. abstract class PumpModule {
  3.     @Binds
  4.     abstract fun providePump(pump: Thermosiphon): Pump
  5. }
  6. @Module(includes = arrayOf(PumpModule::class))
  7. class DripCoffeeModule {
  8.     @Provides @Singleton
  9.     fun provideHeater(): Heater = ElectricHeater()
  10. }

@Module-注解的类定义如何提供不同对象。 需要注意的是,作为多参数传递注解参数时,需要显示的使用 arrayOf 进行包装,比如上文示例中的 @Module(includes = arrayOf(PumpModule::class))。

使用 @Component 为类型生成依赖注入的实现。 自动生成类文件的类名带有 Dagger 前缀,比如下文示例 DaggerCoffeeShop:

  1. @Singleton
  2. @Component(modules = arrayOf(DripCoffeeModule::class))
  3. interface CoffeeShop {
  4.     fun maker(): CoffeeMaker
  5. }
  6. fun main(args: Array<String>) {
  7.     val coffee = DaggerCoffeeShop.builder().build()
  8.     coffee.maker().brew()
  9. }

Dagger 为 CoffeeShop 所生成的实现,允许你获得一个完全注入的 CoffeeMaker。 DaggerCoffeeShop 的具体代码实现可在 IDE 中查看。

我们注意到转换到 Kotlin 时注解代码几乎没有发生改变。 接下来将介绍构建脚本(build script)中需要修改的部分。

在 Java 中需要指定 Dagger 作为 annotationProcessor(或 apt)依赖:

  1. dependencies {
  2.   ...
  3.   annotationProcessor "com.google.dagger:dagger-compiler:$dagger-version"
  4. }

在 Kotlin 中则需要添加 kotlin-kapt 插件激活 kapt,并使用 kapt 替换 annotationProcessor:

  1. apply plugin: ‘kotlin-kapt‘
  2. dependencies {
  3.     ...
  4.     kapt "com.google.dagger:dagger-compiler:$dagger-version"
  5. }

就是这样。 特别提示:kapt 也能够处理 Java 文件,所以不需要再保留 annotationProcessor 的依赖。

ButterKnife

ButterKnife可以直接将view和变量进行绑定从而免去调用findViewById。

另外,Kotlin Android 扩展插件(Android Studio 内置)具有同样的效果:使用简洁明了的代码替换findViewByid。 除非现在你正在使用 ButterKnife 而且没有迁移计划,那么前者非常值得尝试。

我们已经将整个 ButterKnife 示例代码转换为 Kotlin, 参见详细代码

在 Kotlin 中使用 ButterKnife 与 Java 中完全一致。 在 Gradle 构建脚本的修改如下,后面将重点介绍代码部分的差异。

在 Gradle 依赖中添加 kotlin-kapt 插件,并使用 kapt 替代 annotationProcessor。

  1. apply plugin: ‘kotlin-kapt‘
  2. dependencies {
  3.     ...
  4.     compile "com.jakewharton:butterknife:$butterknife-version"
  5.     kapt "com.jakewharton:butterknife-compiler:$butterknife-version"
  6. }

让我门看看发生了什么变化。 在 Java 中使用注解将变量与之对应的 view 进行绑定:

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

在 Kotlin 中使用属性而不是直接使用变量。 对属性使用注解:

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

@BindView 被定义为仅应用于变量字段,而将注解应用于整个属性时,Kotlin 编译器能够理解并且覆盖相应注解的字段。

lateinit 修饰符允许声明非空类型,并在对象创建后(构造函数调用后)初始化。 不使用 lateinit 则需要声明可空类型并且有额外的空安全检测操作。

使用 ButterKnife 注解可以将方法设置为监听器:

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

以上代码表示点击“hello”按钮后的事件响应。 然而在 Kotlin 中使用 lambda 表达式会让代码更加简洁清晰:

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

Anko 库默认提供 toast 函数。

Data Binding

使用  Data Binding 开源库能够让开发者以更简洁的方式将应用程序数据与布局界面进行绑定。

查看完整示例

和使用 Java 一样,开发者需要在 gradle 文件中添加并激活配置。

  1. android {
  2.     ...
  3.     dataBinding {
  4.         enabled = true
  5.     }
  6. }

添加 kapt 的依赖后即可与 Kotlin 代码交互:

  1. apply plugin: ‘kotlin-kapt‘
  2. dependencies {
  3.     kapt "com.android.databinding:compiler:$android_plugin_version"
  4. }  

使用 Kotlin 并不需要修改任何的 xml 文件。 例如,在 data 中使用 variable 来描述可能在布局中使用的变量, 可以使用Kotlin类型声明变量:

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

现在,可以使用 @{} 语法引用 Kotlin 的属性:

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

值得一提的是,数据绑定表达式语言使用和 Kotlin 相同的语法对属性进行引用:data.imageUrl。 在 Kotlin 中可以使用 v.prop 来替代 v.getProp(),尽管 getProp() 是Java中的方法。 类似的,也可以直接向属性赋值,而不再需要调用setter。

  1. class MainActivity : AppCompatActivity() {
  2.     // ……
  3.     override fun onCreate(savedInstanceState: Bundle?) {
  4.         super.onCreate(savedInstanceState)
  5.         val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  6.         binding.data = weather//等同于 binding.setData(weather)
  7.     }
  8. }

在 xml 中绑定监听器,并在运行时对相应操作进行响应:

  1. <Button
  2.     android:text="@string/next"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="wrap_content"
  5.     android:onClick="startOtherActivity" />

例如在 MainActivity 中定义的 startOtherActivity 方法:

  1. class MainActivity : AppCompatActivity() {
  2.     // ……
  3.     fun startOtherActivity(view: View) = startActivity<OtherActivity>()
  4. }

本例中使用的效用函数 startActivity 创建一个不带任何数据参数的 intent,并启动一个新的 activity,这些方法都来自于 Anko 库。 若需要添加参数,则调用

  1. startActivity<OtherActivity>("KEY" to "VALUE").

请注意,与其在 xml 中声明 lambda 表达式,不如直接使用代码绑定相关动作:

  1. <Button 
  2.     android:layout_width="wrap_content" 
  3.     android:layout_height="wrap_content"
  4.     android:onClick="@{() -> presenter.onSaveClick(task)}" />
  1. // 用 Kotlin 代码写的相同逻辑
  2. button.setOnClickListener { presenter.onSaveClick(task) }

最后一行中 button 由 id 使用 Kotlin Android 扩展插件所引用。 使用该插件作为替代方案,既允许在代码中保持绑定逻辑,同时又具有简洁的语法。

DBFlow

DBFlow 是一个用于简化数据库交互的SQLite开源库。 它非常之依赖于注解处理。

查看 DBFlow 配置向导

查看完整示例程序

使用 kapt 配置 Kotlin 依赖:

  1. apply plugin: ‘kotlin-kapt‘
  2. dependencies {
  3.     kapt "com.github.raizlabs.dbflow:dbflow-processor:$dbflow_version"
  4.     compile "com.github.raizlabs.dbflow:dbflow-core:$dbflow_version"
  5.     compile "com.github.raizlabs.dbflow:dbflow:$dbflow_version"
  6. }

若您的项目中已在使用 DBFlow,可以安全地将在项目中引入 Kotlin。 并且逐步地将代码转换为 Kotlin(确保每次编译通过)。 转换后的代码与 Java 并无明显差异。 例如,对表的声明和在 Java 中仅有小小的区别,属性声明时必须显示的指定默认值:

  1. @Table(name="users", database = AppDatabase::class)
  2. class User: BaseModel() {
  3.     @PrimaryKey(autoincrement = true)
  4.     @Column(name = "id")
  5.     var id: Long = 0
  6.     @Column
  7.     var name: String? = null
  8. }

对于 DBFlow 而言,除了将已经有功能代码转换为 Kotlin,还能享受到 Kotlin 的特别支持。 例如,将表声明为数据类:

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

DBFlow 定义了一系列符合 Kotlin 语言习惯的扩展功能,这些都可以通过依赖添加:

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

该扩展可以通过类似 C# 中的 LINQ 语法方式编写查询语句,使用 lambda 表达式可以编写更简单的异步计算代码。

Auto-Parcel

Auto-Parcel 使用 @AutoValue 的注解为类文件自动生成 Parcelable 对应方法和值。

点击这里查看完整示例代码

同样的,gradle 文件中也需要使用 kapt 作为注解处理器来处理 Kotlin 文件:

  1. apply plugin: ‘kotlin-kapt‘
  2. dependencies {
  3.     ...
  4.     kapt "frankiesardo:auto-parcel:$latest-version"
  5. }

对 Kotlin 类文件添加 @AutoValue 注解。 下方的示例展示转换后的 Address 类以及自动生成相应的 Parceable 实现:

  1. @AutoValue
  2. abstract class Address : Parcelable {
  3.     abstract fun coordinates(): DoubleArray
  4.     abstract fun cityName(): String
  5.     companion object {
  6.         fun create(coordinates: DoubleArray, cityName: String): Address {
  7.             return builder().coordinates(coordinates).cityName(cityName).build()
  8.         }
  9.         
  10.         fun builder(): Builder = `$AutoValue_Address`.Builder()
  11.     }
  12.     
  13.     @AutoValue.Builder
  14.     interface Builder {
  15.         fun coordinates(x: DoubleArray): Builder
  16.         fun cityName(x: String): Builder
  17.         fun build(): Address
  18.     }
  19. }

由于 Kotlin 中没有 static 方法,因此相应的方法会在 companion object中生成。 如果仍然需要从 Java 中调用这些方法,需要添加@JvmStatic注解。

如果调用 Java 的类或方法恰好在 Kotlin 中是保留字,可以使用反引号(`)作为转义字符,比如调用上例中生成类的`$AutoValue_Address`。以上所有经过转换的代码与原生 Java 代码非常相似。

null

时间: 2024-10-21 20:17:42

在Kotlin中使用注释处理Android框架 kapt的相关文章

Kotlin中使用注解框架不起作用

问题 在Kotlin中使用注解框架,发现在编译的时候注解的字段没有生效. 原因 由于Kotlin是基于jvm运行时的开发语言,所以需要单独对它引入注解环境 解决方法: 这个方式是基于Gradle方式的,如使用开发工具InterJect,Android Studio,其他配置Gradle的编译环境: 只要在你使用的Kotlin的Module或者程序的build.gradle文件里加入kapt插件和相应的注解处理就好. 步骤:1.在程序或者Module里引入kotlin的注解插件 2.添加注解依赖处

怎么在android的XML文件中添加注释

android的XML文件注释一般采用 <!--注释内容 -->的方式进行 在XML中,形如    <Button           />      的表示方式,其中"/>"的含义表示这个XML中没有内文,他是一个最小组成单元,也就是说他的中间不能包含其他任何< >的代码,所以在<Button />中间注释会出现错误 注意看到,在注释的前面有一个">"符号,这就是我们能够在他中间进行注释的原因,他的完整结

Android Eclipse中查看 Android框架源码

有时候用Eclipse想按住ctrl键查看源码怎么办? 下面具体步骤让你轻松看源码: project->properties->java build path->libraries 点击android.jar下面的source: 这里可以添加zip和文件夹,zip可以去git下载,我这里用的是用sdk manager下载的源码,如下: 从这里面下载的源码就保存在sdk下面的source下面,选一个平台关联就可以了 下面就是button源码: @RemoteView public clas

Kotlin中when表达式的使用:超强的switch(KAD 13)

作者:Antonio Leiva 时间:Feb 23, 2017 原文链接:https://antonioleiva.com/when-expression-kotlin/ 在Java(特别是Java 6)中,switch表达式有很多的限制.除了针对短类型,它基本不能干其他事情. 然而,Kotlin中when表达式能够干你想用switch干的每件事,甚至更多. 实际上,在你的代码中,你可以用when替换复杂的if/else语句. Kotlin的when表达式 开始,你可以像switch那样使用w

Android框架浅析之锁屏(Keyguard)机制原理

最近终于成功的摆脱了FM收音机,迈向了新的模块:锁屏.状态栏.Launcher---姑且称之为"IDLE"小组,或许叫手机 美容小组,要是能施展下周星星同学的还我漂漂拳,岂不快哉. OK,闲话打住,咱开始正文. 本文主要内容: 1.分析锁屏界面的组成 : 2.基于源代码分析锁屏相关类 : 3.提出一种在框架取消锁屏的方法 . 花了一些时间研究Android原生的锁屏框架---Keyguard,今天就慢慢的讲解下我自己对这个模块的总结,因为目前还处于 理论学习的状况,很多细节以及功能上的

App 组件化/模块化之路——Android 框架组件(Android Architecture Components)使用指南

面对越来越复杂的 App 需求,Google 官方发布了Android 框架组件库(Android Architecture Components ).为开发者更好的开发 App 提供了非常好的样本.这个框架里的组件是配合 Android 组件生命周期的,所以它能够很好的规避组件生命周期管理的问题.今天我们就来看看这个库的使用. 通用的框架准则 官方建议在架构 App 的时候遵循以下两个准则: 关注分离 其中早期开发 App 最常见的做法是在 Activity 或者 Fragment 中写了大量

如何在程序开发项目中选择合适的 JavaScript 框架,节省时间和成本的9款极佳的JavaScript框架介绍

从技术上来看,iOS,Android 和 Windows Phone 上的移动应用是使用不同的程序语言开发的,iOS 应用使用 Objective-C,Android 应用使用 Java,而 Windows Phone 应用使用 .NET. .随着 JavaScript,CSS 和 HTML 知识技能的提升,相信你也可以构建一个超赞的移动应用.在这篇博客里,我们将会介绍一些极好的 JavaScript 移动应用程序开发框架. 说到网络开发,就不得不说 JavaScript,这是一款很有前途的程序

Java中的注释和嵌入式文档

摘自Think in java 前面看到一位同事写的android程序,注释如同android源码一样,再看看自己写的,自己都都不懂的注释.所以抽空看了Think in java里面注释和嵌入式文档的章节,并做一个简单的总结备忘. Java中的注释分为两种,// /* */ 嵌入式文档使用了一种特殊的注释语法,通过javadoc工具(javadoc工具为jdk安装的一部分)生成HTML文档,可以使用web浏览器来查看. 语法: 所有的javadoc命令都只在/** 后的注释中才会生效. 嵌入式文

Android框架设计模式(四)——Adapter Method

一适配器模式介绍 什么是适配器模式 定义 分类 适配器应用于什么场景 二Android框架中的适配器模式应用 ListViewBaseAdapter自定义View 通俗UML图 关键代码分析 ActivityBinderMediaPlayer 通俗UML图 关键代码分析 三适配器模式与其他模式的配合 适配器观察者模板策略组合 BaseAdapterListView自定义View 整体UML图 模式分析不同的视角决定 适配器观察者模板 Service Activity 自定义服务 整体UML图 模