Android上为什么使用Coroutines
- 方便的管理长时间运行的任务
- 提供
main-safe
的方法,主线程可以安全调用的方法、
Coroutines使用
// Dispatchers.Main |
Well written suspend functions are always safe to call from the main thread (or main-safe).
使用withContext
来实现main-safe
的方法,withContext
有性能优势,即使多次调用也可以避免线程的切换开销
三个调度器
- Dispatchers.IO (执行网络或IO操作)
- Dispatchers.Default (适合执行占用大量CPU资源的工作,例如排序、解析JSON等)
- Dispatchers.Main (主线程)
scope的来源
suspend方法必须在scope中启动,可以是GlobalScope, viewModelScope或者实现CoroutineScope接口。
class MyActivity : AppCompatActivity(), CoroutineScope by MainScope() { |
structured concurrency管理协程
A leaked coroutine can waste memory, CPU, disk, or even launch a network request that’s not needed.
没有妥善管理好生命周期的协程,可能会造成资源的浪费。所以Kotlin提供了structured concurreny
来管理协程的运行和生命周期。
通过structured concurrency
提供如下能力:
- When a scope cancels, all of its coroutines cancel.
- When a suspend fun returns, all of its work is done.
- When a coroutine errors, its caller or scope is notified.
coroutines
必须运行在CoroutineScope
中,CoroutineScope
就是用来追踪管理所有的coroutines
。
A CoroutineScope keeps track of all your coroutines, and it can cancel all of the coroutines started in it.
CoroutineScope启动coroutines的两种方式
launch
:只负责启动,不会返回任何结果async
:启动并返回一个结果,调用await
获取返回值
scope.launch { |
注意:launch
启动的协程会跑出异常,async
则不会,因为它设计的初衷时通过await
方法取得结果或异常。
并发处理
suspend functions
中使用coroutineScope
或者supervisorScope
来同时启动多个协程。
coroutineScope
可以保证当suspend
方法返回时,它里面的所有协程都已经执行完毕。
Structured concurrency guarantees that when a suspend function returns, all of its work is done.
suspend fun fetchTwoDocs() { |
coroutinesScope
:当其中一个协程失败时,会取消它所启动的所有协程supervisorScope
:当其中一个协程失败时,不会影响它所启动的其他协程
异常错误处理
通过try/catch
不错错误异常
通过async启动协程可能会丢失异常
val unrelatedScope = MainScope() |
async
设计初衷是假设您会通过await
来获取结果或异常,但是如果你没有调用它,就会造成异常丢失。
Structured concurrency guarantees that when a coroutine errors, its caller or scope is notified.
但是通过structured concurrency
可以确保怎么也不会丢失异常
suspend fun foundError() { |
ViewModel中启动coroutines
Structured concurrency guarantees when a scope cancels, all of its coroutines cancel.
取消scope
会取消它里面的所有协程。
使用viewModelScope
需要倒入依赖lifecycle-viewmodel-ktx:2.1.0-alpha04
。
class MyViewModel(): ViewModel() { |
viewModelScope
会在生命周期结束时(onCleared()调用时)自动取消里面的所有协程,所以即使如下代码也是安全的。
fun runForever() { |
Room中使用Coroutines
Note: Room uses its own dispatcher to run queries on a background thread. Your code should not use withContext(Dispatchers.IO) to call suspending room queries. It will complicate the code and make your queries run slower.
- 添加依赖
//Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:$room_version" - 在方法前加上
suspend
interface ProductsDao {
// Because this is marked suspend, Room will use it's own dispatcher
// to run this query in a main-safe way.
suspend fun loadProductsByDateStockedAscending(): List<ProductListing>
// Because this is marked suspend, Room will use it's own dispatcher
// to run this query in a main-safe way.
suspend fun loadProductsByDateStockedDescending(): List<ProductListing>
}
将Callback转换为Coroutines
suspend fun execute(): MyResultType { |
提供main-safe的接口方法
最佳实践,每个层面的接口都是main-safe
的方法
- WebApi
- Dao
- Repository
- UseCase
Coroutines单元测试
导入依赖:kotlinx-coroutines-test
使用runBlocking
或runBlockingTest
测试
|
runBlocking
和runBlockingTest
区别(没看懂这两个东西作用和区别)
Important: The function runBlockingTest will always block the caller, just like a regular function call. The coroutine will run synchronously on the same thread. You should avoid
runBlocking
andrunBlockingTest
in your application code and prefer launch which returns immediately.
runBlockingTest
should only be used from tests as it executes coroutines in a test-controlled manner, whilerunBlocking
can be used to provide blocking interfaces to coroutines.