멀티스레드는 CPU를 조금씩 나눠쓰는 모양임
스레드가 요리사라면 멀티 스레드는 여러 요리사가 화구 (CPU)를 번갈아 쓰는 구조 그래서 다른 요리사가 사용중에는 기존 요리사는 동작을 멈춘다
코루틴은 하나의 요리사(스레드)가 파스타를 만들면서 스테이크까지 굽는 형식, 즉 팬이 두개
자리를 비켜가며 멈추는 행동이 없어서 조금 더 빠르게 동시 작업이 가능하다
package com.bsj0420.ex100coroutinetest
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import com.bsj0420.ex100coroutinetest.databinding.ActivityMainBinding
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
val binding :ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
// * 코루틴 - 경량 스레드
// 스레드를 멈추지 않고 비동기처리하는 것
// 하나의 스레드 안에 여러개의 코루틴 실행
//스레드가 요리사라면 멀티 스레드는 여러 요리사가 화구 (CPU)를 번갈아 쓰는 구조
//그래서 다른 요리사가 사용중에는 기존 요리사는 동작을 멈춘다
//코루틴은 하나의 요리사(스레드)가 파스타를 만들면서 스테이크까지 굽는 형식, 즉 팬이 두개
//자리를 비켜가며 멈추는 행동이 없어서 조금 더 빠르게 동시 작업이 가능하다
// 프로세스 > 스레드 > 코루틴
//코루틴을 구동하는 2개의 범위(Scope)가 존재
//1. GlobalScope : 앱 전체의 생명주기와 함꼐 관리되는 범위
// 앱이 끝날떄 까지 처리할 일이 있을 때 사용하는 일을 할 대 쓴다
//2. CoroutineScope : 버튼 클릭 같은 특정이벤트 순간에 해야할 Job을 위해 실행되는 범위
// ex) 네트워크 통신 , DB CRUD (Room) 특정연산 수행 등...
//실습 1) GlobalScope 코드 연습
binding.btn.setOnClickListener {
//코루틴 없이 오래걸리는 작업 실행
for ( n in 0..9 ) {
Log.d("TAG", "n : $n")
Thread.sleep(500)
}
Toast.makeText(this, "aaa", Toast.LENGTH_SHORT).show()
}
//비동기 적업으로 위 작업을 수행 - 코루틴 사용하여...
binding.btn2.setOnClickListener {
GlobalScope.launch {
for ( n in 0..9 ) {
Log.d("TAG", "n : $n - ${Thread.currentThread().name}")
delay(500)
}
}
Toast.makeText(this, "aaa", Toast.LENGTH_SHORT).show()
}
}
}
Dispatcher의 종류
1) Dispatchers.Default
기본 스레드풀의 스레드를 사용 (CPU 작업이 많은 연산작업에 적함)
2) Dispatchers.IO
DB나 네트워크 IO 스레드 사용 (파일 입출력, 서버작업에 적합) ****
3) Dispatchers.Main
Main 스레드 사용 (UI 작업등에 적합)
4) Dispatchers.Unconfined
조금 특별한 디스패처 (해당 코루틴을 호출하는 스레드에서 실행)
package com.bsj0420.ex100coroutinetest
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import com.bsj0420.ex100coroutinetest.databinding.ActivityMainBinding
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.net.URL
class MainActivity : AppCompatActivity() {
val binding :ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
//실습 2) CoroutineScope 코드 연습
//CoroutineScope는 GlobalScope와 다르게 해당 작업을 어떤 스레드에게 보낼지 결정하는 Dispatcher을 지정해야됨
//Dispatcher의 종류
//1) Dispatchers.Default - 기본 스레드풀의 스레드를 사용 (CPU 작업이 많은 연산작업에 적함)
//2) Dispatchers.IO - DB나 네트워크 IO 스레드 사용 (파일 입출력, 서버작업에 적합) ****
//3) Dispatchers.Main - Main 스레드 사용 (UI 작업등에 적합)
//4) Dispatchers.Unconfined - 조금 특별한 디스패처 (해당 코루틴을 호출하는 스레드에서 실행)
//Dispatchers.Default
binding.btn3.setOnClickListener {
//Dispatchers.Default 사용
CoroutineScope(Dispatchers.Default).launch {
for (n in 100..110) {
Log.d("TAG","n : $n - ${Thread.currentThread().name}")
//UI 작업은 main 스레드만 가능
binding.tv.text = "n : $n"
delay(500)
}
}
Toast.makeText(this, "bbb", Toast.LENGTH_SHORT).show()
}
//Dispatchers.Main
binding.btn4.setOnClickListener {
CoroutineScope(Dispatchers.Main).launch {
for (n in 200..210) {
Log.d("TAG","n : $n - ${Thread.currentThread().name}")
//체크 필요 - 원래는 UI 변경 불가함,,,
binding.tv.text = "n : $n"
delay(500)
}
}
Toast.makeText(this, "main", Toast.LENGTH_SHORT).show()
}
//메인에 서버작업 시켜보기
binding.btn5.setOnClickListener {
CoroutineScope(Dispatchers.Main).launch {
//네트워크 이미지 불러오기 .. 에러 ! 메인 스레드는 네트워크 작업 불가능
val url = URL("https://cdn.pixabay.com/photo/2023/05/24/05/06/dog-8014047_1280.jpg")
val bm : Bitmap = BitmapFactory.decodeStream(url.openStream())
binding.iv.setImageBitmap(bm)
}
Toast.makeText(this, "main", Toast.LENGTH_SHORT).show()
}
//Dispatchers.IO : 입출력
binding.btn6.setOnClickListener {
CoroutineScope(Dispatchers.IO).launch {
//네트워크 이미지 불러오기 .. 에러 ! 메인 스레드는 네트워크 작업 불가능
val url = URL("https://cdn.pixabay.com/photo/2023/05/24/05/06/dog-8014047_1280.jpg")
val bm : Bitmap = BitmapFactory.decodeStream(url.openStream())
//유아이를 건드려서 에러
//binding.iv.setImageBitmap(bm)
//유아이는 메인 스레드가 하도록 시켜야됨!!!!
withContext(Dispatchers.Main) {
binding.iv.setImageBitmap(bm)
}
}
Toast.makeText(this, "io", Toast.LENGTH_SHORT).show()
}
// 스레드 동기 맞추기
//1. 동시에 작업하기
binding.btn7.setOnClickListener {
CoroutineScope(Dispatchers.Default).launch {
//작업 1
launch {
for (n1 in 1000..1010) {
Log.d("TAG","n1: $n1")
delay(500)
}
}
//작업 2
launch {
for (n2 in 2000..2010) {
Log.d("TAG","n2: $n2")
delay(500)
}
}
}
}
// 2. 동기 맞추기
binding.btn8.setOnClickListener {
CoroutineScope(Dispatchers.Default).launch {
//작업 1
launch {
for (n1 in 1000..1010) {
Log.d("TAG","n1: $n1")
delay(500)
}
}.join() //작업1이 끝날 떄 까지 다른 코루틴 실행 대기
//작업 2
launch {
for (n2 in 2000..2010) {
Log.d("TAG","n2: $n2")
delay(500)
}
}
// launch 말고 async도 있음
// async {
//
// }
}
}
//suspend 함수
binding.btn9.setOnClickListener {
CoroutineScope(Dispatchers.Default).launch {
someTask()
}
}
//코루틴 제어
var job : Job? = null //참조 변수 만들기 Job으로
// 스타트
binding.btn10.setOnClickListener {
//스코프르 런치하면 그것을 참조 할 수 있음
job = CoroutineScope(Dispatchers.Default).launch {
for (n1 in 300..310) {
Log.d("TAG","n1: $n1")
delay(500)
}
}
}
//캔슬 - 작업 캔슬 시킬 때 사용
binding.btn11.setOnClickListener {
job?.cancel()
}
}
// delay는 코루틴의 기능임 suspend 키워드를 붙이면 사용 가능해짐
// 코루틴 스코프 범위 밖에서 코루틴의 기능을 사용할 때 함수를 suspend 함수로 만들면 해결할 수 있음
suspend fun someTask() {
for (n in 1000..1010) {
Log.d("TAG", "someTask : $n")
delay(500)
}
}
}
lifecycle-runtime-ktx
버전이 안맞아서 오류날 수 있음현재 2.5.1 버전까진 잘됨 , 2.6.1 버전 오류남...
CoroutineScope은 화면 안보여도 계속 실행
LifecycleScope는 화면 꺼지면 멈춤
package com.bsj0420.ex101coroutineadroid
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//기본 코틀린 언어에서 지원하는 코루틴은 2종류 : GlobalScope, CoroutineScope
//안드로이드는 액티비티 또는 프래그먼트의 라이프사이클이 존재함
//이에 함께 반응하는 코루틴 존재
// lifecycleScope / ViewModelScope -- 별도의 라이브러리 추가 필요
//1. CoroutineScope
findViewById<Button>(R.id.btn).setOnClickListener {
clickBtn1()
}
//2. LifecycleScope
findViewById<Button>(R.id.btn2).setOnClickListener {
clickBtn2()
}
}
private fun clickBtn1() {
CoroutineScope(Dispatchers.Default).launch {
for (n in 0 .. 20) {
Log.d("TAG", "CoroutineScope : $n")
delay(500)
}
}
}
//안드로이드의 라이프사이클에 같이 제어되는 코루틴 스코트 : LifecycleScope
//알아서 디스패쳐 적절히 찾는다
//onCreate 부터 onDestory 가지의 액티비티 라이프사이플 Owner(액티비티, 프래그먼트)사용
private fun clickBtn2() {
this.lifecycleScope.launch {
for (n in 100 .. 120) {
Log.d("TAG", "lifecycleScope : $n")
delay(500)
}
}
}
override fun onBackPressed() {
finish()
}
}
얠 쓰는 걸 가장 선호함!~!!!!!!!
package com.bsj0420.ex101coroutineadroid
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
private fun clickBtn3() {
// onResume() 부터 onPause() 동안에만 코루틴 동작함
// onPause()되면 자동으로 일시정지
// 다시 onResume() 되면 자동으로 이어서 실행함!!!!
lifecycleScope.launchWhenResumed {
loopTask()
}
}
suspend fun loopTask() {
for (n in 0 .. 20) {
Log.d("TAG", "lifecycleScope when resume : $n")
delay(500)
}
}
override fun onBackPressed() {
finish()
}
}