【Kotlin&Android】协程入门到放弃(1)

Kotlin1.1的时候介绍了协程,一种写异步的非阻塞的新方法,使用协程我们要引入kotlinx.coroutines库。

集成步骤

1.确保工程配置为kotlin1.1或者更高版本
2.在build.gradle中添加如下代码

apply plugin: 'kotlin'

kotlin {
    experimental {
        coroutines 'enable'
    }
}

注:如果项目中引用了apply plugin: 'kotlin-android',就不用引用apply plugin: 'kotlin'
3.由于android中使用kotlinx.coroutines,我们将它的最新版本添加到依赖项中

dependencies {
    ...
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.21'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.21'
}

4.添加混淆
在混淆代码中,具有不同类型的字段可以具有相同的名称,并且AtomicReferenceFieldUpdater可能无法找到正确的字段。要避免在混淆期间按类型进行字段重载,请将其添加到配置中:

-keepclassmembernames class kotlinx.** {
    volatile <fields>;
}

第一个协程

我们可以把协程认为是一个轻量的线程。像线程一样,协程同样可以并行运行,彼此等待并进行通信。协程和线程最大的不同就是,协程很轻量,我们可以创建上千个,并且只消耗很少的性能。线程从开始到保持都要耗费很多资源,而且对现在机器来说上千个线程是一个很严峻的挑战。我们可以通过launch{}方法开启一个协程,默认情况下协程运行在一个共享的线程池上。线程仍然可以运行在一个基于协程开发的程序中,一个线程可以运行很多个协程,所以我们将不再需要很多的线程。示例如下:

import kotlinx.coroutines.experimental.*

fun main(args: Array<String>) {
    println("Start")

    // Start a coroutine
    launch {
        delay(1000)
        println("Hello")
    }

    Thread.sleep(2000) // wait for 2 seconds
    println("Stop")
}

运行结果:

image.png

说明:上述代码中我们开启了一个协程,一秒后打印hello。我们使用delay()方法,就像使用Thread.sleep()方法,但是delay方法会更好一些,它不会阻塞线程,它只是暂停协程本身。当协程正在等待时,线程返回到池中,并且当等待完成时,协程将在池中的空闲线程上恢复。
如果你想在main函数中使用非阻塞的delay方法,会发生一个编译错误Suspend functions are only allowed to be called from a coroutine or another suspend function,因为我们没有在协程中执行,我们将它包装在runBolcking{}中使用。runBlocking{}会启动协程并等待协程执行完成


import kotlinx.coroutines.experimental.*

fun main(args: Array<String>) {
   println("Start")

   // Start a coroutine
   launch {
       delay(1000)
       println("Hello1")
   }

runBlocking {
    delay(2000)
    println("Hello2")
}

   Thread.sleep(2000) // wait for 2 seconds
   println("Stop")
}
image.png

对比线程和协程所消耗的资源

执行如下代码:

  fun main(args: Array<String>) {
    createThreads()
    createCoroutines()
  }
  fun createCoroutines() {
        thread(start = true) {
            val c = AtomicInteger()
            for (i in 1..1_000_000)
                launch {
                    c.addAndGet(i)
                }
            println("${c.get()},coroutines")
        }
    }

    fun createThreads() {
        thread(start = true) {
            val c = AtomicInteger()
            for (i in 1..1_000_000)
                thread(start = true) {
                    c.addAndGet(i)
                }
            println("${c.get()},threads")
        }
    }

结果:
image.png

说明:从运行结果上分析,执行协程要比线程轻量很多,但是它打印出了一些任意数字,这是因为有一些协程在输出结果的时候还没有运行结束。让我们在下一个小节对这个问题进行修复。

Async:从协程中返回来一个值

还有一个开启协程的方式就是async{},和lauch()一样,但是它会返回一个Deferred<T>实例,这个实例含有一个await()函数返回协程结果。
让我们再次创建百万个协程,保留它们的Deferred对象。现在就不需要原子计数器了,我们只需要返回协程中添加的数字。

fun main(args: Array<String>) {
    createAsyncCoroutines()
  }
fun createAsyncCoroutines() {
    thread(start = true) {
        val deferred = (1..1_000_000).map { n ->
            async {
                n
            }
        }

        runBlocking {
            val sum = deferred.sumBy { it.await() }
            println("Sum: $sum")
        }
    }

}

注:await()不能在协程之外使用,因为它需要挂起直到计算完成,并且只有协程能过在非阻塞的方式挂起。现在输出结果是1784293664,因为所有协程都执行完毕了,运行结果如下:
结果:

image.png

我们一样可以证明协程是并行运行的。如果我们使用delay函数,给每个async加上一秒延时。程序运行结果并不是1_000_000秒(大概11.5天)

val deferred = (1..1_000_000).map { n ->
    async {
        delay(1000)
        n
    }
}

挂起函数

假如说我们要实现一个单独的方法,这个方法等待1秒并且返回一个数字

suspend fun workload(n: Int): Int {
        delay(1000)
        return n
    }

注:协程最大的价值就是他可以挂起而不阻塞线程。编译器通过suspend关键字实现这一功能
如果我们现在执行workload方法的话,编译器就会知道这个方法可以挂起,并且做相应准备。

async {
    workload(n)
}

我们的workload()函数可以从协程(或其他挂起函数)调用,但不能从协程外部调用。当然,我们上面使用的delay()和await()本身也被声明为suspend,这就是我们必须将它们放在runBlocking {},launch {}或async {}中的原因。

刚开始学习协程,照着文档自己翻译的,后续还会继续写一些高级点的用法,持续更新,如果发现什么问题,欢迎指正!
学习地址:
https://www.kotlincn.net/docs/tutorials/coroutines-basic-jvm.html
https://github.com/Kotlin/kotlinx.coroutines
错误不足之处或相关建议欢迎大家评论指出,谢谢!如果觉得内容可以的话麻烦喜欢(♥)一下

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356

推荐阅读更多精彩内容