CoroutineContext 是协程中的一个重要概念,我们可以通过它来调度协程执行、进行异常处理、跟踪协程层级关系以及携带协程作用域信息。本文通过启动协程的源码,尝试分析其数据结构以及传递机制。
CoroutineContext 是协程中的一个重要概念,我们可以通过它来调度协程执行、进行异常处理、跟踪协程层级关系以及携带协程作用域信息。本文通过启动协程的源码,尝试分析其数据结构以及传递机制。
Kotlin 符号处理(KSP)是一种 API,您可以用它来开发轻量级编译器插件。KSP 提供了一个简化的编译器插件 API,可充分利用 Kotlin 的强大功能,同时将学习曲线保持在最低水平。与 kapt 相比,使用 KSP 的注释处理器运行速度最多可提高两倍。
Kotlin 是 Jetbrain 推出的一门运行在 JVM 上的语言,它结合了面向对象以及函数式语言的特性,超甜的语法糖以及来自知名 IDE 大厂 Jetbrain 的出身让它初一面世就广受瞩目,特别是在 Android 开发社区中。它相比起 Java 拥有了许许多多的优秀特性,并且几乎每一个新特性都对应解决了 Java 开发时的痛苦之处,本篇文章主要讲解 Kotlin 中的委托属性这一特性。
我们先看看官网的定义:
有一些种类的属性,虽然我们可以在每次需要的时候手动实现它们,但是如果能够把他们之实现一次 并放入一个库同时又能够一直使用它们那会更好。例如:
- 延迟属性(lazy properties): 数值只在第一次被访问的时候计算。
- 可控性(observable properties): 监听器得到关于这个特性变化的通知,
- 把所有特性储存在一个映射结构中,而不是分开每一条。
为了支持这些(或者其他)例子,Kotlin 采用 委托属性。
简言之就是简化手动实现的属性,将其抽象出一个库。
Kotlin 中有两种属性:用var
修饰的可变属性和由val
修饰的只读属性。由val
修饰的只读属性使用的委托需要实现ReadOnlyProperty
,而var
修饰的可变属性则需要实现ReadWriteProperty
在调用被委托的属性的getter
和setter
时,对应操作会被委托给getValue()
以及setValue()
。
如实现一个最简单的委托Delegate
:
1 | class Delegate { |
语法为val/var <property name>: <Type> by <expression>
1 | class Example { |
by
后面的是委托表达式,我们调用这个对象并使用属性:
1 | val e = Example() |
打印结果为:
1 | Example@33a17727, thank you for delegating 'p' to me! |
如上可知,thisRef
对应的是拥有该被委托属性的对象实例,property
则是属性,value
是调用setter
时的传入值。
Kotlin 标准库自带的懒加载委托,在属性第一次被使用时才进行初始化。
函数lazy()
接受一个 lambda 然后返回一个可以作为委托Lazy<T>
实例来实现延迟属性: 第一个调用getter
执行变量传递到lazy()
并记录结果, 后来的getter
调用只会返回记录的结果。
1 | val lazyValue: String by lazy { |
其打印结果:
1 | computed! # 第一次使用时先初始化 |
懒加载委托在实际编码中应用十分广泛,比如 Android 中我们可以把很多在OnCreate
中需要进行的初始化操作使用懒加载委托来实现。
本例出自《Kotlin for Android Developer》,使用了when
表达式和委托属性巧妙地使得SharedPrefences
的读写变得十分简便
1 | class Preference<T>(val context: Context, val name: String, val default: T) : ReadWriteProperty<Any?, T> { |
在代码中我们可以如下使用
1 | class WhateverActivity : Activity() { |
从此操作SharedPreferences
变得如此简单 ~
KotterKnife 是一个 Android 控件依赖注入框架,使用它可以很方便地初始化 Activity、Fragment、View 等的控件。
KotterKnife 的实现原理就是使用了委托属性,下面我就使用委托属性简单实现一个 View 注入功能
我们平时是这样初始化 View 的
1 | override fun onCreate(savedInstanceState: Bundle?) { |
考虑到通常我们在onCreate
方法中将其初始化,我们可以用 lazy 委托,在第一次使用该控件的时候才将其初始化,这样可以减少不必要的内存消耗。
1 | val mTextView by lazy { |
对其抽取简化
1 |
|
之后我们就可以在 Activity 中这样注入 View 了
1 | val mTextView by bindView<TextView>(R.id.text_view) |
如上实现了类似 KotterKnife 的控件注入功能,当然 KotterKnife 中还有更加强大的可选绑定以及数组绑定,本文中我们就不细说了,有兴趣的读者可以阅读 KotterKnife源码。
本文分析了 Kotlin 中的委托属性,并对其实际应用做了示例分析。委托属性是 Kotlin 语言的一个特性,灵活使用可以解决实际编码中的许多问题,减少大量重复代码,而由于其与属性的getter
、setter
直接绑定所以使用起来也十分灵活方便。
总而言之:这真是极好的。
在Android中,如果需要序列化对象可以选择实现 Serializable 或 Parceable。如果是在使用内存的情况下,Parcelable 的效率比 Serializable 高。但 Parcelable 不能被持久化存储,此时还是需要实现 Serializable。
首先我们看一个普通的 JavaBean
1 | /** |
其中的代码都是 JavaBean 的属性以及 getter、setter
如果其实现 Parcelable,则是这样的
1 | /** |
在实现Parcelable
的时候我们需要重写两个方法
public void writeToParcel(Parcel dest, int flags)
public int describeContents()
其中describeContents
只需要返回 0 即可
writeToParcel
方法中我们把需要序列化的属性使用writeXXX
的方式写入 Parcel 。
之后是 CREATOR 对象,这个对象负责从 Parcel 中读取对象,所以我们需要重写其方法来读取对象
1 | protected PostEntity(Parcel in) { |
这一段就是其实现方式,可见主要是将对象从 Parcel 中读取出来。
看过了冗长的 Java 实现方式,我们来看看kotlin是如何实现的吧。
首先使用插件将其转换为 Kotlin 文件,并修改其中的错误
1 | class PostEntity : Parcelable { |
这就是 Kotlin 实现 Parcelable 的方式了
经过插件转化的 kotlin 代码其实使用的还是 java 的方式和 java 的思想,我们可以将其完全转化为 kotlin 的方式并对其优化
首先把其转化为数据类,这样会自动为我们生成
equals()/hashCode()
toString()
componentN()
copy()
我们只需要将其改为这样
1 | data class PostEntity(var name: String? = null, /* 帖子标题*/ |
再之后观察发现,所有的 Parcelable 都需要有一个 CREATOR
1 | companion object { |
此处使用了 Kotlin 的伴生对象,使得调用 CREATOR 类似于 Java 中的静态属性
可以使用 Kotlin 的函数式编程特性抽取
新建文件ParcelableExt.kt
1 | public inline fun createParcel<reified T : Parcelable>(crossinline createFromParcel: (Parcel) -> T?): Parcelable.Creator<T> = |
此处使用了 Kotlin 的内联函数,然后我们就可以将 PostEntity
精简为如下
1 | data class PostEntity(var name: String? = null, /* 帖子标题*/ |
虽然可以直接将 Java 文件转化为 Kotlin 文件,但这样毕竟没有办法学习到 Kotlin 的精髓
使用一门语言就应该按照这门语言的编码风格以及规范去实现,这样才会让我们的学习更加有效率且养成良好的编码习惯
Kotlin 是一门典型的函数式编程语言,学习它的风格有利于我们了解函数式编程思想
在实现 Parceable 时我们使用了 Kotlin 的几个特性
查阅官方文档完成的同时我也学会了新的姿势知识,想一想也有点小激动呢