
Android Kotlin 協程asyncmeasureTimeMillis 統計一段代碼耗時內斂函數 measureTimeMillis{ } 可以很方便的統計一段代碼執行的耗時。使用GlobalScope.launch { val time measureTimeMillis { delay(1000) Log.d(zyj-, 日志) } Log.d(zyj-, 耗時:$time) }輸出結果D/zyj-: 日志 D/zyj-: 耗時:1010使用默認順序定義兩個耗時函數suspend fun doSomethingUsefulOne(): Int { delay(1000L) // 假設我們在這里做了一些有用的事 return 13 } suspend fun doSomethingUsefulTwo(): Int { delay(1000L) // 假設我們在這里也做了一些有用的事 return 29 }使用默認的順序調用val time measureTimeMillis { val one doSomethingUsefulOne() val two doSomethingUsefulTwo() println(The answer is ${one two}) } println(Completed in $time ms)它的打印輸出如下The answer is 42 Completed in 2017 ms從輸出結果上看兩個耗時任務是串行的總耗時 耗時函數1 耗時函數2使用 async 并發如果 doSomethingUsefulOne 與 doSomethingUsefulTwo 之間沒有依賴并且我們想更快的得到結果讓它們進行 并發 嗎這就是async 可以幫助我們的地方。在概念上async 就類似于 launch。它啟動了一個單獨的協程與其它所有的協程一起并發的工作。不同之處在于 launch 返回一個 Job 并且不附帶任何結果值而 async 返回一個 Deferred—— 一個非阻塞 future 這代表了一個將會在稍后提供結果的 promise。你可以使用 .await()在一個延期的值上得到它的最終結果 但是 Deferred 也是一個 Job所以如果需要的話你可以取消它。val time measureTimeMillis { val one async { doSomethingUsefulOne() } val two async { doSomethingUsefulTwo() } println(The answer is ${one.await() two.await()}) } println(Completed in $time ms)它的打印輸出如下The answer is 42 Completed in 1017 ms這里快了兩倍因為兩個協程并發執行。 請注意使用協程進行并發總是顯式的。惰性啟動的 async可選的async 可以通過將 start 參數設置為 CoroutineStart.LAZY 而變為惰性的。 在這個模式下只有結果通過 await 獲取的時候協程才會啟動或者在 Job 的 start函數調用的時候。運行下面的示例val time measureTimeMillis { val one async(start CoroutineStart.LAZY) { doSomethingUsefulOne() } val two async(start CoroutineStart.LAZY) { doSomethingUsefulTwo() } // 執行一些計算 one.start() // 啟動第一個 two.start() // 啟動第二個 println(The answer is ${one.await() two.await()}) } println(Completed in $time ms)它的打印輸出如下The answer is 42 Completed in 1017 ms因此在先前的例子中這里定義的兩個協程沒有執行但是控制權在于程序員準確的在開始執行時調用 start。我們首先 調用 one然后調用 two接下來等待這個協程執行完畢。注意如果我們只是在 println 中調用 await而沒有在單獨的協程中調用 start這將會導致順序行為直到 await 啟動該協程 執行并等待至它結束這并不是惰性的預期用例。 在計算一個值涉及掛起函數時這個 async(start CoroutineStart.LAZY)的用例用于替代標準庫中的 lazy 函數。構建async 風格的函數我們可以定義異步風格的函數來 異步 的調用 doSomethingUsefulOne 和 doSomethingUsefulTwo 并使用 async 協程建造器并帶有一個顯式的 GlobalScope 引用。 我們給這樣的函數的名稱中加上“……Async”后綴來突出表明事實上它們只做異步計算并且需要使用延期的值來獲得結果。// somethingUsefulOneAsync 函數的返回值類型是 DeferredInt fun somethingUsefulOneAsync() GlobalScope.async { doSomethingUsefulOne() } // somethingUsefulTwoAsync 函數的返回值類型是 DeferredInt fun somethingUsefulTwoAsync() GlobalScope.async { doSomethingUsefulTwo() }使用class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 我們可以在協程外面啟動異步執行 val one somethingUsefulOneAsync() val two somethingUsefulTwoAsync() // 但是等待結果必須調用其它的掛起或者阻塞 // 當我們等待結果的時候這里我們使用 GlobalScope.launch{ } 來阻塞主線程 GlobalScope.launch { println(The- answer is ${one.await()} ${two.await()}) } } }這種帶有異步函數的編程風格僅供參考因為這在其它編程語言中是一種受歡迎的風格。在 Kotlin 的協程中使用這種風格是強烈不推薦的 原因如下所述。考慮一下如果 val one somethingUsefulOneAsync() 這一行和 one.await() 表達式這里在代碼中有邏輯錯誤 并且程序拋出了異常以及程序在操作的過程中中止將會發生什么。 通常情況下一個全局的異常處理者會捕獲這個異常將異常打印成日記并報告給開發者但是反之該程序將會繼續執行其它操作。但是這里我們的 somethingUsefulOneAsync仍然在后臺執行 盡管如此啟動它的那次操作也會被終止。這個程序將不會進行結構化并發如下一小節所示。使用 async 的結構化并發讓我們使用使用 async 的并發這一小節的例子并且提取出一個函數并發的調用 doSomethingUsefulOne 與 doSomethingUsefulTwo 并且返回它們兩個的結果之和。 由于 async 被定義為了 CoroutineScope上的擴展我們需要將它寫在作用域內并且這是 coroutineScope 函數所提供的suspend fun concurrentSum(): Int coroutineScope { val one async { doSomethingUsefulOne() } val two async { doSomethingUsefulTwo() } one.await() two.await() }這種情況下如果在 concurrentSum 函數內部發生了錯誤并且它拋出了一個異常 所有在作用域中啟動的協程都會被取消。val time measureTimeMillis { println(The answer is ${concurrentSum()}) } println(Completed in $time ms)從上面的 main 函數的輸出可以看出我們仍然可以同時執行這兩個操作The answer is 42 Completed in 1017 ms取消始終通過協程的層次結構來進行傳遞import kotlinx.coroutines.* fun main() runBlockingUnit { try { failedConcurrentSum() } catch(e: ArithmeticException) { println(Computation failed with ArithmeticException) } } suspend fun failedConcurrentSum(): Int coroutineScope { val one asyncInt { try { delay(Long.MAX_VALUE) // 模擬一個長時間的運算 42 } finally { println(First child was cancelled) } } val two asyncInt { println(Second child throws an exception) throw ArithmeticException() } one.await() two.await() }請注意如果其中一個子協程即 two失敗第一個 async 以及等待中的父協程都會被取消Second child throws an exception First child was cancelled Computation failed with ArithmeticException后文Android Kotlin 更多學習資料可以掃碼免費領取《史上最詳Android版kotlin協程入門進階實戰》第一章 Kotlin協程的基礎介紹? ● 協程是什么? ● 什么是Job 、Deferred 、協程作用域? ● Kotlin協程的基礎用法第二章 kotlin協程的關鍵知識點初步講解? ● 協程調度器? ● 協程上下文? ● 協程啟動模式? ● 協程作用域? ● 掛起函數第三章 kotlin協程的異常處理? ● 協程異常的產生流程? ● 協程的異常處理第四章 kotlin協程在Android中的基礎應用? ● Android使用kotlin協程? ● 在Activity與Framgent中使用協程? ● ViewModel中使用協程? ● 其他環境下使用協程第五章 kotlin協程的網絡請求封裝? ● 協程的常用環境? ● 協程在網絡請求下的封裝及使用? ● 高階函數方式? ● 多狀態函數返回值方式第六章 深入kotlin協程原理一? ● suspend的花花腸子? ● 藏在身后的-Continuation? ● 村里的希望-SuspendLambda第七章 深入kotlin協程原理二? ● 協程的那些小秘密? ● 協程的創建過程? ● 協程的掛起與恢復? ● 協程的執行與狀態機第八章 Kotlin Jetpack 實戰? ● 從一個膜拜大神的 Demo 開始? ● Kotlin 寫 Gradle 腳本是一種什么體驗? ● Kotlin 編程的三重境界? ● Kotlin 高階函數? ● Kotlin 泛型? ● Kotlin 擴展? ● Kotlin 委托? ● 協程“不為人知”的調試技巧? ● 圖解協程原理第九章 Kotlin 協程 Retrofit MVVM優雅的實現網絡請求? ● 項目配置? ● 實現思路? ● 協程實現? ● 協程 ViewModel LiveData實現? ● 后續優化? ● 異常處理? ● 更新Retrofit 2.6.0由于文章內容比較多篇幅有限資料已經被整理成了PDF文檔有需要 Android中高級面試必知必會 完整文檔的可以加微信可免費領取!承諾100%免費更有更多資料加微信免費領取