一、概述
特征概括:
① 处理空的情况
② 提升程序的可读性
③ 大多数时候可以互相替代,所以不要死抠概念和区别
二、run
run是一块独立的执行区域,它会将最后一行内容回传带到下一个执行链条(chain)
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val name: String = "zhangSan"
//因为最后一行代码为println没有返回值,所以run执行单元的返回值为Unit
val result: Unit = run { //属于MainActivity的run,不是上面name的执行单元
val name: String = "liShi"
println("name:$name")
}
}
}
打印结果: System.out: name:liShi
如果最后一行的返回值有扩展函数,那么就可以继续调用
val result:Int = run {
10
}.plus(5) //Int的扩展函数
println("打印结果:$result")
看一个复杂点的例子,偏实用,定义一个Person类
data class Person(var name: String, var age: Int) {
//定义一个打印的函数
fun printInfo() {
println("Person(name='$name', age=$age)")
}
}
定义一个执行单元run
run {
val person = Person("zhangSan",18)
person
}.printInfo() //Person的函数
打印结果: System.out: Person(name='zhangSan', age=18)
其他常用的就是空安全
val person = null
person?.run {
...
}
二、let
又或者可以写成 T.let,也是一个 extension function。T 在 scope 內则是用 it 来存取而不是 this。也可以依照需求改成其他的名字,增加可读性。与run相同,会将最后一行传到下一个链条或者回传。
例子同上面的run,此处略。
let与run的区别是如果想通过this访问外部内容时建议用let,run也可以只是let的可读性会更清晰
class MainActivity : AppCompatActivity() {
var personOne = Person("zhangSan", 18)
var bitmap: Bitmap? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val newName = "liSi"
newName.run {
personOne.name = this //this指newName
bitmap //成员变量
}
//如果想通过this获取上层内容时建议用let,而不是用run
newName.let { value -> //如果不改为value,默认是it
personOne.name = value
this.bitmap //可以通过this访问外层,不加也可以,但是加了调用层次关系更清晰
}
}
}
三、with
with一般作为初始化使用,with(T)之中传入的值可以用this调用,不打出this也没关系。with也会将最后一行回传,目前来看基本都是用它做初始化,因为with括号内的内容可以很明确是在给谁进行初始化。
val textView = TextView(context)
with(textView){
setTextColor(...)
text = ""
textSize = 12sp
}
如果要初始化的T为null,那么就要加this?.
四、also
与run和with把最后一行传递不同的是also传递的是自己
val options = ImageOptions().also { //做配置
it.res = res
it.imageView = imageView
it.context = context
it.placeHolderResId = placeHolderResId
}
很多时候用also也有再带着干点啥的意思
Intent().run {
putExtra(GlobalConstants.DATA_KEY, true)
}.also {
setResult(RESULT_OK, this)
}
上面的例子是给Activity回传数据,这样的代码是不是就有kotlin代码的味道了?
五、apply
apply与 also 很像,不同的地方是 apply 在 scope 內 T的存取方式是 this ,其他都与 also 一样。常用于Builder建造者模式。看一个例子,比如需要一个图片配置的类,需要动态设置宽高,那么apply可以用于下面的代码:
//图片配置的类
object ImageOptions {
class Builder constructor(context: Context) {
private var width: Int = 0
private var height: Int = 0
fun setWidth(width: Int) = apply {
this.width = width
}
fun setHeight(height: Int) = apply {
this.height = height
}
}
}
当然apply一般场景也常用,举一个粗糙的例子
OkHttpClient.Builder().apply {
//添加缓存拦截器
addInterceptor(MyHeadInterceptor())
// 日志拦截器
addInterceptor(LogInterceptor())
addInterceptor(TokenInterceptor())
//超时时间 连接、读、写
connectTimeout(10, TimeUnit.SECONDS)
readTimeout(10, TimeUnit.SECONDS)
writeTimeout(10, TimeUnit.SECONDS)
}
六、launch
launch其实属于协程的部分,跟上面的几个api放一起不合适,写在这里权当做记录吧
launch的源码
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
可以看到是协程的扩展函数,一般怎么用呢?举一个比较实用的例子。
① 获取ViewModel的协程
class SearchViewModel : BaseViewModel() {
//与viewModel生命周期关联的协程
val coroutineScope: CoroutineScope
get() = viewModelScope //需要导包
}
② 使用协程加Flow流
mViewModel.coroutineScope.launch { //launch启动协程
flow { //流
for (i in 0 until list.size) {
val modelId = list[i].id
if (modelId == recommendModelItemBean.id) {
emit(i) //发送拿到的结果
break //跳出循环
}
}
}.collect { position -> //拿到上游发送的结果
//更新数据
mAdapter.data[position].is_like = recommendModelItemBean.is_like
mAdapter.notifyItemChanged(position)
}
}
个人学习笔记
参考文章: