【Android学习】Kotlin系统化学习

一. 集合

1. 安全获取集合元素

List.getOrElse(index: Int, defaultValue: (Int) -> T): T 函数原型 :
List.getOrNull(index: Int): T? 函数原型 :

2. mutableList

  • 添加元素运算符 += 和 删除元素运算符 -=
    运算符重载 : MutableList 集合 还有一些 运算符重载 , 添加元素运算符 += 和 删除元素运算符 -= ;
mutableList += "Jack"
mutableList -= "Tom"
//等价于
mutableList.add("Jack")
mutableList.remove("Tom")
  • 通过 Lambda 表达式筛选要删除的元素
fun main() {
    // 创建可变列表集合
    val mutableList = mutableListOf("Tom", "Jerry")
    // 通过 Lambda 表达式筛选要操作的元素
    // 删除包含 字母 T 的元素
    mutableList.removeIf {
        it.contains("T")
    }
    println(mutableList)
}

3. list遍历

fun main() {
    // 创建可变列表集合
    val list = listOf("Tom", "Jerry", "Jack")

    // 使用 for in 循环遍历
    for (name in list) {
        println("for : $name")
    }

    // 使用 forEach 遍历
    list.forEach {
        println("forEach : $it")
    }

    // 遍历时获取索引位置
    list.forEachIndexed { index, s ->
        println("$index : $s")
    }
}
// 输出结果
for : Tom
for : Jerry
for : Jack
forEach : Tom
forEach : Jerry
forEach : Jack
0 : Tom
1 : Jerry
2 : Jack

4. List 通过解构一次性给多个元素赋值

fun main() {
    // 创建可变列表集合
    val list = listOf("Tom", "Jerry", "Jack")

    // 使用 list 集合一次性给 2 个元素赋值, 第 1 个元素跳过
    val (name1, _, name3) = list

    println("name1 = $name1")
    println("name3 = $name3")
}
//
name1 = Tom
name3 = Jack

5. Set集合

https://blog.youkuaiyun.com/shulianghan/article/details/128717667

二. 关键字

1. reified

reified关键字介绍
在Kotlin中,reified是一个特殊的关键字,用于修饰内联函数中的类型参数。这使得在函数内部可以访问类型参数的具体类型。通常情况下,由于类型擦除(type erasure),在运行时是无法直接获取泛型类型参数的具体类型的

2. noline / inline / crossline

参考资料

  • 局部返回
  • inline 减少开销,避免重复创建fun
  • noline 取消内联,一些场景下,需要把参数传给非内联函数

viewmodel源码

public inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
    noinline extrasProducer: (() -> CreationExtras)? = null,
    noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null
): Lazy<VM> {
    val factoryPromise = factoryProducer ?: defaultViewModelProviderFactory
    return ViewModelLazy(
        VM::class,
        { viewModelStore },
        factoryPromise,
        { extrasProducer?.invoke() ?: this.defaultViewModelCreationExtras }
    )
}

三. 协程相关

1. 扔物线b站教程

https://www.bilibili.com/video/BV16S421d7tf?spm_id_from=333.788.videopod.sections&vd_source=695bce36c88a2f5a73d2b07a4041ec19

1.1 切线程

  • 并发管理总结
    1.切线程;
    2.在各个线程之间等待各个线程;
    3.互斥锁:并发安全
  • 切线程原因
    1.代码需要执行,不行被block
    2.客户端特定切到UI主线程
    java开发常用线程池实现
  • 基本用法
        CoroutineScope(EmptyCoroutineContext).launch { 
            // 把代码封装起来丢给线程池处理,类似于Java的Excute
        }
  • 管理任务执行的线程的工具 ContinuationInterceptor
    官方默认实现4个 常用default和IO Main够用
public object Dispatchers {
	// 默认的 8个
    @kotlin.jvm.JvmStatic public final val Default: kotlinx.coroutines.CoroutineDispatcher /* compiled code */
	// CPU 计算密集类型 IO工作是磁盘在做,CPU空闲; 64个
	// 判断IO密集类型:跟磁盘/网络交互的
    @kotlin.jvm.JvmStatic public final val IO: kotlinx.coroutines.CoroutineDispatcher /* compiled code */
	// 主线程
    @kotlin.jvm.JvmStatic public final val Main: kotlinx.coroutines.MainCoroutineDispatcher /* compiled code */
        public final get
	// Unconfined 调度器不会限制协程的执行线程。它的特点是:
	// 当协程被启动时,它会在调用协程的线程上执行。
	// 一旦协程挂起,它会恢复到调用 resume 的线程上,而不管最初的执行线程是什么。
	// 适用场景:
	// 轻量级的协程,特别是那些不涉及 UI 更新或不需要特定线程的任务
	// 如果你的协程在被挂起时会影响性能或不需要线程上下文的场景。
    @kotlin.jvm.JvmStatic public final val Unconfined: kotlinx.coroutines.CoroutineDispatcher /* compiled code */

    @kotlinx.coroutines.DelicateCoroutinesApi public final fun shutdown(): kotlin.Unit { /* compiled code */ }
}

继承关系:Default -> CoroutineDispatcher -> ContinuationInterceptor

