正确使用协程
Using coroutines in a right way
我是第一次实现协程。我正在为一个简单的登录应用程序遵循 MVP 模式。这是我的代码流程 -
点击的登录按钮会按照这个方向-
LoginFragment -> LoginPresenter -> 存储库 -> API存储库 -> RetrofitInterface
登录响应将遵循这个方向-
RetrofitInterface -> APIRepository -> Repository -> LoginPresenter -> LoginFragment
这是代码-
RetrofitInterface.kt
@POST("login")
fun loginAPI(@Body loginRequest: LoginRequest): Deferred<LoginResponse>?
这是我的 Result.kt
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>()
}
APIRepository.kt
override suspend fun loginAPICall(loginRequest: LoginRequest) : Result<LoginResponse>? {
try {
val loginResponse = apiInterface?.loginAPI(loginRequest)?.await()
return Result.Success<LoginResponse>(loginResponse!!)
} catch (e : HttpException) {
return Result.Error(e)
} catch (e : Throwable) {
return Result.Error(e)
}
}
Repository.kt
override suspend fun loginUser(loginRequest: LoginRequest): Result<LoginResponse> {
if (isInternetPresent(context)) {
val result = apiRepositoryInterface?.loginAPICall(loginRequest)
if (result is Result.Success<LoginResponse>) {
val loginData = result.data
cache?.storeData(loginData)
}
return result!!
} else {
return Result.Error(Exception())
}
}
现在如何在我的 Presenter 中启动协程?我需要在后台线程上执行此 API 调用并将结果发布到 UI 线程?
你可以这样使用协程
private var parentJob = Job()
private val coroutineContext: CoroutineContext
get() = parentJob + Dispatchers.Main
private val scope = CoroutineScope(coroutineContext)
scope.launch(Dispatchers.IO) {
// your api call
}
可以调用parentJob.cancel()
取消作业或者在ViewModel的onClear中调用
您需要使用本地范围在 Presenter 中启动协程并注入 CoroutineContext
才能更改它,例如在单元测试中:
class Presenter(
private val repo: Repository,
private val uiContext: CoroutineContext = Dispatchers.Main
) : CoroutineScope { // creating local scope
private var job: Job = Job() // or SupervisorJob() - children of a supervisor job can fail independently of each other
// To use Dispatchers.Main (CoroutineDispatcher - runs and schedules coroutines)
// in Android add dependency: 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(request: LoginRequest) = launch { // launching a coroutine
val result = repo.loginUser(request) // calling 'loginUser' function will not block the Main Thread, it suspends the coroutine
//use result, update UI
when (result) {
is Success<LoginResponse> -> { /* update UI when login success */ }
is Error -> { /* update UI when login error */ }
}
}
}
我是第一次实现协程。我正在为一个简单的登录应用程序遵循 MVP 模式。这是我的代码流程 -
点击的登录按钮会按照这个方向-
LoginFragment -> LoginPresenter -> 存储库 -> API存储库 -> RetrofitInterface
登录响应将遵循这个方向-
RetrofitInterface -> APIRepository -> Repository -> LoginPresenter -> LoginFragment
这是代码-
RetrofitInterface.kt
@POST("login")
fun loginAPI(@Body loginRequest: LoginRequest): Deferred<LoginResponse>?
这是我的 Result.kt
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>()
}
APIRepository.kt
override suspend fun loginAPICall(loginRequest: LoginRequest) : Result<LoginResponse>? {
try {
val loginResponse = apiInterface?.loginAPI(loginRequest)?.await()
return Result.Success<LoginResponse>(loginResponse!!)
} catch (e : HttpException) {
return Result.Error(e)
} catch (e : Throwable) {
return Result.Error(e)
}
}
Repository.kt
override suspend fun loginUser(loginRequest: LoginRequest): Result<LoginResponse> {
if (isInternetPresent(context)) {
val result = apiRepositoryInterface?.loginAPICall(loginRequest)
if (result is Result.Success<LoginResponse>) {
val loginData = result.data
cache?.storeData(loginData)
}
return result!!
} else {
return Result.Error(Exception())
}
}
现在如何在我的 Presenter 中启动协程?我需要在后台线程上执行此 API 调用并将结果发布到 UI 线程?
你可以这样使用协程
private var parentJob = Job()
private val coroutineContext: CoroutineContext
get() = parentJob + Dispatchers.Main
private val scope = CoroutineScope(coroutineContext)
scope.launch(Dispatchers.IO) {
// your api call
}
可以调用parentJob.cancel()
取消作业或者在ViewModel的onClear中调用
您需要使用本地范围在 Presenter 中启动协程并注入 CoroutineContext
才能更改它,例如在单元测试中:
class Presenter(
private val repo: Repository,
private val uiContext: CoroutineContext = Dispatchers.Main
) : CoroutineScope { // creating local scope
private var job: Job = Job() // or SupervisorJob() - children of a supervisor job can fail independently of each other
// To use Dispatchers.Main (CoroutineDispatcher - runs and schedules coroutines)
// in Android add dependency: 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(request: LoginRequest) = launch { // launching a coroutine
val result = repo.loginUser(request) // calling 'loginUser' function will not block the Main Thread, it suspends the coroutine
//use result, update UI
when (result) {
is Success<LoginResponse> -> { /* update UI when login success */ }
is Error -> { /* update UI when login error */ }
}
}
}