從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的函數的時候最好仍是多點進去看看,看看到底會不會拋異常,免的搞出線上事故。

相關文章
相關標籤/搜索