일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- android ar 개발
- jvm 작동 원리
- 자바 다형성
- rxjava hot observable
- 안드로이드 유닛 테스트 예시
- 큐 자바 코드
- 클래스
- rxjava disposable
- 안드로이드 레트로핏 사용법
- 객체
- 스택 자바 코드
- rxjava cold observable
- 서비스 vs 쓰레드
- 안드로이드 라이선스
- 스택 큐 차이
- 2022 플러터 안드로이드 스튜디오
- jvm이란
- 2022 플러터 설치
- 안드로이드 유닛테스트란
- android retrofit login
- 서비스 쓰레드 차이
- 안드로이드 유닛 테스트
- 안드로이드 os 구조
- ANR이란
- 안드로이드 레트로핏 crud
- 멤버변수
- ar vr 차이
- Rxjava Observable
- 안드로이드 라이선스 종류
- 플러터 설치 2022
- Today
- Total
나만을 위한 블로그
[Android] withContext란? 본문
코루틴 예제들을 찾아보다 보면 가끔 withContext라는 걸 볼 수 있다. 코틀린 공식문서에서 설명하는 withContext는 아래와 같다.
지정된 코루틴 컨텍스트를 써서 일시 중단 블록을 호출하고 완료될 때까지 일시 중단한 다음 결과를 반환한다. 블록에 대한 결과 컨텍스트는 coroutineContext + context를 써서 현재 coroutineContext를 지정된 컨텍스트와 병합해 파생된다. 이 정지 함수는 취소할 수 있다. 결과 컨텍스트의 취소를 즉시 확인하고 활성화하지 않은 경우 CancellationException을 발생시킨다. 컨텍스트 인수가 현재 것과 다른 CoroutineDispatcher를 제공하는 withContext에 대한 호출은 필요에 따라 추가 디스패치를 수행한다. 블록은 즉시 실행될 수 없으며 전달된 CoroutineDispatcher에서 실행을 위해 디스패치되어야 한다. 그런 다음 블록이 완료되면 실행은 원래 디스패처로 다시 이동해야 한다. withContext 호출의 결과는 즉시 취소 보장과 함께 취소 가능한 방식으로 원래 컨텍스트로 발송된다
안드로이드 디벨로퍼에서도 withContext에 대해 말하는 내용이 있어 가져왔다.
https://developer.android.com/kotlin/coroutines/coroutines-adv?hl=ko#perf
withContext()는 상응하는 콜백 기반 구현에 의해 오버헤드를 추가하지 않는다. 또한 일부 상황에서 상응하는 콜백 기반 구현을 능가하도록 withContext() 호출을 최적화할 수 있다. 함수가 네트워크를 10번 호출하는 경우 외부 withContext()를 써서 쓰레드를 한 번만 전환하도록 코틀린에 지시할 수 있다. 그러면 네트워크 라이브러리에서 withContext()를 여러 번 쓰더라도 동일한 디스패처에 유지되고 쓰레드가 전환되지 않는다. 또한 코틀린은 가능한 한 쓰레드 전환을 방지하도록 Dispatchers.Default와 Dispatchers.IO 간의 전환을 최적화한다
https://www.geeksforgeeks.org/withcontext-in-kotlin-coroutines/
async, launch는 코루틴을 시작하는 2가지 방법으로 알려져 있다. async는 결과를 받기 위해 쓰는 것으로 알려져 있어 &는 병렬 실행이 필요할 때만 써야 하는 반면, launch는 결과를 돌려받고 싶지 않을 때 사용하며 업데이트 같은 작업에 사용된다. 비동기가 지금까지 코루틴을 시작하고 결과를 다시 가져오는 유일한 방법이라는 걸 알고 있지만 비동기의 문제는 병렬 네트워크 호출을 원하지 않을 때 발생한다. async를 쓸 때 await()를 써야 메인 쓰레드가 차단되는 것으로 알려져 있지만 여기선 메인 쓰레드 차단 문제를 제거하는 withContext 개념을 소개한다. withContext()는 await()를 작성할 필요가 없는 비동기를 작성하는 또 다른 방법일 뿐이다. withContext를 쓰면 작업을 직렬로 실행한다. 따라서 백그라운드에 단일 작업이 있고 해당 작업의 결과를 반환하려는 경우 withContext를 사용해야 한다
https://androidwave.com/coroutines-withcontext-example/
withContext를 쓰면 컨텍스트를 쉽게 바꿀 수 있다. 이것은 디스패처를 쉽게 전환한다는 걸 의미한다. 안드로이드 앱에서 수행해야 하는 무거운 처리가 있을 때 Default Dispatchers에서 수행하고 완료되면 UI에 결과를 표시하기 위해 Main Dispatchers로 바꾸려고 한다. 따라서 컨텍스트 간에 쉽게 전환할 수 있는 방법이 필요한데 이걸 수행하는 가장 좋은 방법은 withContext()다. 가볍기 때문이다
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
fun main() {
runBlocking {
launch(Dispatchers.Default) {
// default context
println("First context: $coroutineContext")
withContext(Dispatchers.IO) {
// IO context
println("Second context: $coroutineContext")
}
// back to default context
println("Third context: $coroutineContext")
}
}
}
// First context: [StandaloneCoroutine{Active}@39f18e56, Dispatchers.Default]
// Second context: [DispatchedCoroutine{Active}@6c6fe848, Dispatchers.IO]
// Third context: [StandaloneCoroutine{Active}@39f18e56, Dispatchers.Default]
이 코드에선 먼저 Default 디스패처에서 코루틴을 시작한다. 그리고 coroutineContext를 출력한다. 그 후 디스패처를 IO 디스패처로 바꾼다. 따라서 withContext(Dispatchers.IO)를 써서 2번째 컨텍스트를 출력한다. 완료되면 1번째 컨텍스트로 돌아간다. 따라서 기본 컨텍스트에서 코루틴을 시작한 다음 IO로 전환하고 다시 원래 상태로 돌아간다...(중략)...따라서 기본적으로 기능 유형, 수행해야 하는 처리 유형에 따라 컨텍스트를 전환하는 매우 가벼운 방법이다
withContext에 대해 정리하면 아래와 같다.
- 코루틴 라이브러리에서 제공하는 함수. 코루틴의 실행 컨텍스트(쓰레드 or 디스패처)를 바꿀 때 사용한다
- 현재 코루틴이 실행 중인 컨텍스트에서 다른 컨텍스트로 잠깐 전환해 중괄호 블록 안의 코드를 실행할 수 있다
- 시간이 오래 걸리는 작업을 메인 쓰레드에서 분리해 UI를 차단하지 않게 하거나, 다른 쓰레드 or 디스패처에서 실행해야 하는 코드가 있을 경우 사용한다
메인 쓰레드와 다른 실행 공간을 만들고 거기서 중괄호 블록 안의 코드들을 실행한다고 보면 된다. 다른 예제 코드를 보자.
import kotlinx.coroutines.*
import kotlin.random.Random
suspend fun main() {
println("메인 코루틴 시작")
val result = withContext(Dispatchers.IO) {
executeLongTimeTask()
}
println("result : $result")
println("메인 코루틴 종료")
}
suspend fun executeLongTimeTask(): Int {
delay(1000L)
return Random.nextInt(100)
}
메인 함수를 실행하면 어떤 결과가 표시될까? 일단 메인 코루틴 시작 문자열을 먼저 출력할 것이다.
그 다음에는 withContext {} 가 호출되는데, withContext {} 안의 코드 실행과 resut 변수 출력, 메인 코루틴 종료 문자열 출력 중 어느 게 가장 먼저 실행될까?
withContext {} 가 호출되면 메인 쓰레드는 잠시 멈추고, IO 디스패처를 사용하는 별도의 작업 공간에서 executeLongTimeTask()를 실행한다. 이 함수의 영향으로 1초 뒤 0~100까지의 숫자 중 랜덤한 숫자를 하나 뽑아서 result 변수에 담은 뒤 withContext는 종료된다. 이후 result 변수가 출력되고 메인 코루틴 종료까지 표시된다.
즉 비동기 코드 안에서 withContext {}를 사용하면 동기식으로 코드가 작동하게 할 수 있다. 그러나 모든 것은 과하게 사용하면 부작용이 있듯 withContext 또한 필요 이상으로 사용하면 부작용이 발생할 수 있다.
- 성능 저하 : 코루틴의 실행 컨텍스트를 바꾸게 되면 오버헤드가 발생한다. 이 때문에 코루틴의 전체 실행 시간이 오래 걸리게 되고, 결과적으로 앱 성능이 저하될 수 있다
- 코드 복잡성 증가, 가독성 감소 : 위의 특징을 가진 withContext를 많이 쓰면 코드 복잡성이 증가하고 디버깅이 어려워지면서 유지보수에 어려움이 발생할 수 있다
또한 IO 작업은 Dispatchers.IO에서 돌아가게 하는 등 준비된 디스패처 중에서 해야 하는 작업에 따라 적절한 디스패처를 선택해야 한다.
'Android' 카테고리의 다른 글
[Android] 페이징 라이브러리, Hilt, Flow로 Github API 사용하기 (0) | 2023.04.09 |
---|---|
[Android] 페이징 라이브러리, Hilt, LiveData로 Github API 사용하기 (0) | 2023.04.09 |
[Android] CameraX 코드랩 뜯어보기 - 3 - (0) | 2023.03.27 |
[Android] CameraX 코드랩 뜯어보기 - 2 - (0) | 2023.03.26 |
[Android] dataUrl이란? 웹뷰로 dataUrl 전송하는 법 (0) | 2023.03.25 |