如何使用Kotlin提高生产力-协程

为什么要使用协程

举几个开发中常见的例子

  1. 从服务器拉取一张图片,下载,裁剪后展示在Activity上?
  2. 接问题1, 期间Activity关闭了怎么办?
  3. 接问题1, 如果是多张图片怎么同时展示在Activity上?

问题1 我先用Java描述下

    //  Bolts Task写法
    public fun demoMothed1ForBolts() {
        Task.callInBackground(Callable<String?> {
            // 1. 获取图片URL
            return@Callable getUrl()
        }).continueWith(Continuation<String?, Bitmap?> {
            // 2. 获取图片
            val connection = URL(it.result).openConnection() as HttpURLConnection
            return@Continuation if (connection.responseCode == 200) BitmapFactory.decodeStream(connection.inputStream) else null
        }, Task.BACKGROUND_EXECUTOR).continueWith(Continuation<Bitmap?, Bitmap?> {
            // 3. 裁剪图片
            it.result?.let {
                return@Continuation Bitmap.createBitmap(it, it.width / 2, it.height / 2, it.width / 2, it.height / 2)
            }
            return@Continuation null
        }, Task.BACKGROUND_EXECUTOR).continueWith(Continuation<Bitmap?, Any> {
            // 4. 展示图片
            it.result?.let {
                findViewById<ImageView>(R.id.imageView1).setImageBitmap(it)
            }
            null
        }, Task.UI_THREAD_EXECUTOR)
    }

再用kotlin+协程描述下

    public fun demoMothed1ForCoroutine() = coroutineScope.launch { val bitmap = async(Dispatchers.IO) { createTargetBitmap() } findViewById (R.id.imageView1).setImageBitmap(bitmap.await()) } private fun createTargetBitmap(): Bitmap? {
        getUrl()?.let {
            val connection = URL(it).openConnection() as HttpURLConnection
            val sourceBitmap = if (connection.responseCode == 200) BitmapFactory.decodeStream(connection.inputStream) else null
            sourceBitmap?.let {
                return Bitmap.createBitmap(it, it.width / 2, it.height / 2, it.width / 2, it.height / 2)
            }
            return null;
        }
    }

因为依赖了Facebook的开源的第三方线程库Bolts,感觉Java的代码调理还算清晰,和使用协程看起来差异不大.
但是如果不使用这个线程库或者使用系统提供的AsyncTask,这里面会涉及到3次异步回调,代码可读性可能会大大降低. 也就是Java代码要达到协程的条理性必须依赖一些好的线程框架.

问题2

我仔细看了下Bolts的API,没有发现有方法让我可以取消正在运行的Task.
但是协程因为有域的概念,取消协程域,那么运行在这个域中的所有任务都会被取消了,从而可以防止在Activity被Destory后Task还在运行.

    override fun onDestroy() {
        super.onDestroy()
        coroutineScope.cancel()
    }

问题3

  listOf(async { createTargetBitmap(url1) }, async { createTargetBitmap(url2) }).awaitAll();

同样如果是多个任务同时进行的话,使用线程回调不太好处理.
回调回来的时候我需要去检查其他线程里面的资源是否准备好,然后再去刷新.

协程怎么用

结合上面的栗子,可以看出协程是很好上手的,看看协程里面的关键字

1. suspend

挂起.

只能修饰方法 suspend fun

表示该方法只能在协程里面调用
表示该方法方法体内可以使用其他suspend方法
注意 这个关键字只是告诉编译器这是一个挂起函数,并没有实际执行.

2. CoroutineScope

协程作用域.

一般使用launch或者async创建的协程任务或者叫子域,都是运行在域里面,而域的作用其实就是帮助我们管理这些子域.

Android推荐使用CoroutineScope的方式运行协程,这样当外部组件生命周期结束的时候我们可以取消掉正在执行的任务.

override fun onDestroy() { super.onDestroy() coroutineScope.cancel() }
使用CoroutineScope运行,如果其中的一个子域抛出异常会导致整个协程作用域结束执行.

3.CoroutineContext/Dispatchers

协程上下文

协程作用域,子域构造函数里面都需要一个上下文,用来指名协程具体运行的线程

public fun CoroutineScope(context: CoroutineContext): CoroutineScope = ContextScope(if (context[Job] != null) context else context + Job())
而Dispatchers和CoroutineContext的关系就像是 线程和线程池的关系(ExecutorService/Executors),类似于一个静态工厂.

Type 作用
Dispatchers.Main 主线程
Dispatchers.IO IO子线程
Dispatchers.Default
Dispatchers.Unconfined CPU密集类型子线程

4. CoroutineStart

启动模式

这个属性用的较少,一般都是使用默认模式CoroutineStart.DEFAULT. 使用CoroutineStart.LAZY 这个属性可以让定义和启动分开,类似于Java里面的

Thread t = new Thread(){...};t.start()
使用Kotlin

val one = async(start = CoroutineStart.LAZY) { doSomethingUseful() }    one.start() 

5.launch/async await

子域

async和launch 的参数完全一致. 都是接受一个协程上下文,协程启动模式,闭包协程域

context: CoroutineContext = EmptyCoroutineContext,    start: CoroutineStart = CoroutineStart.DEFAULT,    block: suspend CoroutineScope.() -> T

他们的区别是 launch不关心返回值, async会在await的时候返回值.

如果拿不准直接使用async await组合即可,这是一个常规模式,存在多钟语言异步中.

6. withContext

子域

使用withContext 可以自动切回到调用线程的子域

coroutineScope.launch(Dispatchers.Main) { // 切换到IO线程 val image = withContext(Dispatchers.IO) { getImage(imageId) } // 执行完成这里自动回到Main线程 avatarIv.setImageBitmap(image) }

7. runBlocking

阻塞式子域

这种方式会阻塞当前线程,所以一般也不用.

协程是什么

协程(Coroutine),协程是一种并发设计模式,本质上是更轻量级的线程。在一个线程上可以同时跑多个协程。与线程相比,它更轻量、资源占用更少。

就是一个可以用简单同步代码写出安全的异步回调的线程工具库.

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

推荐阅读更多精彩内容