Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android NetworkOnMainThreadException通过`Dispatchers.IO启动协同路由时发生异常`_Android_Kotlin_Kotlin Coroutines_Networkonmainthread - Fatal编程技术网

Android NetworkOnMainThreadException通过`Dispatchers.IO启动协同路由时发生异常`

Android NetworkOnMainThreadException通过`Dispatchers.IO启动协同路由时发生异常`,android,kotlin,kotlin-coroutines,networkonmainthread,Android,Kotlin,Kotlin Coroutines,Networkonmainthread,我正在尝试使用View/ViewModel/UseCase/Repository模式进行基本的网络调用。主异步调用通过协程执行,这两个协程都是使用Dispatchers.IO启动的 首先,以下是相关代码: 视图模型: class ContactHistoryViewModel @Inject constructor(private val useCase: GetContactHistory) : BaseViewModel() { // ... fun getContactHi

我正在尝试使用View/ViewModel/UseCase/Repository模式进行基本的网络调用。主异步调用通过协程执行,这两个协程都是使用
Dispatchers.IO
启动的

首先,以下是相关代码:

视图模型:

class ContactHistoryViewModel @Inject constructor(private val useCase: GetContactHistory) : BaseViewModel() {
    // ...
    fun getContactHistory(userId: Long, contactId: Long) {
        useCase(GetContactHistory.Params(userId, contactId)) { it.either(::onFailure, ::onSuccess) }
    }
}
class GetContactHistory @Inject constructor(private val repository: ContactRepository) : UseCase<ContactHistory, GetContactHistory.Params>() {

    override suspend fun run(params: Params) = repository.getContactHistory(params.userId, params.contactId)
    data class Params(val userId: Long, val contactId: Long)
}
abstract class UseCase<out Type, in Params> where Type : Any {

    abstract suspend fun run(params: Params): Either<Failure, Type>

    operator fun invoke(params: Params, onResult: (Either<Failure, Type>) -> Unit = {}) {
        val job = GlobalScope.async(Dispatchers.IO) { run(params) }
        GlobalScope.launch(Dispatchers.IO) { onResult(job.await()) }
    }
}
class ContactDataRepository(...) : SyncableDataRepository<ContactDetailDomainModel>(cloudStore.get(), localStore),
        ContactRepository {

    override fun getContactHistory(userId: Long, contactId: Long): Either<Failure, ContactHistory> {
        return request(cloudStore.get().getContactHistory(userId, contactId), {it}, ContactHistory(null, null))
    }

    /**
     * Executes the request.
     * @param call the API call to execute.
     * @param transform a function to transform the response.
     * @param default the value returned by default.
     */
    private fun <T, R> request(call: Call<T>, transform: (T) -> R, default: T): Either<Failure, R> {
        return try {
            val response = call.execute()
            when (response.isSuccessful) {
                true -> Either.Right(transform((response.body() ?: default)))
                false -> Either.Left(Failure.GenericFailure())
            }
        } catch (exception: Throwable) {
            Either.Left(Failure.GenericFailure())
        }
    }
}
GetContactHistory用例:

class ContactHistoryViewModel @Inject constructor(private val useCase: GetContactHistory) : BaseViewModel() {
    // ...
    fun getContactHistory(userId: Long, contactId: Long) {
        useCase(GetContactHistory.Params(userId, contactId)) { it.either(::onFailure, ::onSuccess) }
    }
}
class GetContactHistory @Inject constructor(private val repository: ContactRepository) : UseCase<ContactHistory, GetContactHistory.Params>() {

    override suspend fun run(params: Params) = repository.getContactHistory(params.userId, params.contactId)
    data class Params(val userId: Long, val contactId: Long)
}
abstract class UseCase<out Type, in Params> where Type : Any {

    abstract suspend fun run(params: Params): Either<Failure, Type>

    operator fun invoke(params: Params, onResult: (Either<Failure, Type>) -> Unit = {}) {
        val job = GlobalScope.async(Dispatchers.IO) { run(params) }
        GlobalScope.launch(Dispatchers.IO) { onResult(job.await()) }
    }
}
class ContactDataRepository(...) : SyncableDataRepository<ContactDetailDomainModel>(cloudStore.get(), localStore),
        ContactRepository {

    override fun getContactHistory(userId: Long, contactId: Long): Either<Failure, ContactHistory> {
        return request(cloudStore.get().getContactHistory(userId, contactId), {it}, ContactHistory(null, null))
    }

    /**
     * Executes the request.
     * @param call the API call to execute.
     * @param transform a function to transform the response.
     * @param default the value returned by default.
     */
    private fun <T, R> request(call: Call<T>, transform: (T) -> R, default: T): Either<Failure, R> {
        return try {
            val response = call.execute()
            when (response.isSuccessful) {
                true -> Either.Right(transform((response.body() ?: default)))
                false -> Either.Left(Failure.GenericFailure())
            }
        } catch (exception: Throwable) {
            Either.Left(Failure.GenericFailure())
        }
    }
}
class GetContactHistory@Inject构造函数(私有val存储库:ContactRepository):用例(){
覆盖挂起有趣的运行(params:params)=repository.getContactHistory(params.userId,params.contactId)
数据类参数(val userId:Long,val contactId:Long)
}
上面使用的基本用例类:

class ContactHistoryViewModel @Inject constructor(private val useCase: GetContactHistory) : BaseViewModel() {
    // ...
    fun getContactHistory(userId: Long, contactId: Long) {
        useCase(GetContactHistory.Params(userId, contactId)) { it.either(::onFailure, ::onSuccess) }
    }
}
class GetContactHistory @Inject constructor(private val repository: ContactRepository) : UseCase<ContactHistory, GetContactHistory.Params>() {

    override suspend fun run(params: Params) = repository.getContactHistory(params.userId, params.contactId)
    data class Params(val userId: Long, val contactId: Long)
}
abstract class UseCase<out Type, in Params> where Type : Any {

    abstract suspend fun run(params: Params): Either<Failure, Type>

    operator fun invoke(params: Params, onResult: (Either<Failure, Type>) -> Unit = {}) {
        val job = GlobalScope.async(Dispatchers.IO) { run(params) }
        GlobalScope.launch(Dispatchers.IO) { onResult(job.await()) }
    }
}
class ContactDataRepository(...) : SyncableDataRepository<ContactDetailDomainModel>(cloudStore.get(), localStore),
        ContactRepository {

    override fun getContactHistory(userId: Long, contactId: Long): Either<Failure, ContactHistory> {
        return request(cloudStore.get().getContactHistory(userId, contactId), {it}, ContactHistory(null, null))
    }

    /**
     * Executes the request.
     * @param call the API call to execute.
     * @param transform a function to transform the response.
     * @param default the value returned by default.
     */
    private fun <T, R> request(call: Call<T>, transform: (T) -> R, default: T): Either<Failure, R> {
        return try {
            val response = call.execute()
            when (response.isSuccessful) {
                true -> Either.Right(transform((response.body() ?: default)))
                false -> Either.Left(Failure.GenericFailure())
            }
        } catch (exception: Throwable) {
            Either.Left(Failure.GenericFailure())
        }
    }
}
抽象类用例,其中类型:Any{
抽象挂起趣味跑步(params:params):或
运算符fun invoke(params:params,onResult:(任意)->Unit={}){
val job=GlobalScope.async(Dispatchers.IO){run(params)}
GlobalScope.launch(Dispatchers.IO){onResult(job.await())}
}
}
最后,存储库:

class ContactHistoryViewModel @Inject constructor(private val useCase: GetContactHistory) : BaseViewModel() {
    // ...
    fun getContactHistory(userId: Long, contactId: Long) {
        useCase(GetContactHistory.Params(userId, contactId)) { it.either(::onFailure, ::onSuccess) }
    }
}
class GetContactHistory @Inject constructor(private val repository: ContactRepository) : UseCase<ContactHistory, GetContactHistory.Params>() {

    override suspend fun run(params: Params) = repository.getContactHistory(params.userId, params.contactId)
    data class Params(val userId: Long, val contactId: Long)
}
abstract class UseCase<out Type, in Params> where Type : Any {

    abstract suspend fun run(params: Params): Either<Failure, Type>

    operator fun invoke(params: Params, onResult: (Either<Failure, Type>) -> Unit = {}) {
        val job = GlobalScope.async(Dispatchers.IO) { run(params) }
        GlobalScope.launch(Dispatchers.IO) { onResult(job.await()) }
    }
}
class ContactDataRepository(...) : SyncableDataRepository<ContactDetailDomainModel>(cloudStore.get(), localStore),
        ContactRepository {

    override fun getContactHistory(userId: Long, contactId: Long): Either<Failure, ContactHistory> {
        return request(cloudStore.get().getContactHistory(userId, contactId), {it}, ContactHistory(null, null))
    }

    /**
     * Executes the request.
     * @param call the API call to execute.
     * @param transform a function to transform the response.
     * @param default the value returned by default.
     */
    private fun <T, R> request(call: Call<T>, transform: (T) -> R, default: T): Either<Failure, R> {
        return try {
            val response = call.execute()
            when (response.isSuccessful) {
                true -> Either.Right(transform((response.body() ?: default)))
                false -> Either.Left(Failure.GenericFailure())
            }
        } catch (exception: Throwable) {
            Either.Left(Failure.GenericFailure())
        }
    }
}
class ContactDataRepository(…):SyncableDataRepository(cloudStore.get(),localStore),
联系人存储库{
覆盖趣味getContactHistory(userId:Long,contactId:Long):或者{
返回请求(cloudStore.get().getContactHistory(userId,contactId),{it},ContactHistory(null,null))
}
/**
*执行请求。
*@param调用要执行的API调用。
*@param transform函数用于转换响应。
*@param default默认返回的值。
*/
私有乐趣请求(call:call,transform:(T)->R,默认值:T):或者{
回击{
val response=call.execute()
何时(response.isSuccessful){
true->one.Right(转换((response.body()?:默认值)))
false->earth.Left(Failure.GenericFailure())
}
}捕获(例外:可丢弃){
.Left(Failure.GenericFailure())
}
}
}
摘要: 将调试断点放置在存储库中的
catch{}
块中(见正上方)表明正在抛出
android.os.NetworkOnMainThreadException
。这很奇怪,因为这两个协同程序都是在
Dispatchers.IO
的上下文中启动的,而不是
Dispatchers.Main
(Android的主UI线程)

问题:
为什么会抛出上述异常,以及如何更正此代码?

标记函数
挂起
不会使其挂起,您必须确保工作实际发生在后台线程中

你有这个

override suspend fun run(params: Params) = repository.getContactHistory(params.userId, params.contactId)
这叫什么

override fun getContactHistory(userId: Long, contactId: Long): Either<Failure, ContactHistory> {
    return request(cloudStore.get().getContactHistory(userId, contactId), {it}, ContactHistory(null, null))
}

但是一个更好的解决方案是使用协程适配器进行改装。

问题是您没有在任何地方创建协程。您可以使用更高阶函数
suspendCoroutine
进行此操作。一个简单的例子如下:

private suspend fun <T, R> request(call: Call<T>, transform: (T) -> R, default: T): Either<Failure, R> {
        return suspendCoroutine { continuation ->
            continuation.resume(try {
                val response = call.execute()
                when (response.isSuccessful) {
                    true -> Either.Right(transform((response.body() ?: default)))
                    false -> Either.Left(Failure.GenericFailure())
                }
            } catch (exception: Throwable) {
                Either.Left(Failure.GenericFailure())
            })
        }
    }
private-suspend-fun请求(call:call,transform:(T)->R,默认值:T):或者{
返回suspendCoroutine{continuation->
继续。继续(尝试){
val response=call.execute()
何时(response.isSuccessful){
true->one.Right(转换((response.body()?:默认值)))
false->earth.Left(Failure.GenericFailure())
}
}捕获(例外:可丢弃){
.Left(Failure.GenericFailure())
})
}
}
你也可以用很多方法。注意:此函数永远不会引发异常。我认为这是有意的,但如果您想将其向上传播,以便能够将其包装在一个
try catch
块中,例如,您可以使用
continuation.resumeWithException(…)


由于此函数返回实际的协同程序,因此您的
与上下文(Dispatchers.IO)
应按预期工作。希望有帮助

即使suspend函数从不挂起,它也会在
CoroutineScope.async
上下文中被调用,因此它应该在IO调度程序上执行。您需要提供堆栈跟踪,甚至开始回答该问题,因为我们无法从提供的代码推断实际调用函数和导致异常的原因。