协程和改造2的经验

这是什么


谁还没有阅读过文档 -我强烈建议您熟悉一下。


Jetbrain写道:


协程简化了异步编程,将所有复杂性留在了库中。 程序的逻辑可以在协程中顺序表达,而基库将为我们异步实现它。 该库可以将用户代码的相应部分包装在订阅相应事件的回调中,并将执行分派到不同的线程(甚至到不同的机器!)。 该代码将保持简单,就像严格按顺序执行一样。

简单来说,这是一个用于同步/异步代码执行的库。


怎么了


因为RxJava不再流行(只是在开玩笑)。


首先,我想尝试一些新的东西,其次,我碰到了一篇文章 -比较Corutin和其他方法的速度。


例子


例如,您需要在后台执行操作。


首先-添加对corutin的build.gradle依赖项:


dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1"
....
}
view raw build.gradle hosted with ❤ by GitHub

我们在代码中使用该方法:


suspend fun <T> withContext(
context: CoroutineContext,
block: suspend CoroutineScope.() -> T
)
view raw gistfile1.txt hosted with ❤ by GitHub

在上下文中-我们指定了所需的线程池-在简单情况下,它们是IO,Main和Default

IO-用于使用API​​进行简单操作,使用数据库进行操作,共享优先级等。
Main-UI线程,从中可以访问视图
默认-适用于CPU负载高的繁重操作
本文更多)


块-我们要执行的Lambda


var result = 1.0
withContext(IO) {
for (i in 1..1000) {
result += i * i
}
}
Log.d("coroutines example", "result = $result")
view raw Example.kt hosted with ❤ by GitHub

原则上,就是这样,我们得到的平方和的结果是1到1000,同时我们不阻塞主线程,这意味着没有ANR


但是,如果协程执行了20秒,并且在此期间我们旋转了设备2次,则将有3个同时运行的程序段。 哎呀


而且,如果我们将指向活动的链接传递给该块-则是泄漏,并且缺乏在旧块中执行视图操作的能力。 两次


那该怎么办呢?


做得更好


private var viewModelJob = Job()
private val viewModelScope = CoroutineScope(Dispatchers.Main + viewModelJob)
fun doWork() {
var result = 1.0
viewModelScope.launch {
withContext(IO) {
for (i in 1..1000) {
result += i * i
}
}
}
Log.d("coroutines example", " result = $result")
}
fun cancelJob() {
viewModelJob.cancel()
}
view raw Example.kt hosted with ❤ by GitHub

因此,例如,当屏幕旋转时,我们能够停止流中代码的执行。


CoroutineScope使合并所有嵌套协程的范围成为可能,并且在调用job.cancel()时停止了它们的执行


如果您打算在停止执行后重用作用域,则需要使用job.cancelChildren()而不是job.cancel()感谢Neikist评论


同时,我们仍然有机会控制流量:


fun doWork() {
var result = 1.0
var result2 = 1.0
viewModelScope.launch {
withContext(IO) {
for (i in 1..1000) {
result += i * i
}
}
withContext(Default) {
for (i in 1..1000) {
result2 += i * i
}
}
}
Log.d("coroutines example", "running result = $result, result 2 = $result2")
}
fun cancelJob() {
viewModelJob.cancel()
}
view raw Example.kt hosted with ❤ by GitHub

我们连接改造2


将依赖项添加到冰雹中:


dependencies {
implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
implementation "com.squareup.retrofit2:converter-moshi:$converterMoshiVersion"
implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:$retrofitCoroutinesVersion"
...
}
view raw build.gradle hosted with ❤ by GitHub

我们以钢笔https://my-json-server.typicode.com/typicode/demo/posts为例


我们描述改造接口:


interface RetrofitPosts {
@GET("posts")
fun getPosts(): Deferred<Response<List<Post>>>
}
view raw RetrofitPosts.kt hosted with ❤ by GitHub

描述返回的Post模型:


data class Post(val id: Int, val title: String)
view raw Post.kt hosted with ❤ by GitHub

我们的基本资料库:


abstract class BaseRepository<Params, Result> {
abstract suspend fun doWork(params: Params): Result
}
view raw BaseRepository.kt hosted with ❤ by GitHub

