[Kotlin] 코루틴

이상목·2024년 5월 5일
0

Kotlin

목록 보기
6/20
post-thumbnail

코루틴 적용

목차

  1. Coroutine 개념
  2. dependency 설정
  3. delay 활용
    3-1. delay 특징
  4. dispatcher 활용
  5. Coroutine Contxt 만들기
  6. 여러 Context 이동하며 사용하기



1. Coroutine 개념

Coroutine은 다양한 테스크를 진행할 때 필요한 요소이다.
스레드(Thread)와 햇갈릴 수 있는데 스레드와 다른 점은

  1. Coroutine은 스레드와 함께 사용되고
  2. Coroutine은 코드를 실행 중일 때 멈출 수 있고(suspendable) 다시 실행할 수 있는(resume) 제어 능력을 가지고 있지만 스레드는 불가능하다.
  3. 코루틴을 사용하면 작업을 쉽게 전환하며 스레드를 옮겨다니며 작업할 수 있게 된다.
  4. 효율적이고 처리 속도도 빠르다.



2. dependency 설정

  • 코루틴을 사용하려면 dependency 설정을 해 주어야 한다.
  • build.gradle.kts에서 dependency를 설정한다.

dependencies {

    // coroutine
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.3")
   
    implementation(libs.androidx.core.ktx)
}



3. delay 활용

  • Coroutine을 실행하는 가장 기본적인 방법은 GlobalScope를 이용하는 것이다.
  • 아래 예시처럼 GlobalScope.launch()를 하게되면 Job이라는 타입을 리턴해준다.
  • Job은 백그라운드 작업을 의미한다.
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        // 화면 그려주는 코드

        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)
        
        val TAG = "MainActivity";
        
        GlobalScope.launch { 
            // 1초후 실행
            delay(1000L) 
            Log.d(TAG, "thread1 ${Thread.currentThread().name}")
        }
        
        Log.d(TAG, "thread2 ${Thread.currentThread().name}")
        
}
  • 이렇게 GlobalScope 안에 작업을 제어할 수 있는 delay를 넣어줄 수 있다.
  • delay를 사용하게 되면 지금 이 GlobalScope에 쓰레드가 멈추게 된다.
    단, 모든 스레드가 중단되는 것이 아니다.
  • sleeping은 모든 스레드를 멈추 게 한다면, Coroutine을 사용한 delay는 특정 스레드만 중단하고 다른 스레드는 정상 작동한다.
  • 크래서 위 코드를 실행시키면 thread2가 실행되고 1초 뒤 thread1이 실행된다.
    즉, thread1이 delay 상태로 들어갔을 때 thread2는 뒤에서 실행중 인 것을 알 수 있다.


3-1. delay 특징

  • delay는 GlobalScope와 Suspend function 두 곳에서 사용할 수 있으며, 다른 곳에서는 사용할 수 없다.
  • Suspend function 예시
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        // 화면 그려주는 코드

        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)
        
        val TAG = "MainActivity";
        
        GlobalScope.launch(Dispatchers.Main) {

            Log.d(TAG,"1")

            delay(3000L)
            Log .d(TAG, "Coroutine says hello from thread1 ${Thread.currentThread().name}")
            Log.d(TAG, "2")

            doNetworkCall()
            Log.d(TAG, "3")

            doNetworkCall2()
            Log.d(TAG, "4")
        }
        Log.d(TAG, "5")
        
}

//suspend function 활용
suspend fun doNetworkCall():String{
        delay(1000L)
        return "this is the answer"
}
suspend fun doNetworkCall2(): String{
        delay(2000L)
        return "this is the answer"
}
  • 이렇게 suspend를 사용하면 delay를 함수에 넣어서 사용할 수 있다.
  • delay가 들어갔을 때 활동 주기의 이해를 돕기 위해 log를 추가해 보겠다.
