This is for recording 'Today-I-Learned'
Postings are managed on 'Issues' catergory
dlqkfzlr / til Goto Github PK
View Code? Open in Web Editor NEWThis is for recording 'Today-I-Learned'
This is for recording 'Today-I-Learned'
from 태환님 블로그
class SearchViewModel @ViewModelInject constructor(
private val repository: GiphyRepository
) : ViewModel() {
fun searchGiphy(query: String) {
viewModelScope.launch {
withContext(Dispatchers.IO){
val response = repository.requestToSearchGiphy(query, 10, 0)
}
}
}
}
ViewModel에서 suspend메서드를 호출할때
viewModelScope.lauch{ } 를 통해 블럭 내에서 suspend메서드를 호출함.
하지만 이때 viewModelScope.lauch내에서 굳이 Dispatchers.IO로 설정할 필요가 없음!
viewModelScope는 내부적으로 Main Scheduler를 사용하기 때문에,
당연하게도 withContext(Dispatchers.IO)를 적용해줘야되지 않을까라고 생각할 수 있지만
이는 잘못된 생각이다.
// retrofit2의 KotlinExtensions.kt 내부
suspend fun <T : Any> Call<T>.await(): T {
return suspendCancellableCoroutine { continuation ->
continuation.invokeOnCancellation {
cancel()
}
enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
if (response.isSuccessful) {
val body = response.body()
if (body == null) {
val invocation = call.request().tag(Invocation::class.java)!!
val method = invocation.method()
val e = KotlinNullPointerException("Response from " +
method.declaringClass.name +
'.' +
method.name +
" was null but response body type was declared as non-null")
continuation.resumeWithException(e)
} else {
continuation.resume(body)
}
} else {
continuation.resumeWithException(HttpException(response))
}
}
override fun onFailure(call: Call<T>, t: Throwable) {
continuation.resumeWithException(t)
}
})
}
}
retrofit2의 KotlinExtensions.kt파일을 살펴보면 Coroutine사용시 Callback을 처리하는 방법이 기술되어 있다.
enqueue를 사용해서 onResponse를 받아오게 되는데, 이때 onResponse는 자동으로 Main스케줄러로 동작을 한다.
따라서 위처럼 IO스케줄러를 사용하려 해도 어짜피 Main스케줄러를 사용하게 된다.
요약하자면 백그라운드 스레드에서 네트워크 통신을 하고
그걸 onResponse로 받을때는 메인 스레드에서 작업을 하고
호출한 Coroutine쪽으로 결과값을 return 해주는 프로세스를 거친다.
그래서 Room을 사용하여 캐싱이 필요한 경우가 아니라면 굳이 IO스케줄러를 사용할 필요가 없다.
By Wiki →
코루틴은 (컴퓨터 프로그램 구성 요소 중 하나로 )
비선점형(non-preemptive) 멀티태스킹을 수행하는
일반화한 서브루틴(Subroutine)이다.
⇒ 일단, 멀티태스킹은 여러 작업을 동시에 수행하는 것처럼 보이거나 실제로 동시에 수행하는 것.
그럼 비선점형이란?
'멀티태스킹의 각 작업을 수행하는 참여자들의 실행을
운영체제(OS)가 강제로 일시 중단시키고 다른 참여자를 실행하게 만들 수 없다'는 뜻이다.
즉, 각 참여자들이 서로 자발적으로 협력해야만 비선점형 멀티태스킹이 제대로 작동할 수 있다.
⇒ 여러 명령어를 모아 이름을 부여해서 반복 호출할 수 있게 정의한 프로그램 구성 요소
Co + (sub)routine ; 쉽게 말해 협동 루틴으로
서로 협력해서 실행을 주고받으면서 작동하는 여러 Subroutine을 말한다.
비동기 작업을 Sequential하게 쓸 수 있다.
Google I/O 19'에 나온 좋은 예시로 설명
1번째 방법) 이렇게 Coroutine없이 (=suspend함수없이) sequential하게 서버 통신을 하게 된다면,
UIthread에서 fetchUser가 진행되기 때문에 응답을 받을 때까지 blocking이 일어나게 되어서
ANR이 일어나게 됨.
2번째 방법) 비동기로 Callback을 사용하여 다른 Thread로 서버 응답을 받음.
이렇게 하면 동작은 하지만 코드가 간결하지 않음
3번째 방법) suspend함수로 Coroutine을 사용해서 서버 응답을 받음.
결과적으로 비동기 작업을 Sequential하게 코드로 작성이 가능해서 가독성이 매우 좋다.
즉, suspend시에 pause상태로 바뀌고 완료되면 중단됐던 시점부터 resume한다.
suspend의 기능을 콜백을 가지는 것으로 이해해도 좋다.
그리고 resume은 callback이 작동할때와 동일함.
바로 suspend함수 내부에는 Continuation이라는 객체가 존재하기 때문이다.
참고) https://tv.naver.com/v/15354002/list/629240
Dispatchers에 대해 알기 전에 CoroutineContext에 대한 이해가 우선이다.
CoroutineContext란 Coroutine을 어떻게 처리할 것인지에 대한 여러가지 정보의 집합니다. CoroutineContext의 주요 요소로는 Job과 Dispatcher가 있다.
이 중 Dispatcher는 '어떤 Thread에서 해당 coroutine을 실행할지'에 대한 정보를 담고 있다.
CPU-intensive한 작업을 할 때 사용!
ex) 몇백개의 element를 변환하는 작업, DiffUtil사용시 등등
Network & Disk와 관련된 Blocking 작업을 할 때 사용!
ex) File을 write하는 작업, Socket을 read하는 작업
UI이벤트에 대한 response를 실행하야 하는 작업에 사용!
Google은 suspend의 함수의 시작점에서 사용하는걸 추천.
그러면 간단한 작업시 UI thread에서 모든걸 끝낼 수 있기 때문.
I/O작업이 필요하다면 그때 withContext를 사용해서 Dispatchers를 변경하면 됨
runBlocking과 coroutineScope내의 launch는 모두 블록내에서 CoroutineScope를 scope로 사용한다.
또한 블록내의 body와 children의 작업이 모두 끝날때까지 기다린다는 공통점을 가지고 있다.
하지만 이 둘은 엄연히 다른 함수이다.
runBlocking은 regular function
coroutineScope는 suspend function
따라서, runBlocking은 내부작업의 waiting을 위해 현재 스레드를 실제로 block하지만,
coroutineScope는 block하는 것이 아니라 suspend하기 때문에 작업이 안끝나도 기반 스레드를 다른 용도를 위해 release한다.
fun main() = runBlocking { // this: CoroutineScope
launch {
delay(2000L)
println("Task from runBlocking")
}
coroutineScope { // Creates a coroutine scope
launch {
delay(5000L)
println("Task from nested launch")
}
delay(1000L)
println("Task from coroutine scope")
}
println("Coroutine scope is over")
}
Task from coroutine scope
Task from runBlocking
Task from nested launch
Coroutine scope is over
위의 코드를 보면 coroutineScope는 suspend 함수이기 때문에
내부 launch의 코드 println("Task from nested launch") 가 아직 끝나지 않았음에도
coroutineScope 외부의 println("Task from runBlocking") 를 실행한다.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.