본문 바로가기

안드로이드

[안드로이드] Coroutine은 무엇일까?

728x90
반응형

 

 

 

 

Coroutine

 

 

 

 

 

 

Coroutine이란 무엇인가요 ?

 

 

-> Coroutine비동기 프로그래밍을 간편하게 처리할 수 있도록 도와주는 동시 실행 패턴입니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Coroutine의 특징

 

-> 경량성, 비동기 작업의 직관적인 표현, Coroutine Scope, 일시 중지와 같은 특징이 있습니다.

 

 

 

 

 

 

 

 

 

 

 

 

그렇다면, 왜 Coroutine을 사용하나요 ?

 

1. Coroutine이 비동기 프로그래밍으로, 여러 작업을 처리할 수 있게 도와주기 때문입니다 !

 

서버와 데이터 통신을 하거나, 파일을 읽거나 쓸 때 앱이 멈추면 사용자가 앱을 사용하는데 불편함을 겪습니다.

비동기 프로그래밍이 이러한 작업들을 백그라운드에서 실행되도록 해주기 때문에 앱을 더 원활하게 사용할 수 있게 해줍니다.

 

 

2. Coroutine의 장점

 

- 가독성 : 복잡한 비동기 작업을 마치 동기 코드처럼 직관적으로 작성할 수 있습니다.

- 유지보수성 : 코루틴을 사용하면 코드가 더 명확해지고, 오류를 추적하기 쉬워 코드 수정이나 기능 추가 시 문제가 발생할 가능성이 줄어듭니다.

- 메모리 효율성 : 코루틴이 스레드보다 가볍습니다. 코루틴은 경량 스레드로, 동시에 실행해도 메모리 소비가 적습니다.

* 스레드는 운영체제에서 관리되며, 코루틴은 JVM에서 관리됩니다. 때문에 비용 차이도 많이 발생합니다.

 

 

 

 

 

 

 

 

 

다양한 방법으로 스레드와 코루틴의 효율성을 비교할 수 있지만, Visual VM으로 두 개를 비교해보겠습니다.

 

 

간단한 예제로 힙 메모리 사용량을 측정해보겠습니다.

 

import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

fun main() {
    createThreads()
    Thread.sleep(10000L)
//    createCoroutines()
//    Thread.sleep(10000L)
}

fun createThreads() {
    val threads = mutableListOf<Thread>()
    for (i in 1..10000) {
        val thread = Thread {
            Thread.sleep(1000L) // 1초 대기
        }
        threads.add(thread)
        thread.start()
    }
    threads.forEach { it.join() }
}

fun createCoroutines() = runBlocking {
    val jobs = List(10000) {
        launch {
            delay(1000L) // 1초 대기
        }
    }
    jobs.forEach { it.join() }
}

 

 

 

 

Thread 함수를 실행 했을 때

 

 

 

 

 

Coroutine 함수를 실행 했을 때

 

 

 

 

Thread는 약 764MB, Coroutine은 761MB의 힙 메모리를 사용합니다.

약 2MB로, 엄청난 차이를 나타내지는 않지만 이게 만약 많은 양의 Thread를 사용하는 앱이라면 더 차이가 날 것으로 예상됩니다.

(환경이나, 측정 시점에 따라 더 차이가 날 것 같습니다 ~)

 

 

 

 

 

 

 

 

 

 

 

Coroutine 사용

 

- build.gradle 추가 ( 버전확인 -> https://github.com/Kotlin/kotlinx.coroutines)

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC.2")
}

 

 

 

 

 

- Scope

 

<자주 사용하는 Scope>

 

 (1). GlobalScope : 코루틴을 전역적으로 관리하기 위해 사용되는 특수한 스코프입니다.

   - 애플리케이션 전체의 수명 동안 작동하는 최상위 코루틴을 시작하는데 사용됩니다. 

   - 리소스 및 메모리 누수 가능성이 있어 조심해야 하고, suspend 함수를 사용해서 GlobalScope를 대체할 수 있습니다.

 

fun loadConfiguration() {
    GlobalScope.launch {
        val config = fetchConfigFromServer() // 네트워크 요청
        updateConfiguration(config)
    }
}

 

 

  (2). viewModelScope : Android의 ViewModel 라이프사이클에 맞춰 코루틴을 관리하는 스코프입니다.

    - ViewModel이 소멸되면 코루틴이 취소됩니다. 

    - UI와 관련된 비동기 작업을 할 때 사용합니다.

 

class MyViewModel: ViewModel() {
    init {
        viewModelScope.launch {
            
        }
    }
}

 

 

 

  (3). lifecycleScope : Android의 'Lifecycle' 객체와 결합된 코루틴 스코프입니다.

    - Activity나 Fragment의 생명주기에 따라 코루틴을 자동으로 취소합니다.

    - UI 업데이트와 관련된 비동기 작업에 적합합니다.

 