2024-05-05 12:20:51.123 10245-10245 MainActivity            com.sangmoki.bts_photo               D  5
2024-05-05 12:20:51.129 10245-10245 MainActivity            com.sangmoki.bts_photo               D  1
2024-05-05 12:20:51.179 10245-10264 OpenGLRenderer          com.sangmoki.bts_photo               E  Unable to match the desired swap behavior.
2024-05-05 12:20:51.719 10245-10245 WindowOnBackDispatcher  com.sangmoki.bts_photo               W  sendCancelIfRunning: isInProgress=falsecallback=android.view.ViewRootImpl$$ExternalSyntheticLambda17@482f0b1
2024-05-05 12:20:54.133 10245-10245 MainActivity            com.sangmoki.bts_photo               D  Coroutine says hello from thread1 main
2024-05-05 12:20:54.133 10245-10245 MainActivity            com.sangmoki.bts_photo               D  2
2024-05-05 12:20:55.135 10245-10245 MainActivity            com.sangmoki.bts_photo               D  3
2024-05-05 12:20:55.768 10245-10281 ProfileInstaller        com.sangmoki.bts_photo               D  Installing profile for com.sangmoki.bts_photo
2024-05-05 12:20:57.136 10245-10245 MainActivity            com.sangmoki.bts_photo               D  4
2024-05-05 12:21:24.356 10245-10255 gmoki.bts_photo         com.sangmoki.bts_photo               W  Cleared Reference was only reachable from finalizer (only reported once)
  • 5와 1번이 동시에 실행되고 3초 후(delay3000) 2번 1초 후(donetworkCall) 3번 2초 후(doNetworkCall2) 4번이 로드된다.



4. Dispatcher 활용하기

  • GlobalScope에 launch는 파라미터로 Dispatcher을 받을 수 있다.
  • Dispatcher는 CoroutineContext를 상속받아 어떤 스레드를 이용해서 동작할 것인지 미리 정해주는 역할을 한다.
  • Dispatcher에는 크게 4가지 종류가 있다.

4-1. Dispatcher 종류

Dispatchers.Main - 메인 쓰레드에서 사용하게 되고, UI 관련 작업을 할 때 사용한다.

Dispatchers.IO - 네트워크 작업에서 사용하며, 데이터를 쓰고 읽는데 적합하다.

Dispatchers.Default - 계산이 많이 필요할 때 유용하다. 예를들어 10000개의 계산이 필요하면 다른 쓰레드를 방해하지 않고 계산할 수 있게 한다.

Dispatchers.Unconfined - 제한되지 않는다.
  • Dispatcher 사용 예시
// Dispatcher는 Main, IO, Default, Unconfined 등으로 변경이 가능하다.
GlobalScope.launch(Dispatchers.Main) {
}



5. Coroutine Context 만들기

  • 4번 처럼 dispatcher를 사용하지 않고 직접 스레드를 만들어 줄 수 있다.
GlobalScope.launch(newSingleThreadContext("My Thread")) {}

6. 여러 Context를 이동하며 사용하기

  • dispatcher.IO Thread 안에서 네트워크 데이터를 가져왔다고 했을 때, 이 데이터를 UI에 사용하고 싶다면 Dispatcher.Main Thread를 사용해야 한다.
  • 이렇게 두 thread를 모두 써야 할 때 coroutine을 통해 쉽게 스레드를 바꿔 사용할 수 있다.
GlobalScope.launch(Dispatchers.IO) { //네트워크에서 불러올 데이터
            Log.d(TAG,"1")
            val answer = doNetworkCall() //이 함수를 보면 3초 delay가 있기때문에 3초뒤에 dispatcherMain 스레드를 실행하게 된다. 
            
            withContext(Dispatchers.Main){ //now this thread will run main thread
                Log.d(TAG, "2")
                TextView.text = answer

			}

}
        
//doNetwork Call()은 delay예시에서 사용했던 suspend function다.

suspend fun doNetworkCall():String{
        delay(3000L)
        return "this is the answer"
}
profile
기록은 기억을 지배한다.

0개의 댓글