Я пытаюсь сделать базовый сетевой вызов, используя шаблон View / ViewModel / UseCase / Repository. Основной асинхронный вызов выполняется через сопрограммы, которые запускаются с использованием Dispatchers.IO
.
Для начала вот соответствующий код:
ViewModel:
class ContactHistoryViewModel @Inject constructor(private val useCase: GetContactHistory) : BaseViewModel() {
// ...
fun getContactHistory(userId: Long, contactId: Long) {
useCase(GetContactHistory.Params(userId, contactId)) { it.either(::onFailure, ::onSuccess) }
}
}
GetContactHistory UseCase:
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)
}
Базовый класс UseCase, использованный выше:
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())
}
}
}
Резюме:
Помещение точки останова отладки в этот блок catch{}
в хранилище (видно выше) показывает, что выбрасывается android.os.NetworkOnMainThreadException
. Это странно, учитывая, что обе сопрограммы запускаются с контекстом Dispatchers.IO
, а не Dispatchers.Main
(основной поток пользовательского интерфейса Android).
Вопрос:
Почему выбрасывается указанное выше исключение и как можно исправить этот код?