안드로이드에서 사용되는 coroutine 에 대해서 자세히 알아 보겠습니다.
Android의 Lifecycle 을 반영한 CoroutineScope 을 제공하고 있는것이 중요하다고 생각합니다.
AAC에서는 구조적인 동시성과 안전한 Scope 제공을 위해서 특별한 Library를 제공하고 있습니다.
대표적으로
예제를 ViweModel 에서 scope 만들기와 취소하기
class MyViewModel: ViewModel() {
init {
viewModelScope.launch {
// Coroutine that will be canceled when the ViewModel is cleared.
}
}
}
public val ViewModel.viewModelScope: CoroutineScope
get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
return setTagIfAbsent(
JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
)
}
internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context
override fun close() {
coroutineContext.cancel()
}
}
Activity, Fragment 사용하되는 Scope 를 알보자
public class ComponentActivity extends Activity implements
LifecycleOwner,
KeyEventDispatcher.Component {
}
기본 Activity를 상속받은 ComponentActivity 에는 LifecycleOwner 존재하고 아래와 같이 Extention으로 각각의 Activity or Fragment Lifecycle 에 맞춘 coroutineScope 을 제공하게 됩니다.
public val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope
get() = lifecycle.coroutineScope
ViewMode + Repository + Network(Retrofit) with flow 사용한 Kotlin coroutine을 알아 보겠습니다.
class LoginViewModel (
private val repo: AuthRepository
) : ViewModel() {
fun login(id: String, pwd: String) {
viewModelScope.launch {
repo.login(id, pwd).collect { result ->
when(result) {
is Result.Success -> { }
is Result.Error -> { }
}
}
}
}
}
sealed class Result<out T> {
data class Success<out T>(val value: T) : Result<T>()
data class GenericError(val exception: Exception? = null) : : Result<Nothing>()
}
interface AuthRepository {
suspend fun login(id: String, pwd: String): Flow<Result<Auth>>
}
class AuthRepositoryImpl(
val service : LogispotApi,
) : AuthRepository {
override suspend fun login(id: String, pwd: String): Flow<Result<Auth>> {
val params = HashMap<String, Any>()
params["id"] = id
params["pwd"] = pwd
return flow {
emit(
try {
Result.Success(service.login(params = params))
} catch (throwable: Throwable) {
Result.Error(throwable)
}
)
}
}
}
// Retrofit
interface ServiceApi {
@FormUrlEncoded
@POST("authentication/login")
suspend fun login(@FieldMap params: HashMap<String, Any>): Auth
}