class MyFragment: Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewLifecycleOwner.lifecycleScope.launch {
            val params = TextViewCompat.getTextMetricsParams(textView)
            val precomputedText = withContext(Dispatchers.Default) {
                PrecomputedTextCompat.create(longTextContent, params)
            }
            TextViewCompat.setPrecomputedText(textView, precomputedText)
        }
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

Coroutine 사용법

 

1. launch & async

 

- launch : 새로운 코루틴을 시작하고, 그 결과를 기다리지 않을 때 사용합니다. -> Job을 통해 객체를 반환

launch {
        delay(1000L) // 비동기 작업 수행
        println("Launch: Task completed")
    }

 

- async : 새로운 코루틴을 시작하고, 결과를 반환받을 수 있습니다. -> Deferred<T>로 객체 반환

 * await() 메서드로 결과를 기다릴 수 있습니다.

val deferred: Deferred<String> = async {
        delay(1000L) // 비동기 작업 수행
        "Async result"
    }

 

 

 

 

2. withContext

 

- 코루틴의 context를 변경하여 다른 Dispatcher에서 작업을 실행할 때 사용합니다.

- I/O 작업이나, CPU 작업을 적절히 사용할 때 유용합니다.

- 종류는 Main, IO, Default, Unconfined 가 있습니다.

 

 

 

 

 

 

 

 

 

 

Coroutine 사용 시 주의할 점 

-> 코루틴 사용 시, 적절한 처리를 하지 않으면 예기치 않은 동작이 발생해 자원을 낭비하기 때문에 아래의 기능들도 고려해야 합니다.

 

 

- 취소 : Job 객체를 통해 취소할 수 있습니다 -> 취소하지 않으면 메모리, 네트워크 연결 등.. 낭비됩니다.

fun main() = runBlocking {
    val job: Job = launch {

    }

    job.cancel() // 코루틴 취소
}

 

 

 

- 타임아웃 : 비동기 작업이 너무 오래 걸릴 때 작업을 자동으로 취소하기 위한 방법입니다 -> 시스템이 무응답 상태에 빠지는 것을 방지합니다.

 * withTimeout은 TimeoutCancellationException이 발생, withTimeoutOrNull은 null을 반환합니다.

fun main() = runBlocking {

    // 시간 초과시 TimeoutCancellationException 예외가 발생
    try {
        val result = withTimeout(1000L) {

        }
        
    } catch (e: TimeoutCancellationException) {
        println("Timeout occurred: ${e.message}")
    }
    
    
    // 시간 초과시 예외를 발생시키지 않음
    val nullResult = withTimeoutOrNull(1000L) {
    
    
        }
}

 

 

 

 

- 에러처리 : 코루틴에서 발생하는 예외를 적절하게 처리해줘야 앱의 비정상적인 종료를 막을 수 있습니다.

 

 

 

 

 

 

 

 

 

 

 

 

Android에서 코루틴 활용 예제 

(간단히 !)

 

 

 

 

MemoService.kt

interface MemoService {

    @GET("memo")
    suspend fun fetchMemos(): List<Memo>
}

 

- suspend 함수를 통해 코루틴 내에서 호출될 수 있도록 해줍니다.

 

 

 

 

MainViewModel.kt

class MainViewModel(private val memoRepository: MemoRepository): ViewModel() {

    private val _memos = MutableLiveData<List<Memo>>()
    val memos: LiveData<List<Memo>> get() = _memos

    fun fetchMemos() {
        viewModelScope.launch(Dispatchers.IO) {
            try {
                val memoList = memoRepository.fetchMemos()
                _memos.postValue(memoList)
            } catch (e: Exception) {
                _memos.postValue(emptyList())
            }
        }
    }
}

 

- viewModelScope를 통해 viewmodel 라이프 사이클을 따르도록 하고, Dispatchers.IO를 통해 네트워크 요청을 수행하도록 합니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

결론

코루틴은 스레드의 좋은 대안으로, 비동기 프로그래밍을 원할하게 해줘 시스템 리소스를 효율적으로 관리할 수 있게 됩니다.

이를 통해 비동기 작업의 가독성을 높이고, 유지보수성을 개선하며 코드를 간결하게 짤 수 있게 도와줍니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

감사합니다 !!!!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

참고 자료

 

https://kotlinlang.org/docs/coroutines-basics.html#your-first-coroutine

 

Coroutines basics | Kotlin

 

kotlinlang.org

 

https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/

 

kotlinx.coroutines

General-purpose coroutine builders, contexts, and helper functions. General-purpose coroutine builders, contexts, and helper functions. General-purpose coroutine builders, contexts, and helper functions. General-purpose coroutine builders, contexts, and he

kotlinlang.org

https://developer.android.com/codelabs/basic-android-kotlin-training-introduction-coroutines?hl=ko#0

 

코루틴 소개  |  Android Developers

코루틴 소개

developer.android.com

https://developer.android.com/kotlin/coroutines?hl=ko#groovy

 

Android의 Kotlin 코루틴  |  Android Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. Android의 Kotlin 코루틴 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 코루틴은 비동기적으로 실행되는

developer.android.com

 

728x90
반응형