标签: kotlin-coroutines

处理 Kotlin 协程中自定义 okhttp 拦截器抛出的异常

Interceptor在我的 Android 应用程序中使用自定义和 Retrofit 客户端,在某些特定情况下会引发异常。我正在尝试使用 Kotlin 协程使其工作。

问题是我无法处理前面提到的错误,因为在从 Interceptor 实例中抛出异常的那一刻,它会使整个应用程序崩溃,而不是在协程的try/catch语句中被捕获。当我使用 Rx 实现时,异常完美地传播到onError回调中,在那里我能够以我需要的方式处理它。

我想这与用于网络调用的底层线程有某种关系,请从调用的地方、在抛出异常之前的拦截器和堆栈跟踪中查看下面的日志:

2019-11-04 17:17:34.515 29549-29729/com.app W/TAG: Running thread: DefaultDispatcher-worker-1
2019-11-04 17:17:45.911 29549-29834/com.app W/TAG: Interceptor thread: OkHttp https://some.endpoint.com/...

2019-11-04 17:17:45.917 29549-29834/com.app E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
    Process: com.app, PID: 29549
    com.app.IllegalStateException: Passed refresh token can\'t be used for refreshing the token.
        at com.app.net.AuthInterceptor.intercept(AuthInterceptor.kt:33)
Run Code Online (Sandbox Code Playgroud)

我应该怎么做才能正确地从拦截器中捕获和处理这个异常?我错过了什么吗?

android exception interceptor okhttp kotlin-coroutines

18
推荐指数
2
解决办法
6203
查看次数

withTimeout 函数给出了 IllegalStateException:没有事件循环。使用 runBlocking { ... } 启动一个。在 Kotlin 多平台 iOS 客户端中

更新:如果我首先在没有超时的情况下执行协程然后使用超时,它会起作用。但是如果我先执行一个协程 withTimeout 那么它会给我一个错误。异步也是如此。

我正在创建一个演示 kotlin 多平台应用程序,我在其中使用 ktor 执行 API 调用。我想在 ktor 请求上有一个可配置的超时函数,所以我在协程级别使用 withTimeout 。

这是我使用网络 API 调用的函数。

suspend fun <T> onNetworkWithTimeOut(
    url: String,
    timeoutInMillis: Long,
    block: suspend CoroutineScope.() -> Any): T {
    return withTimeout(timeoutInMillis) {
        withContext(dispatchers.io, block)
    } as T
}

suspend fun <T> onNetworkWithoutTimeOut(url: String, block: suspend CoroutineScope.() -> Any): T {
    return withContext(dispatchers.io, block) as T
}
Run Code Online (Sandbox Code Playgroud)

这是我的 iOSMain 模块的 AppDispatcher 类。

@InternalCoroutinesApi
actual class AppDispatchersImpl : AppDispatchers {
@SharedImmutable
override val main: CoroutineDispatcher =
    NsQueueDispatcher(dispatch_get_main_queue())

@SharedImmutable
override …
Run Code Online (Sandbox Code Playgroud)

ios kotlin ktor kotlin-native kotlin-coroutines

18
推荐指数
1
解决办法
1914
查看次数

如何使用 Kotlin 协程在 Retrofit 中处理 204 响应?

我正在使用带有 Kotlin 协程的 Retrofit 2.7.1。

我有一个 Retrofit 服务定义如下:

@PUT("/users/{userId}.json")
suspend fun updateUserProfile(
        @Path("userId") userId: String,
        @Query("display_name") displayName: String) : Void
Run Code Online (Sandbox Code Playgroud)

此调用返回HTTP 204 No Content响应,这会导致 Retrofit 崩溃:

kotlin.KotlinNullPointerException: Response from com.philsoft.test.api.UserApiService.updateUserProfile was null but response body type was declared as non-null
        at retrofit2.KotlinExtensions$await$2$2.onResponse(KotlinExtensions.kt:43)
        at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:129)
        at okhttp3.RealCall$AsyncCall.execute(RealCall.java:174)
        at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:919)
Run Code Online (Sandbox Code Playgroud)

如何在改造中使用协程处理 204 响应而不会崩溃?

android retrofit retrofit2 kotlin-coroutines

18
推荐指数
4
解决办法
7238
查看次数

如何安全地(生命周期感知).collectAsState() StateFlow?

我正在尝试遵循官方指南,根据以下文章,使用 Compose 从 LiveData 迁移到 Flow/StateFlow:

\n

从 Android UI 收集流量的更安全方法

\n

从 LiveData 迁移到 Kotlin\xe2\x80\x99s 流程

