第一次使用协同程序.我需要帮助.

Here is my flow:

演示者想要登录,因此调用存储库接口.存储库实现RepositoryInterface. 所以Repository调用APIInterface.APIInterface由APIInterfaceImpl实现. APIInterfaceImpl最终调用MyRetrofitInterface.

Here is the flow diagrammatically:

Presenter -> Repository -> APIInterfaceImpl -> MyRetrofitInterface

一旦我得到登录响应:

APIInterfaceImpl -> Repository -> Stores the data in cache -> Gives http status code to Presenter

Here is my code:

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. 还有更好的解决方案吗?

推荐答案

在本地范围内启动协程是一个很好的实践,它可以在支持生命周期的类(例如PresenterViewModel)中实现.您可以使用下一种方法传递数据:

  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. Make onUserLogin function suspendable and returning Result in RepositoryInterface and Repository:

    suspend fun onUserLogin(loginRequest: LoginRequest): Result<LoginResponse> {
        return apiInterface.makeLoginCall(loginRequest)
    }
    
  3. 根据以下代码更改APIInterfaceAPIInterfaceImpl中的makeLoginCall功能:

    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 */ }
            }
        }
    }
    

EDIT

We can use extension functions on Result class to replace when expression:

inline fun <T : Any> Result<T>.onSuccess(action: (T) -> Unit): Result<T> {
    if (this is Success) action(data)
    return this
}
inline fun <T : Any> Result<T>.onError(action: (Error) -> Unit): Result<T> {
    if (this is Error) action(this)
    return this
}

class Presenter(...) : CoroutineScope {

    // ...

    fun login() = launch {
        val request = LoginRequest()
        val result = repo.onUserLogin(request) 

        result
            .onSuccess {/* update UI when login success */ }
            .onError { /* update UI when login error */ }
    }
}

Kotlin相关问答推荐

最好的方法来创建一个 map 在kotlin从两个列表

为什么可组合对象看似无状态(唯一传递的参数是函数,而不是状态),但会进行重组

为什么会出现Kotlin.Unit错误以及如何修复它?

Kotlin SIZE_BYTES

如何将 `when` 与 2 个密封类一起使用并获取内部值?

将一个列表元素分组为多个组(indexBy)

Kotlin 中私有集的完整语法 struct 是什么?

如何从kotlin中的ArrayList中删除所有元素

Moshi:解析单个对象或对象列表(kotlin)

为什么 IntelliJ Idea 无法识别我的 Spek 测试?

Kotlin 方法重载

无法删除部分子项.这可能是因为进程打开了文件或将其工作目录设置在目标目录中

对列表中数字的子集求和

Kotlinwhen表达式在使用主题时是否支持复合布尔表达式?

Android Studio 和 Kotlin:Unresolved reference: also

有没有办法在数据类构建时转换属性的值?

如何从协程范围返回值

Kotlin Android:属性委托必须有一个 'getValue(DashViewModel, KProperty*>)' 方法

我应该使用Kotlin数据类作为JPA实体吗?

Kotlin中的嵌套let块