Kotlin协程使用

目录

  • 协程的用法
  • 协程同步异步请求与Rxjava,原生写法的区别与优势
  • 对于协程的理解

1.协程的用法

在安卓中添加引用,引用里面也包括了协程核心库

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5'

接下来我们做三个网络请求实验,fetchData()方法是一个网络请求,handleDataOnUI()方法是一个主线程UI方法。

1.这里我们把方法放在GlobalScope.launch当中,指定Dispatchers.Main,即里面的代码将在主线程中执行
2.我们准备了三个网络请求,api1 api2 api3, 其中api2是一个耗时的网络请求,用来区分是否网络请求是有序发出还是并发
3.为async,withContext的方法指定了不同的Dispatchers.IO线程

        //实验1,通过log日志可以看出,这三个网络请求是有序的,一个完成在请求另一个,等待全部完成执行最后
        GlobalScope.launch(Dispatchers.Main) {

            val data1 = async(Dispatchers.IO) { fetchData(1, api1) }.await()
            val data2 = async(Dispatchers.IO) { fetchData(2, api2) }.await()
            val data3 = async(Dispatchers.IO) { fetchData(3, api3) }.await()

            handleDataOnUI(data1!!, data2!!, data3!!)

        }
实验一.png
      //实验2,通过log日志可以看出,这三个网络请求也是有序的
        GlobalScope.launch(Dispatchers.Main) {

            val data1 = withContext(Dispatchers.IO) { fetchData(1, api1) }
            val data2 = withContext(Dispatchers.IO) { fetchData(2, api2) }
            val data3 = withContext(Dispatchers.IO) { fetchData(3, api3) }

            handleDataOnUI(data1!!, data2!!, data3!!)
        }
实验二.png
        //实验3,通过log日志可以看出,终于这三个网络请求是并发的
        GlobalScope.launch(Dispatchers.Main) {

            val data1 = async(Dispatchers.IO) { fetchData(1, api1) }
            val data2 = async(Dispatchers.IO) { fetchData(2, api2) }
            val data3 = async(Dispatchers.IO) { fetchData(3, api3) }

            handleDataOnUI(data1.await()!!, data2.await()!!, data3.await()!!)

        }

实验三.png
  • 通过上面实验可以得出结论,无论网络请求是顺序执行的,还是并发执行的,都是执行完毕所有网络请求以后,才最终去处理handleDataOnUI()的方法。通过协程,就能做到这么简洁的写发。

原因分析

  • 为什么会造成,不同的写法,一种是有序而另一种方式并发的呢?
    1.首先我们看withContext(),这是一个suspend函数,通过api我们可以看到,当执行这个方法时,会阻塞当前block代码块,也就是下一行withContext()函数不会开始,直到当前方法结束
 * Calls the specified suspending block with a given coroutine context, suspends until it completes, and returns
 * the result.
public suspend fun <T> withContext(
    context: CoroutineContext,
    block: suspend CoroutineScope.() -> T
): T = suspendCoroutineUninterceptedOrReturn sc@ { uCont ->
    //.....

2.接着我们看async函数,它实际上返回的是Deferred对象,并不会阻塞当前block函数。但是Deferred对象有一个await()函数,我们看下具体定义。我们可以看到,这里await()同样是一个susupend函数,等待结果。有点类似于Java当中的Future模式,等待但是并不阻塞,直到结果返回在再次唤醒。

/**
 * Awaits for completion of this value without blocking a thread and resumes when deferred computation is complete,
 * returning the resulting value or throwing the corresponding exception if the deferred was cancelled.
 *
 * This suspending function is cancellable.
 * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function
 * immediately resumes with [CancellationException].
 *
 * This function can be used in [select] invocation with [onAwait] clause.
 * Use [isCompleted] to check for completion of this deferred value without waiting.
 */
public suspend fun await(): T

3.总而言之,如果想要并发请求,我们必须要用async,如果是顺序执行,我们则可以使用withContext, 没必要使用async

//并发执行的正确写法
val task1 = async(Dispatcher.io){ doSomeStuff}
val task2 = async(Dispatcher.io){ doSomeStuff}
task1.await()
task2.await()

//顺序执行的常规写法
val task1 = withContext(Dispatcher.io){ doSomeStuff}
val task2 = withContext(Dispatcher.io){ doSomeStuff}
task1.await()
task2.await()
//或者
val task1 = async(Dispatcher.io){ doSomeStuff}.await()
val task2 = async(Dispatcher.io){ doSomeStuff}.await()
val total=  task1+task2

2.协程同步异步请求与Rxjava,原生写法的区别与优势

  • rxjava实现多个有序网络请求,因为rxjava是上游依次往下游发送事件,所以很容易写顺序请求
Observable.create(object : ObservableOnSubscribe<String> {
            override fun subscribe(emitter: ObservableEmitter<String>?) {
                emitter!!.onNext("url")
                emitter!!.onComplete()
            }
        }).map(object : Function<String, String> {
            override fun apply(t: String?): String {
                val data1 = fetchData(1, api1)
                return data1!!;
            }
        }).map(object : Function<String, String> {
            override fun apply(t: String?): String {
                val data2 = fetchData(2, api2)
                return "[======$t][----$data2]"
            }

        }).subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({ next ->
                Log.d(TAG, next)

            }, { error ->
                error.printStackTrace()
            })
  • rxjava实现多个并发网络请求,我们可以用zip
 Observable.zip(getObservable(1, api1), getObservable(2, api2),
            getObservable(3, api3), object : Function3<String, String, String, String> {
                override fun apply(t1: String?, t2: String?, t3: String?): String {

                    return t1 + t2 + t3
                }

            })
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : Observer<String> {
                override fun onComplete() {
                    Log.d(TAG, "Observer onComplete")
                }

                override fun onSubscribe(d: Disposable?) {
                    Log.d(TAG, "Observer onSubscribe")
                }

                override fun onNext(t: String?) {
                    Log.d(TAG, "Observer onNext $t")
                }

                override fun onError(e: Throwable?) {
                    Log.d(TAG, "Observer onError")
                }

            })

  • 如果用原生代码实现的话,这里就简单说下思路
    1.顺序请求,不停接口回调即可实现
    2.并发请求,多个线程并发执行,维护一个数组判断每个任务执行状态,在每个线程结束时,以同步的方式去检测和更新数组,如果全部执行完毕,则任务结束。

总之就是,协程对于多个并发网络请求,确实能够简写代码,方便使用和处理。

3.对于协程的理解

协程的用法有很多,我这也是简单的了解了基本的用法。协程更像是一个封装好的多线程请求框架,在安卓里面能更加方便切换线程,又能做到顺序和并发的网络请求,确实大大方便了网络请求的开发书写,但是对于网络请求中的失败,无网络等错误状态。还需要进一步学习和研究。对于协程的核心原理,更多是对底层java 多线程,线程池的封装,也有java Future模式的影子...

参考

https://www.kotlincn.net/docs/reference/coroutines/basics.html
https://stackoverflow.com/questions/52369508/kotlin-coroutines-async-await-sequence
https://blog.mindorks.com/mastering-kotlin-coroutines-in-android-step-by-step-guide
concurrent-using-async

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

推荐阅读更多精彩内容