Kotlin协程处理错误和实现

问题描述 投票:0回答:2

第一次使用协同程序。需要帮忙。

这是我的流程:

Presenter想要登录,因此调用Repository Interface。存储库实现RepositoryInterface。所以Repository调用APIInterface。 APIInterface由APIInterfaceImpl实现。 APIInterfaceImpl最终调用MyRetrofitInterface。

以下是流程图:

Presenter - > Repository - > APIInterfaceImpl - > MyRetrofitInterface

一旦我得到登录回复:

APIInterfaceImpl - > Repository - >将数据存储在缓存中 - >将http状态代码提供给Presenter

这是我的代码:

RepositoryInterface.kt

fun onUserLogin(loginRequest: LoginRequest): LoginResponse

Repository.kt

class Repository : RepositoryInterface {
   private var apiInterface: APIInterface? = null

   override fun onUserLogin(loginRequest: LoginRequest): LoginResponse {
         return apiInterface?.makeLoginCall(loginRequest)
   }
}

APIInterface.kt

suspend fun makeLoginCall(loginRequest): LoginResponse?

APIInterfaceImpl.kt

override suspend fun makeLoginCall(loginRequest: LoginRequest): LoginResponse? {
        if (isInternetPresent(context)) {
            try {
                val response = MyRetrofitInterface?.loginRequest(loginRequest)?.await()
                return response
            } catch (e: Exception) {
                //How do i return a status code here
            }
        } else {
        //How do i return no internet here
            return Exception(Constants.NO_INTERNET)
        }
}

MyRetrofitInterface.kt

@POST("login/....")
fun loginRequest(@Body loginRequest: LoginRequest): Deferred<LoginResponse>?

我的问题是:

  1. 我的方法在架构上是对的吗?
  2. 如何在我的代码中传递http错误代码或没有Internet连接
  3. 我的解决方案有更好的方法吗?
android kotlin coroutine kotlinx.coroutines
2个回答
6
投票

在本地范围内启动协程是一种很好的做法,可以在生命周期感知类中实现,例如Presenter或ViewModel。您可以使用下一种方法传递数据:

  1. 在单独的文件中创建sealed Result类及其继承者: sealed class Result<out T : Any> class Success<out T : Any>(val data: T) : Result<T>() class Error(val exception: Throwable, val message: String = exception.localizedMessage) : Result<Nothing>()
  2. 使onUserLogin函数可暂停并返回ResultRepositoryInterface中的Repositorysuspend fun onUserLogin(loginRequest: LoginRequest): Result<LoginResponse> { return apiInterface.makeLoginCall(loginRequest) }
  3. 根据以下代码更改makeLoginCallAPIInterface中的APIInterfaceImpl函数: suspend fun makeLoginCall(loginRequest: LoginRequest): Result<LoginResponse> { if (isInternetPresent()) { try { val response = MyRetrofitInterface?.loginRequest(loginRequest)?.await() return Success(response) } catch (e: Exception) { return Error(e) } } else { return Error(Exception(Constants.NO_INTERNET)) } }
  4. 使用Presenter的下一个代码: class Presenter(private val repo: RepositoryInterface, private val uiContext: CoroutineContext = Dispatchers.Main ) : CoroutineScope { // creating local scope private var job: Job = Job() // To use Dispatchers.Main (CoroutineDispatcher - runs and schedules coroutines) in Android add // implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1' override val coroutineContext: CoroutineContext get() = uiContext + job fun detachView() { // cancel the job when view is detached job.cancel() } fun login() = launch { // launching a coroutine val request = LoginRequest() val result = repo.onUserLogin(request) // onUserLogin() function isn't blocking the Main Thread //use result, make UI updates when (result) { is Success<LoginResponse> -> { /* update UI when login success */ } is Error -> { /* update UI when login error */ } } } }

2
投票

我对这个话题有很多想法并提出了解决方案。我认为这个解决方案更清洁,易于处理异常。首先使用写代码时

fun getNames() = launch { }  

您正在将工作实例返回给我,我认为这是不正确的。用户不应该参考工作实例。我尝试了以下解决方案,它对我有用。但我想讨论是否会出现任何副作用。感谢您的评论。

fun main() {


    Presenter().getNames()

    Thread.sleep(1000000)

}


class Presenter(private val repository: Repository = Repository()) : CoroutineScope {

    private val job = Job()

    override val coroutineContext: CoroutineContext
        get() = job + Dispatchers.Default // Can be Dispatchers.Main in Android

    fun getNames() = launchSafe(::handleLoginError) {
        println(repository.getNames())
    }


    private fun handleLoginError(throwable: Throwable) {
        println(throwable)
    }

    fun detach() = this.cancel()

}

class Repository {

    suspend fun getNames() = suspendCancellableCoroutine<List<String>> {
        val timer = Timer()

        it.invokeOnCancellation {
            timer.cancel()
        }

        timer.schedule(timerTask {
            it.resumeWithException(IllegalArgumentException())
            //it.resume(listOf("a", "b", "c", "d"))
        }, 500)
    }
}


fun CoroutineScope.launchSafe(
    onError: (Throwable) -> Unit = {},
    onSuccess: suspend () -> Unit
) {
    val handler = CoroutineExceptionHandler { _, throwable ->
        onError(throwable)
    }

    launch(handler) {
        onSuccess()
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.