반응형
[Kotlin] 코루틴 Coroutine 사용법 및 개념 정리 - GlobalScope, GlobalScope.launch, Delay, Dispatcher, Coroutine Context - 1편
이번포스트에서는 GlobalScope, GlobalScope.launch, Delay, Dispatcher, Coroutine Context 의 개념과 사용 예시를 다뤄보도록 하겠습니다.
Coroutine 개념
Coroutine은 다양한 테스크를 진행할 때 필요한 요소입니다. 스레드(Thread)와 햇갈리실수 있는데 스레드와 다른 점은
1. Coroutine은 스레드와 함께 사용되고
2. Coroutine은 코드를 실행 중일 때 멈출 수 있고(suspendable) 다시 실행할 수 있는(resume) 제어 능력을 가지고 있지만 스레드는 불가능합니다.
3. 코루틴을 사용하면 작업을 쉽게 전환하며 스레드를 옮겨다니며 작업할 수 있게 됩니다.
4. 효율적이고 처리 속도도 빠릅니다.
Dependency 추가
- Croutine을 사용하기 전 dependency를 추가해줘야 합니다.
dependencies {
//coroutine
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.5'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5'
}
Coroutine을 사용하여 Delay 활용하기
- Coroutine을 실행하는 가장 기본적인 방법은 GlobalScope을 이용하는 겁니다.
- 아래 예시처럼 GlobalScope.launch()를 하게되면 이함수는 Job이라는 타입을 리턴하게됩니다.
- Job은 백그라운드 작업을 의미합니다.
- MainActivity
class MainActivity : AppCompatActivity() {
val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
GlobalScope.launch() {
delay(3000L)//3초후에 실행
Log.d(TAG, "thread1 ${Thread.currentThread().name}")
}
Log.d(TAG, "thread2 ${Thread.currentThread().name}")
}
}
- 이렇게 GlobalScope안에는 작업을 제어할 수 있는 delay를 넣어줄 수 있습니다.
- delay를 사용하게 되면 지금 이 GlobalScope에 스레드가 멈추게 됩니다. 그런데 모든 스레드가 멈추는 게 아닙니다.
- sleeping은 모든 스레드를 멈춘 게 한다면 Coroutine을 사용한 delay는 특정 테스크만 중단하고 다른 테스크는 정상 작동합니다.
- 그러므로 위 코드를 실행시킨 후 로그를 확인해보면 thread2가 실행되고 3초 후 thread1 이 실행되는 걸 볼 수 있습니다.
- 이 말은 thread1이 delay상태에 들어갔을 때 thread 2는 뒤에서 실행 중인 것을 알 수 있습니다.
Delay 특징
- delay는 두 곳에서 사용할 수 있습니다. GlobalScope과 Suspend function **그 외의 곳에서는 사용할 수 없습니다.**
- Suspend function은 무엇일까요? 아래에서 만들어보겠습니다.
class MainActivity : AppCompatActivity() {
val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
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(3000L)
return "this is the answer"
}
suspend fun doNetworkCall2(): String{
delay(3000L)
return "this is the answer"
}
}
- 이렇게 suspend를 사용하면 delay를 fun안에 넣어서 사용할 수 있게 됩니다.
- 딜레이가 들어갔을 때 활동주기의 이해를 돕기 위해 log를 추가해서 보여드리겠습니다.
- 위 코드를 실행하면 아래와 같은 결과가 나옵니다.
- 5,1번이 동시에 실행되고 3초 딜레이 후 2번 3초 딜레이 후 3번 4초 딜레이 후 4번이 로드된 걸 볼 수 있습니다.
- 이와 같이 delay는 다른 delay가 끝난 후 추가되는 걸 보실 수 있습니다.
Dispatcher 활용하기
- GlobalScope에 launch는 파라미터로 Dispatcher을 받을수있습니다.
- Dispatcher는 CoroutineContext를 상속받아 어떤스레드를 이용해서 동작할 것인지 미리 정해주는 역할을 합니다.
- Dispatcher에는 크게 4가지가있습니다.
Dispatcher 종류 | 활용 |
Dispatchers.Main | 메인스레드에서 사용하게되고 UI관련된 작업을할때 씁니다. |
Dispatchers.IO | 네트워크 작업에서 씁니다. 데이터를 쓰고 읽는데 적합합니다. |
Dispatchers.Default | 계산이 많이 필요할때 유용합니다. 만약 10000개의 계산이 필요하면 다른 스레드를 방해하지않고 계산할수있게 합니다. |
Dispatchers.Unconfined |
//Dispatcher 사용
GlobalScope.launch(Dispatchers.Main) { //Main, IO 등등으로 바꿔주면됩니다.
}
Coroutine Context 만들기
- 위와 같이 dispatcher를 사용하지 않고 직접 스레드를 만들어 줄 수 있습니다.
GlobalScope.launch(newSingleThreadContext("MyThread")) {
}
여러 Context를 이동해가며 사용해야 할 때
- dispatcher.IO Thread 안에서 네트워크 데이터를 가져왔다고 했을 때, 이 데이터를 UI에 사용하고 싶다면 Dispatcher.Main Thread를 사용해야 합니다. 이렇게 두 thread를 모두 써야 할 때 coroutine을 통해 쉽게 스레드를 바꿔 사용할 수 있습니다.
- 아래 예시로 보여드리겠습니다.
- MainActivity
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"
}
다음포스트에서는 runblocking을 다뤄보도록하겠습니다.
반응형