\n

我尝试遵循第一篇文章末尾处的 Jetpack Compose 中的安全流集合中的建议。

\n
\n

在 Compose 中,副作用必须在受控环境中执行。\n 为此,请使用 LaunchedEffect 创建一个遵循可组合\xe2\x80\x99s 生命周期的协程。在其块中,如果您需要在主机生命周期处于某种状态时重新启动代码块,则可以调用\nsuspend Lifecycle.repeatOnLifecycle。

\n
\n

我设法以这种方式使用.flowWithLifecycle()来确保当应用程序转到后台时流不会发出:

\n
@Composable\nfun MyScreen() {\n\n    val lifecycleOwner = LocalLifecycleOwner.current\n\n    val someState = remember(viewModel.someFlow, lifecycleOwner) {\n        viewModel.someFlow\n            .flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.STARTED)\n            .stateIn(\n                scope = viewModel.viewModelScope,\n                started = SharingStarted.WhileSubscribed(5000),\n                initialValue = null\n            )\n    }.collectAsState()\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我发现这非常“样板”——一定有更好的东西。我想在 ViewModel 中使用 StateFlow,而不是在 @Composable 中转换为 StateFLow 的 Flow,并使用.repeatOnLifeCycle(),这样我就可以使用多个.collectAsState() …

android kotlin kotlin-coroutines android-jetpack-compose kotlin-flow

18
推荐指数
1
解决办法
1万
查看次数

如何在 kotlinx.coroutines.test.runTest 中使用 kotlinx.coroutines.withTimeout?

我有一个挂起函数,可以对外部 API 进行休息调用,我希望该函数在 1 分钟后超时。

suspend fun makeApiCallWithTimeout(): List<ApiResponseData> =
   withTimeout(1.minutes) {
      apiCall()
   }
        
Run Code Online (Sandbox Code Playgroud)

我正在尝试使用 Junit5 和 kotlinx.coroutines.test 1.6.0 对其进行测试,如下所示:

