Kotlin Coroutine for Android

안드로이드에서 사용되는 coroutine 에 대해서 자세히 알아 보겠습니다.

Android의 Lifecycle 을 반영한 CoroutineScope 을 제공하고 있는것이 중요하다고 생각합니다.

AAC에서는 구조적인 동시성과 안전한 Scope 제공을 위해서 특별한 Library를 제공하고 있습니다.

대표적으로

ViweModelScope 알아보기

예제를 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()
    }
}

LifecycleScope 알아보기

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
}
  1. ViewModelScope 이용해서 Network 에 비동기 통신을 시도합니다.