PostsRepository的实现:


class PostsRepository :
BaseRepository<PostsRepository.Params, PostsRepository.Result>() {
override suspend fun doWork(params: Params): Result {
val retrofitPosts = Retrofit
.Builder()
.baseUrl("https://jsonplaceholder.typicode.com")
.addConverterFactory(MoshiConverterFactory.create())
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.build()
.create(RetrofitPosts::class.java)
val result = retrofitPosts
.getPosts()
.await()
return Result(result.body())
}
class Params
data class Result(val posts: List<Post>?)
}

我们的BaseUseCase:


abstract class BaseUseCase<Params, Result> {
abstract suspend fun doWork(params: Params): Result
}
view raw BaseUseCase.kt hosted with ❤ by GitHub

GetPostsListUseCase的实现:


class GetListOfPostsUseCase
: BaseUseCase<GetListOfPostsUseCase.Params, GetListOfPostsUseCase.Result>() {
override suspend fun doWork(params: Params): Result {
return Result(
PostsRepository()
.doWork(PostsRepository.Params())
.response
.posts
)
}
class Params
class Result(val posts: List<Post>?)
}

结果如下:


fun doWork() {
val useCase = GetListOfPostsUseCase()
viewModelScope.launch {
withContext(Dispatchers.IO) {
val result = useCase.doWork(
GetListOfPostsUseCase.Params()
)
Log.d("coroutines example", "get list of posts = ${result.posts}")
}
}
}
fun cancelJob() {
viewModelJob.cancel()
}
view raw Example.kt hosted with ❤ by GitHub

变得更好


我是一个懒惰的人,每次我不想拖动整个代码表时,都在BaseViewModel中进行了必要的方法:


abstract class BaseViewModel : ViewModel() {
private var viewModelJob = Job()
private val viewModelScope = CoroutineScope(Dispatchers.Main + viewModelJob)
private var isActive = true
// Do work in IO
fun <P> doWork(doOnAsyncBlock: suspend CoroutineScope.() -> P) {
doCoroutineWork(doOnAsyncBlock, viewModelScope, IO)
}
// Do work in Main
// doWorkInMainThread {...}
fun <P> doWorkInMainThread(doOnAsyncBlock: suspend CoroutineScope.() -> P) {
doCoroutineWork(doOnAsyncBlock, viewModelScope, Main)
}
// Do work in IO repeately
// doRepeatWork(1000) {...}
// then we need to stop it calling stopRepeatWork()
fun <P> doRepeatWork(delay: Long, doOnAsyncBlock: suspend CoroutineScope.() -> P) {
isActive = true
viewModelScope.launch {
while (this@BaseViewModel.isActive) {
withContext(IO) {
doOnAsyncBlock.invoke(this)
}
if (this@BaseViewModel.isActive) {
delay(delay)
}
}
}
}
fun stopRepeatWork() {
isActive = false
}
override fun onCleared() {
super.onCleared()
isActive = false
viewModelJob.cancel()
}
private inline fun <P> doCoroutineWork(
crossinline doOnAsyncBlock: suspend CoroutineScope.() -> P,
coroutineScope: CoroutineScope,
context: CoroutineContext
) {
coroutineScope.launch {
withContext(context) {
doOnAsyncBlock.invoke(this)
}
}
}
}
view raw BaseViewModel.kt hosted with ❤ by GitHub

现在获取帖子列表如下所示:


class PostViewModel : BaseViewModel() {
val lengthOfPostsList = MutableLiveData<String>()
fun getListOfPosts() {
doWork {
val result = GetListOfPostsUseCase()
.doWork(GetListOfPostsUseCase.Params())
Log.d("coroutines example", "get list of posts = ${result.posts}")
lengthOfPostsList.postValue(result.posts?.size.toString())
}
}
view raw PostViewModel.kt hosted with ❤ by GitHub

结论


我在产品中使用了协程,结果证明代码确实更简洁易读。


UPD:
改造异常处理说明,请参见注释

Source: https://habr.com/ru/post/zh-CN445242/


All Articles