自定义管理器

        val context = newFixedThreadPoolContext(5,"my_thread")
        val context1 = newSingleThreadContext("my_thread_pool")
        CoroutineScope(context).launch { 
            // TODO
        }
        context.close()

1.2 挂起函数

协程的优势:切完协程后还能切回来,也是相较于常规线程池最大的特点。
实现方法:挂起函数suspend。
挂起函数,挂起的是协程,继续执行挂起函数的逻辑,等待函数执行完成后,回到协程指定的线程,执行里面的逻辑
协程的挂起指的是,协程从它指定线程暂时离开;
挂起函数仅在协程里才有意义。
在这里插入图片描述
案例分析:contrubutors为suspend函数,切换到后台获取网络数据,再切回主线程更新UI

1.3 协程代码写法

// activity/fragment: 
        lifecycleScope.launch { 
            
        }
// viewModel
       
        viewModelScope.launch { 
            
        }

1.4 手动切线程

withcontext

        CoroutineScope(Dispatchers.Main).launch {
            println(1)
            withContext(Dispatchers.IO) {
                delay(2000)
                println(2)
            }
            println(3)
        }
        // out: 1/2/3
        // 如果改成withContext换成launch,则输出 1/3/2

1.5 挂起函数的性能优势及原理(挂起函数为什么不卡主线程)

- 保证百分百把耗时操作放在挂起函数里去实现

// 把withContext逻辑也一起抽取出来
        CoroutineScope(Dispatchers.Main).launch {
            getData()
        }
    }
    
    private suspend fun getData() {
        withContext(Dispatchers.Default) {
            // todo getData
        }
    }
  • 原理
    Android的主线程是一个无限循环的过程,在不停的刷新UI界面,同时监听各种点击和触摸事件
    在这里插入图片描述
    切换线程的底层原理都是:handler.post
    挂起函数的实现原理就是在挂起函数开始前和后分别切换一次线程(回调),挂起函数调用时,实际上这时候已经离开主线程了,当然不卡主线程了
    在这里插入图片描述

1.6 结构化并发

  • 取消协程
job.cancel()
lifecycleScope.cancel() //不需要写,底层已实现

job 类似于 Rxjava中的dispose

  • 父子协程
    在这里插入图片描述

1.7 并行协程的启动与交互

  • 两个请求串行执行
        lifecycleScope.launch {
            val timeBegin = System.currentTimeMillis()
            val request1 = getData()
            val request2 = getData()
            updateView(request1 + request2)
            val timeEnd = System.currentTimeMillis()
            Log.d("TestKotlin", (timeEnd - timeBegin).toString())
        }
        
        private suspend fun getData() : List<String>{
        withContext(Dispatchers.Default) {
            delay(2000)
        }
        return mutableListOf("11")
    }
    // 输出4043 
  • 两个请求并行执行
    通过async启动协程,返回deferred对象,通过await()可以获取协程的结果
        lifecycleScope.launch {
            coroutineScope {
                val timeBegin = System.currentTimeMillis()
                val deferred1 = async { getData() }
                val deferred2 = async { getData() }
                updateView(deferred1.await() + deferred2.await())
                val timeEnd = System.currentTimeMillis()
                Log.d("TestKotlin", (timeEnd - timeBegin).toString())
            }
        }
        // 输出:2039
  • coroutineScope

自动取消:在 coroutineScope 内部启动的所有协程在作用域结束时都会自动取消。
挂起函数:coroutineScope 是一个挂起函数,允许在其中使用其他挂起函数(如 delay 和 await)。
结构化并发:使用 coroutineScope 可以确保在同一作用域内启动的协程相互之间可以安全地协作,并且它们的执行顺序是可预测的。

  • join函数
    在这里插入图片描述
  • 结论
  • 使用 join: 当你启动一个不需要返回值的协程,并且你只想等待它完成时,例如在执行多个并发任务时,你想确保某个任务完成再继续执行后续代码。
  • 使用 await: 当你启动一个需要返回值的协程(通过 async),并想在获取结果后继续处理,使用 await 是更合适的选择。

1.8 连接线程世界:和回调型 API 协作

  • suspendCoroutine
    把传统的回调函数变成挂起函数, it.resume()
    在这里插入图片描述
  • 捕获异常

try-catch
注意结构化并发的情况下 如何捕获子协程的异常 (这里讲的比较简单)

  • 支持取消功能的协程suspendCancellableCoroutine

suspendCoroutine/线程API等不支持取消的功能,大多数场景还是需要取消功能
取消之后的操作 it.invokeOnCancellation { }

1.9 runBlocking()

runBlocking 是 Kotlin 协程库中的一个函数,它用于启动一个新的协程并阻塞当前线程,直到该协程执行完成。这个函数通常在主函数或测试代码中使用,以便在协程中执行挂起操作,同时保持代码的同步性。

基本概念
目的: runBlocking 允许你在非协程环境中执行协程代码,适用于需要阻塞当前线程以等待协程结果的场景。
阻塞线程: 与其他协程构建器(如 launch 和 async)不同,runBlocking 会阻塞调用它的线程,直到其内部的协程完成

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值