从Kotlin协程的实战 看 kotlin与java 在异常处理上的不一样(Checked Exception)

kotlin协程是什么?

kotlin-jvm 这套东西上的协程 其实就是个线程框架。 与go 语言那种高性能的协程是有本质不一样的,千万不要被迷惑了。除了阿里巴巴本身魔改的jvm之外,目前没有哪家jvm能够实现相似于go语言的那种协程能力。java

kotlin中的suspend 关键字是什么做用?

他最大的做用就是 若是你在某个方法前面加了suspend 那么这个方法的执行就会要求一个线程的执行上下文环境,不然会编译不经过。编程

看上图中的例子,被suspend 标识的io1 方法, 若是没有指定线程的执行环境 那么ide报红直接 编译不过, 可是若是你指定了他的线程上下文环境 则是能够编译经过的。bash

这里要注意的是 若是你用suspend方法标记的函数 函数体内部 并无使用协程关键字用来切线程的话, 那在编译的时候 suspend其实就没做用了,这里看图也能够知道 suspend在ide中显示成了灰色。网络

此外,所谓的kotlin协程的挂起 就是指切了个线程去干活,仅此而已。框架

kotlin-jvm 平台的协程到底有啥用?

其实就是方便给咱们切线程使用的,假设咱们有一个需求是简单的从本地sd卡上请求一个数据 而后显示在ui上jvm

纯java版本的ide

new Thread()
        {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {

                }

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mGoodsTitle.setText("adasdasda");
                    }
                });

            }
        }.start();

复制代码

若是这个需求再复杂一点 ,好比须要你显示在ui上之后 再去网络请求个什么东西 而后接口回来再刷新ui。 这一套若是用原生的java来写,可想而知会很麻烦,并且代码会有不少回调。可读性也很差。函数

可是若是用Rxjava来写,则会简单不少性能

subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
复制代码

咱们能够用 rxjava提供的这些操做符 来方便的切换咱们的线程,代码可读性会变的很是好。ui

有人要问了,那还须要kotlin的协程干啥?

kotlin的协程在处理相似代码的时候 会更加智能, 他的线程切走了之后 是能够自动切回来的 不须要你再手动的指定你的代码执行线程了。

举个例子:

GlobalScope.launch(Dispatchers.Main) {
            withContext(Dispatchers.IO) {
                //do io sth
            }
            textview.text = " do ui sth"

            withContext(Dispatchers.IO) {
                //do io sth2
            }
            textview.text = " do ui sth2"
        }

复制代码

这里就是典型的kotlin的 协程应用场景。 咱们先指定了一个线程执行上下文的环境 是main 也就是主线程。

而后咱们用协程的关键字 withContext 来切到io线程执行咱们的工做, 这个时候 后面的代码是不会走的。

必定会等到withContext里面的代码执行完毕之后, 才会走到 textview.text = " do ui sth" 这里。

而后后面咱们又执行了 withContext(Dispatchers.IO) 一次 ,这时 最后一行的textview.text 也是不会走的。

他必定会等到 上面的协程执行完毕之后 才会走。

因此这里你好好体会下 是否是会以为这种写法 比rxjava还要方便?毕竟线程切走之后 再切回来这个操做 kotlin协程帮咱们作好了,不再须要手动执行线程了 这个就是kotlin 协程的最大做用了。

其他的什么协程速度快啊之类的话 都是吹牛逼的。至少在kotlin-jvm上是不对的

使用协程会内存泄露吗?

固然会,由于协程 前面咱们说过了,本质上就是个线程。 因此线程会致使activity内存泄露的场景 在协程中同样存在。 其实这里若是有rxjava 编程经验的同窗 应该大概知道怎么作了。这里rxjava的写法我就很少写了,写一下协程的吧。

首先定义一个scope

var scope= MainScope();
复制代码

而后稍微改变一下咱们的写法 注意此次不是global scope了 是咱们刚才定义的scope了

