在Android中,kotlin 的一些开发技巧

4,943 阅读3分钟

屏幕截图 2024-06-30 093907.png

@JvmOverloads

在Kotlin中,在有默认参数值的方法中使用 @JvmOverloads 注解,就可以很方便地实现多个重载方法。最常使用的地方就是自定义 View,代码示例如下:

class MyView @JvmOverloads constructor(context:Context, 
                                       attributeSet: AttributeSet? = null, 
                                       defStyleAttr: Int = 0): View(context, attributeSet, defStyleAttr) {
}

它等价于如下 java 代码:

public class MyView extends View {

    public MyView(Context context) {
        this(context, null);
    }

    public MyView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}

Parcelable 序列化

在 Android 开发中,如果需要使用 Parcelable 来实现序列化,就需要写一堆模板代码。而在 kotlin 中,你可以很简单地通过 @Parcelize 就可以实现相同的效果。

首先,我们需要先引入插件,代码如下:

plugins {
    id 'kotlin-parcelize'
}

然后,在需要需要实现 Parcelable 序列化的地方,加上 @Parcelize 注解就可以了。这样就不用写一大堆的模板代码。

import kotlinx.parcelize.Parcelize

@Parcelize
class User(val firstName: String, val lastName: String, val age: Int): Parcelable

data class

data class User(val name: String, val age: Int, val gender: Int)

在Java中我们常常使用Java bean来保存数据对象,在 Kotlin 中直接提供了数据类来处理这种情况。在 Kotlin 当中,编译器会为数据类自动生成一些有用的方法。它们分别是:equals()hashCode()toString() componentN()copy()等等。

其中 componentN() 方法可以让我们对对象解构, copy() 方法可以让我们只修改其中一个值,代码示例如下:

val user = User("小明", 18, 0)
// 解构
val (name, age, gender) = user

// 只需求其中的姓名
val user1 = user.copy(name = "小军")

在使用 data class 时,有三点需要注意,分别是:

  • data class的构造函数至少需要有一个参数。
  • data class不能被继承,不能加 open 关键字。
  • data class 不是深拷贝,需要自己处理

runCatching

runCatching {
    // 执行一段可能异常的代码
}.onSuccess { 
    // 执行成功
}.onFailure {
    // 发生异常
}

使用 runCatching 取代 java 风格的 try-catch,可以很清晰地处理成功或者失败地情况。

runInterruptible

在 kotlin 协程中,我们使用 cancel 来取消任务;而在 Java 多线程中,我们使用 interrupt 方法来中断线程。如果你对这两个方法都不了解,可以看我之前写的java多线程面试系列——旧版apikotlin 协程入门教程

现在考虑这样一种情况,如果我们在 kotlin 协程中,用到了 Java 的并发集合,比如说 LinkedBlockingQueueArrayBlockingQueue 等等,这时候我们调用了它的阻塞接口,我们使用 cancel 能中断当前线程,并且 cancel 当前协程吗?

答案是不行的,我们还需要调用 interrupt 方法才可以完全停止这个任务。不过我们不需要太过担心这个问题,kotlin 考虑到了这种情况,提供了 runInterruptible 方法来内部处理这种情况。我们只需要像之前一样调用 cancel 就可以了,它内部会判断线程的状态,并执行 interrupt 方法。代码示例如下:

class Test  {

    suspend fun test() {
        runInterruptible { // 内部会调用 interrupt 方法
            while (true){
                Thread.sleep(100)
                println("do something")
            }
        }
    }

}

fun main(): Unit = runBlocking {
    val t = Test()
    val job = launch(Dispatchers.IO) {
        t.test()
    }
    delay(500)
    job.cancel() // 这里只需要 cancel 就可以了
    delay(1000)
    println("end")
}

measureTimeMillis

在 Android 开发中,我们常常需要测试 SDK 初始化或者执行一些操作的耗时。在 kotlin 中,我们可以简单地使用 measureTimeMillis 来实现这个功能。代码示例如下:

val spendTime = measureTimeMillis { 
    // 初始化SDK或者执行一些操作
}

withTimeout 和 withTimeoutOrNull

在 Android 开发中,我们需要确保每一次操作地耗时不要太长,即有一个超时失败地机制。在 kotlin 中,我们可以通过 withTimeoutwithTimeoutOrNull 方法来简单地实现。其中 withTimeout 在超时时会抛出异常,而 withTimeoutOrNull 只会返回一个 null 值。代码示例如下:

runCatching {
    // withTimeout 会抛出异常,需要 runCatching 包裹
    val result = withTimeout(5000) {

    }
}

val result = withTimeoutOrNull(5000) {

}

参考