@Test
fun `Test api call`() = runTest {
   val responseData = "[]"
   mockWebServer.enqueue(mockResponse(body = responseData)
   val result = sut.makeApiCallWithTimeout()
   advanceUntilIdle()
   assertEquals(0, result.size)
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,我收到如下错误:

Timed out waiting for 60000 ms
kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 60000 ms
    at app//kotlinx.coroutines.TimeoutKt.TimeoutCancellationException(Timeout.kt:184)
    at app//kotlinx.coroutines.TimeoutCoroutine.run(Timeout.kt:154)
    at app//kotlinx.coroutines.test.TestDispatcher.processEvent$kotlinx_coroutines_test(TestDispatcher.kt:23)
    at app//kotlinx.coroutines.test.TestCoroutineScheduler.tryRunNextTask(TestCoroutineScheduler.kt:95)
    at app//kotlinx.coroutines.test.TestCoroutineScheduler.advanceUntilIdle(TestCoroutineScheduler.kt:110)
    at app//kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTestCoroutine(TestBuilders.kt:212)
    at app//kotlinx.coroutines.test.TestBuildersKt.runTestCoroutine(Unknown Source)
    at app//kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$1$1.invokeSuspend(TestBuilders.kt:167)
    at app//kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$1$1.invoke(TestBuilders.kt)
    at app//kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$1$1.invoke(TestBuilders.kt)
    at app//kotlinx.coroutines.test.TestBuildersJvmKt$createTestResult$1.invokeSuspend(TestBuildersJvm.kt:13) …
Run Code Online (Sandbox Code Playgroud)

kotlin kotlin-coroutines

18
推荐指数
1
解决办法
5312
查看次数

如何在 Kotlin 中创建具有多个接收器的扩展函数?

我希望我的扩展功能有几个接收器。例如,我希望函数handle能够调用CoroutineScopeIterable实例的方法:

fun handle() {
    // I want to call CoroutineScope.launch() and Iterable.map() functions here
    map {
        launch { /* ... */ }
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为这可能有效:

fun <T> (Iterable<T>, CoroutineScope).handle() {}
Run Code Online (Sandbox Code Playgroud)

但这给了我一个错误:

Function declaration must have a name
Run Code Online (Sandbox Code Playgroud)

我知道我可以使用参数创建函数,但是

单个函数是否可以有多个接收器以及如何在没有参数的情况下做到这一点?

android kotlin extension-function kotlin-coroutines kotlin-context-receivers

18
推荐指数
1
解决办法
1万
查看次数

发射和发射源与 LiveData 有什么区别?(如在实时用例中)

emit接受data类而emitSource接受LiveData<T>( T -> data)。考虑以下示例:-我有两种类型的调用:-

suspend fun getData(): Data // returns directly data
Run Code Online (Sandbox Code Playgroud)

而另一个;

suspend fun getData(): LiveData<Data> // returns live data instead
Run Code Online (Sandbox Code Playgroud)

对于第一种情况,我可以使用:-

liveData {
   emit(LOADING)
   emit(getData())
}
Run Code Online (Sandbox Code Playgroud)

我的问题:使用上述方法可以解决我的问题,为什么我们还需要emitSource(liveData)

任何使用该emitSource方法的好用例都会清楚地表明!

android android-livedata android-architecture-components android-jetpack kotlin-coroutines

17
推荐指数
4
解决办法
5963
查看次数

单元测试新的 Kotlin 协程 StateFlow

最近作为 Kotlin 协程的一部分引入了该类StateFlow

我目前正在尝试它并在尝试对我的 ViewModel 进行单元测试时遇到问题。我想要实现的目标:测试我StateFlow是否在 ViewModel 中以正确的顺序接收所有状态值。

我的代码如下:

视图模型:

class WalletViewModel(private val getUserWallets: GetUersWallets) : ViewModel() {

val userWallet: StateFlow<State<UserWallets>> get() = _userWallets
private val _userWallets: MutableStateFlow<State<UserWallets>> =
    MutableStateFlow(State.Init)

fun getUserWallets() {
    viewModelScope.launch {
        getUserWallets.getUserWallets()
            .onStart { _userWallets.value = State.Loading }
            .collect { _userWallets.value = it }
    }
}
Run Code Online (Sandbox Code Playgroud)

我的测试:

    @Test
fun `observe user wallets ok`() = runBlockingTest {
    Mockito.`when`(api.getAssetWallets()).thenReturn(TestUtils.getAssetsWalletResponseOk())
    Mockito.`when`(api.getFiatWallets()).thenReturn(TestUtils.getFiatWalletResponseOk())

    viewModel.getUserWallets()

    val res = arrayListOf<State<UserWallets>>()
    viewModel.userWallet.toList(res) //doesn't works

    Assertions.assertThat(viewModel.userWallet.value is State.Success).isTrue() //works, …
Run Code Online (Sandbox Code Playgroud)

android kotlin android-viewmodel kotlin-coroutines kotlin-flow

17
推荐指数
2
解决办法
5356
查看次数

在 ViewModel 中收集流。是否需要repeatOnLifeCycle?

到目前为止,我曾经在活动/片段或 ViewModel 中收集我的流,如下所示

活动/片段

lifecycleScope.launch {
    myViewModel.readTokenCredentials().collect { data -> /* do something */ }
}
Run Code Online (Sandbox Code Playgroud)

视图模型

viewModelScope.launch {
    prefsRepo.readTokenCredentials().collect { data -> /* do something */ }
}
Run Code Online (Sandbox Code Playgroud)

现在,谷歌开发人员告诉我们,这不是一种收集流量的安全方法,因为它可能会导致内存泄漏。相反,他们建议将集合包装在lifecycle.repeatOnLifecycle“活动/片段”中以进行流集合。

lifecycleScope.launch {
    lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
        myViewModel.readTokenCredentials().collect { data -> /* do something */ }
    }   
}
Run Code Online (Sandbox Code Playgroud)

我的问题是:

为什么在视图模型内收集流时不能使用repeatOnLifecyclewith ?viewModelScope当然,我知道视图模型不具有生命周期意识,但viewModelScope在流收集期间可能不太可能引入内存泄漏?

android kotlin kotlin-coroutines

17
推荐指数
2
解决办法
2万
查看次数

MutableSharedFlow - 重放和 extraBufferCapacity 之间的区别

MutableSharedFlow 有 3 个参数:replay、extraBufferCapacity 和 onBufferOverflow。replay 和 extraBufferCapacity 有什么区别?

该文档提到以下内容:

replay - 重播给新订阅者的值的数量(不能为负数,默认为零)。

extraBufferCapacity - 除了重放之外缓冲的值的数量。当还有剩余缓冲区空间时,emit 不会暂停(可选,不能为负数,默认为零)。

我不明白两者之间的区别以及何时需要 extraBufferCapacity > 0。 extraBufferCapacity 只是发射器的额外重播能力吗?

android kotlin kotlin-coroutines kotlin-flow kotlin-sharedflow

17
推荐指数
1
解决办法
6676
查看次数