scope.launch(Dispatchers.Main) {
            withContext(Dispatchers.IO) {
                //do io sth
            }
            textview.text = " do ui sth"

            withContext(Dispatchers.IO) {
                //do io sth2
            }
            textview.text = " do ui sth2"
        }

复制代码

而后在这里 释放便可。

override fun onDestroy() {
        scope.cancel()
        super.onDestroy()
    }
复制代码

跟Rxjava十分的像。就是换了个写法而已。 固然你若是还使用了lifecycle

那就更加简单了

lifecycleScope.launch {  }
复制代码

这样释放的时候 咱们连cancel 均可以省略了, 所有交给谷歌提供的lifecycleScope就能够了。

最后提一下 coroutineScope 这个函数 也至关有用,能够限制咱们的协程执行环境 ,有兴趣的同窗能够自行搜索一下,这里再也不展开。

delay函数 比thread的sleep函数更加高效?

纯扯淡的说法,什么delay 不阻塞线程, sleep阻塞 线程 都是纯扯淡的说法,不要信。

本质上就是kotlin的delay函数 就是个协程,delay的本质就是切了个线程 而后在那个线程里面 sleep 了一段时间, 结束之后再切回来,仅此而已。

若是你本身读懂了我上面的文章 那么看到这个delay函数前面的suspend 其实你就知道是啥意思了。

retrofit支持kotlin了之后 写起来很简单?

确实很简单。能够看一下以前的写法:

这是java版本的:

咱们看下kotlin协程版本的:

可是这里要注意了,这里有个大坑,kotlin是一个 不强制异常处理检查的语言,因此这里的异常 是会抛出来的! 必定要注意,一旦网络请求有了诸如404 或者502的异常 你上面的代码就直接crush了。

其实想一想也能想明白 你看我这个简写的写法 甚至都没有地方能够处理onFailure 里面的流程,那万一发生了 onFailure里面的状况怎么办? 怎么把这些错误信息抛给你? 也只有异常了。

看下retrofit的 源码

一目了然。

因此使用retrofit+kotlin的时候 必定要谨记 主动捕获异常,一方面是为了程序不要crush 另一方面也是为了 处理程序中的异常状况

GlobalScope.launch(Dispatchers.Main) {
            val reposItem = try {
                retrofit.create(GitHubService::class.java)
                    .allRepositories2(page, "pushed:<" + Date().format("yyyy-MM-dd"), 10)
            } catch (e: Exception) {
                Log.v(
                    "wuyue",
                    "e:" + (e is HttpException) + " e:" + e.message + " e:" + (e is Throwable)
                );
            }
            Log.v("wuyue", "reposItem2222222==$reposItem")
        }
复制代码

kotlin 与 java 在ce上的不一样?

对于java 来讲 ,若是你在方法内部 throw了 一个Exception 那你必须在方法签名的地方 主动用throws标记这个异常 而且在调用的时候 主动捕获,不然编译就会不经过。

看到没 这里是编译不过的。

改为这样就能够了。

调用的地方也是:

必须try catch 不然也是编译不过的。

可是要注意噢,若是是 RuntimeException 则不会出现上面的状况,若是你在方法体内部 throw 一个

RuntimeException ,那你方法签名的地方 不写throws 也是能够编译经过的。 调用的地方啥都不干 不写try catch 也没问题。 这里必定要注意。

最后就是坑爹的kotlin语言,无论你在方法里面 throw的 是 RuntimeException 仍是Exception 他都让你编译经过。。。

这就会致使 若是你调用一个函数,若是里面throw了一个异常,而后函数的注释或者文档又没告诉你这里可能会抛异常的话,那这个地方 可能就会发生可怕的事情了,对于客户端来讲就是crush。 目前我不知道kotlin为啥要这样设计,可是你们在调用kotlin的函数的时候最好仍是多点进去看看,看看到底会不会抛异常,免的搞出线上事故。

相关文章
相关标签/搜索