Kotlin基础知识点

Posted 刘兆贤

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin基础知识点相关的知识,希望对你有一定的参考价值。

          本文来自刘兆贤的博客_CSDN博客-Java高级,Android旅行,Android基础领域博主 ,引用必须注明出处!

Kotlin越来越流行,新的知识点和应用技巧,也十分吸引人,尤其是协程,其他还有规避回调的代码写法(类似RxJava)。

下面是经典写法,比较符合kotlin语法

class Test 
    public static long plus(int i1, int i2) 
        return i1 + i2;
    
    public static void main(String[] args) 
        System.out.println(plus(1, 2));
    


class Test 
    interface Continuation 
        void next(int result);
    
    public static void plus(int i1, int i2, Continuation continuation) 
        continuation.next(i1 + i2);
    
    public static void main(String[] args) 
        plus(1, 2, result -> System.out.println(result));
    

协程,协助线程,而非代替线程。可以在单线程中使用,多线程同样可以,不需要切换到内核(有效降低线程上下文切换所浪费的时间),但需要遵循线程的临界条件,比如不能在主线程做网络请求等费时操作。

下面是顺序代码,无需回调,体现Kotlin的优雅。

launch(
    val user = api.getUser() // 👈 网络请求(IO 线程),非阻塞式挂起,不影响后面操作
    nameTv.text = user.name  // 👈 更新 UI(主线程)
)

其中launch方法有3种

// 方法一,使用 runBlocking 顶层函数
runBlocking 
    getImage(imageId)


// 方法二,使用 GlobalScope 单例对象
//            👇 可以直接调用 launch 开启协程
GlobalScope.launch 
    getImage(imageId)


// 方法三,自行通过 CoroutineContext 创建一个 CoroutineScope 对象
//                                    👇 需要一个类型为 CoroutineContext 的参数
val coroutineScope = CoroutineScope(context)
coroutineScope.launch 
    getImage(imageId)

方法一,阻塞线程,通常用于单元测试,线上不用。

方法二,生命周期与APP一致,且不能取消,故不推荐。

方法三,推荐使用,其中lanch时,可加入IO/Main等类似RxJava线程类型。

coroutineScope.launch(Dispatchers.IO) 
    //切到IO线程,执行耗时操作


coroutineScope.launch(Dispatchers.Main) 
    //切到主线程,执行UI刷新操作

业务流程变成这样

coroutineScope.launch(Dispatchers.Main)        // 开始协程:主线程
    val token = api.getToken()                  // 网络请求:IO 线程
    val user = api.getUser(token)               // 网络请求:IO 线程
    nameTv.text = user.name                     // 更新 UI:主线程

还有一个经典的withContext操作,执行结束返回原线程,避免切换线程的麻烦。

coroutineScope.launch(Dispatchers.Main)     //在 UI 线程开始
    val image = withContext(Dispatchers.IO) //切换到 IO 线程,并在执行完成后切回 UI 线程
        getImage(imageId)                    //将会运行在 IO 线程
    
    avatarIv.setImageBitmap(image)           //回到 UI 线程更新 UI
 

其他两个等号判断值相等,三个等号判断引用相等。

进程、线程和协程区别:

1、线程是CPU调度的基本单位,受内存大小影响有数量限制,切换发生在内核态,做数据同步时需要加锁。

2、进程是操作系统调度的基本单位,包含线程,线程共享其资源。

3、协程运行在线程中,可以阻断当前函数,切换发生在用户态,节省资源。

线程和协程的应用场景:

1、如果两个线程前后依赖,则可以写到一个协程里(比如两个网络请求的结果,去渲染同一页面)。线程用于独立的、耗时的操作(比如不同的线程往同一个文件里写内容)。

2、由于线程运行耗费CPU资源,多线程适用于计算密集处理。由于协程可切换用户态,多协程适用于IO密集处理。但多协程顺序执行时,前面阻塞,后面也暂停。

怎么做上下文切换?

使用程序计数器,记录切换的位置,返回时恢复方法栈,从上次记录的地方继续执行。

协程上下文(CoroutineContext)切换具体实现:

使用Dispatchers,Main指运行在主线程,IO即IO线程,Default用于简单计算,Unconfined不指定线程的分发器。

newSingleThreadContext使用一个新线程,在结束时要用cancel方法取消,此线程也可以作为复用线程来使用。

Job是launch或async方法返回的协程实例,可用于判断协程是否存活、已取消、已完成等状态和取消协程。

切换原理:

采用hander和方法回调的方式,分别实现切换到主线程,以及其他线程。

LiveData为什么是粘性的?

正常情况下,观察者订阅事件后,非观察者发送事件,观察者才能收到事件。粘性是指,不管观察者什么时候订阅事件,都可以接收到非观察者发送的事件。原因:已经订阅的,勿庸置疑;未事先订阅的,在初始化时拿到LiveData的START_VERSION(-1),每次LiveData改变后,其mVersion(初始值-1)都会加1,设置观察事件后比对Version值不同,则会主动给自己接收一个事件(即值变化)。

番外:EventBus也有类似机制,发送粘性事(stickyEvent)件时,会将此事件的class文件缓存,如果后面观察者再订阅这个事件,同样会先收到一个消息。

Kotlin里的uncaughtExceptionHandler:

CoroutineExceptionHandler,需要在协程初始化的时候使用。

参考:
破解 Kotlin 协程(6):协程挂起篇 | Bennyhuo协程的挂起最初是一个很神秘的东西,因为我们总是用线程的概念去思考,所以我们只能想到阻塞。不阻塞的挂起到底是怎么回事呢?说出来你也许会笑~~(哭?。。抱歉这篇文章我实在是没办法写的更通俗易懂了,大家一定要亲手实践!)https://www.bennyhuo.com/2019/05/07/coroutine-suspend/

https://blog.csdn.net/u011109881/article/details/120899119

以上是关于Kotlin基础知识点的主要内容,如果未能解决你的问题,请参考以下文章

史上最详Android版kotlin协程入门进阶实战

Kotlin 知识点

全新升级 Kotlin系统入门与进阶

事业单位法律基础知识:行政法及行政诉讼法 -1

C语言基础知识

C语言基础知识