#기본은 자바
fun main(){
print("hello world") #main함수는 fun main(){}, 세미콜론은 있어도 되고 없어도 되고
#prinln 도 존재
}
기본 변수는 var (변수명) = (값)
기본적으로 변수형태는 알아서 추리
변수명 정할 시->var (변수명) : (Int, String, Double.....) = (값)
상수는->val (변수명) = (값)
상수는 값 변경 불가
전역변수는 메인 함수 밖에
변수 or상수 앞에 const 붙일 시 메인함수보다 먼저 컴파일
var i = 10
var l = 20L
var name = "10"
i = name.toInt() #(변수명).to+(원하는 형)() 형태로 입력
베이스가java
ㄴuppercase,lowercase사용 가능
ㄴ배열도 변수명[] 사용 가능
ㄴprint할 때 변수가 필요하면 + 대신
ㄴKotlin.math.max(),min() 사용 가능
random 사용할때는
val randeomNumber = Randem.nextInt(0,100) #0~99까지
처럼 사용
val reader = Scanner(System.'in')
reader.nextInt() #숫자입력
reader.next() #문자입력
java와 동일
var i = 5
when {
i>10 -> {
print("10보다 크다")
}
i > 5 ->{
print("5보다 크다")
}
}
변수에 if or when 입력 가능
var i = 5
var result = if (i>10) true else false
val items = listOf(1,2,3,4,5)
for (item in items){
print(item)
}
val items = listOf(1,2,3,4,5)
for (i in 0..(items.size -1)){
print(item)
}
items.forEach {item ->
print(item)
}
변경가능한 list만들때는
val items = mutableListOf(1,2,3,4,5)
items.add(6)
items.remove(3)
val items = arrayOf(1,2,3)
items[0] = 10
try{
} catch (e.message){
}
var name: String? = null #String? 는 String와 다르다
null이 아닐 때 실행하려면
name?.let{
name2 = name
}
fun sum(a:Int, b:Int) : Int{
return a+b
}
또는
fun sum(a:Int, b:Int) = a+b
fun name(){
val jhon = Person("John", 20)
print(jhon.name, jhon.age)
}
class Person(val name: String, var age : Int,)
override를 통해 설정 or class 앞에 data 붙이기
data class Person()
class가 실행될때마다 실행되게 하려면 init 사용
class Person(){
init{
print("1")
}
}
init 부분에 private set 작성 시 외부에서 변경 불가
class Person(){
var hobby = "축구"
private set
get() = "취미": $field #여기는 변수명이 아닌 field 사용
init{
print("1")
}
}
fun main(){
}
class dog{
fun move(){
//
}
}
class cat{
fun move(){
//
}
}
을 바꾸면
fun main(){
}
abstract ckass Animal{
open fun move(){ #코틀린은 상속받아도 사용하려면 open 필요
print("이동")
}
}
class dog : Animal(){
override fun move(){
println("껑충")
}
}
class cat : Animal(){
override fun move(){
println("살금")
}
}
fun main(){
}
interface Drawable{
fun draw()
}
abstract ckass Animal{
open fun move(){ #코틀린은 상속받아도 사용하려면 open 필요
print("이동")
}
}
class dog : Animal(), Drawable{ #소괄호 없음
override fun move(){
println("껑충")
}
}
class cat : Animal(){
override fun move(){
println("살금")
}
}
이해 더 필요
fun main(){
val dog : Animal = DOG()
val cat = Cat()
if(dog is Dog){
println("멍멍이")
}
}
interface Drawable{
fun draw()
}
abstract ckass Animal{
open fun move(){
print("이동")
}
}
class dog : Animal(), Drawable{
override fun move(){
println("껑충")
}
}
class cat : Animal(){
override fun move(){
println("살금")
}
}
fun main(){
val box = Box(10)
val box2 = Box("dfdfdf")
print(box.value)
}
class box<T>(var value: T){
}
이해 더 필요
fun main(){
myFunc(10){ #람다식?
println("함수 호출")
}
}
fun myFunc(a:Int,callBack : () -> Unit){
println("함수 시작")
callBack()
println("함수 끝")
}
이래저래 이해 더 필요?
코루틴(Coroutine)은 일반 루틴과는 다르게 중단과 재개가 가능하며 중단한 곳에서 재개가 가능하다.
비동기 처리를 코루틴이 순차적으로 할 수 있도록 도와준다.
suspend에 대한 이해 및 비동기 처리에 대한 공부가 더 필요한 듯...
물론 후반에 갈수록 어려워지지만 시작하기에는 어렵지 않다
import kotlinx.coroutines.*
fun main(){
GlobalScope.launch{this: CoroutinesScope //launch:코루틴 빌드, GlobalScope:메인 코드가 작동된 후 실행.
delay(timeMillis: 1000L)
println("World!")
}
println("Hello,")
Thread.sleep(2000L)
}
ㄴ한번World!를 실행 후 계속 실행한다는데 프로그램이 종료돠어서 World!가 2번 이상 츨력되지 않는다고 함...어쨰서..?
ㄴ다시 생각해보니 메인 코드와 코루틴을 동시에 실행시키는데 프로그램 종료 조건은 메인 프로그랭 종료이니 그런듯?
import kotlinx.coroutines.*
fun main(){
GlobalScope.launch{this: CoroutinesScope
delay(timeMillis: 1000L)
println("World!")
}
println("Hello,")
runBlocking{this:CoruotineScope //코루틴 실행용 runBlocking
delay(timeMillis: 2000L)
}
}
import kotlinx.coroutines.*
fun main() =
runBlocking{this: CoroutineScope
GlobalScope.launch{this: CoroutinesScope //launch:코루틴 빌드, GlobalScope:메인 코드가 작동된 후 실행.
delay(timeMillis: 1000L)
println("World!")
}
println("Hello,")
delay(timeMillis: 2000L)
}
import kotlinx.coroutines.*
fun main() =
runBlocking{this: CoroutineScope
val job = GlobalScope.launch{this: CoroutinesScope
delay(timeMillis: 3000L)
println("World!")
}
println("Hello,")
job.join() //아마 함수 실행시키는 거로 보임
}
import kotlinx.coroutines.*
fun main() =
runBlocking{this: CoroutineScope
this.launch{this: CoroutinesScope //launch:코루틴 빌드, GlobalScope:메인 코드가 작동된 후 실행.
delay(timeMillis: 3000L)
println("World!")
}
launch{this: CoroutinesScope //launch:코루틴 빌드, GlobalScope:메인 코드가 작동된 후 실행.
delay(timeMillis: 3000L)
println("World!")
}
println("Hello,")
}
결론적으로 실행까지 기다려주는 것이 스트럭쳐드 컨크러시이고 상당히 자주 나온다.
import kotlinx.coroutines.*
fun main() =
runBlocking{this: CoroutineScope
launch{this: CoroutinesScope
myWorld()
}
println("Hello,")
}
suspend fun myWorld(){
delay(timeMillis: 1000L)
println("world!")
}
ㄴ서스펜드 평션(예:delay)는 서스펜드 및 코루틴에서만 사용 가능하다
fun main() = runBlocking{
repeat(100_000){
launch{
delay(1000L)
print(".")
}
}
}
ㄴ코루틴이 가볍다 증명용 코드
import kotlinx.coroutines.*
fun main() =
runBlocking{this: CoroutineScope
GlobalScope.launch {this: CoroutineScope
repeat(times:1000) {i->
println("I'm sleeping $i ...")
delay(timeMillis: 500L)
}
}
delay(timeMillis: 1300L)
}
ㄴ메인프로세스가 끝날 시 코루틴도 끝난다는 코드
import kotlinx.coroutines.*
fun main() =
runBlocking{this: CoroutineScope
launch {this: CoroutineScope
repeat(times:5) {i->
println("Coroutine A, $i ...")
delay(timeMillis:10L)
}
}
launch {this: CoroutineScope
repeat(times:5) {i->
println("Coroutine B, $i ...")
delay(timeMillis:10L)
}
}
println("Coroutine Outer")
}
ㄴ코루틴의 중단 및 재개의 예시 코드
메인 코드와 코루틴은 동시에 실행이 가능하지만 코루틴끼리는 싱글 스레드에서 동작하여서
내부 작업은 순차적으로 실행된다.
병렬로 동작하려면 동시성을 제어해야 한다.(await,async,delay등 사용)
import kotlinx.coroutines.*
fun main() = runBlocking { this: CoroitineScope
val job = launch { this: CoroutinesScope #캔슬 가능 개체
repeat(time:1000) {i ->
prinln(msg:"job: I'm sleeping $i ...")
delay(timeMillis: 500L)
}
}
delay(timeMillis: 1300L)
println(msg: "main: I'm tried of waiting!")
job.cancle()
job.join()
println(msg: "main: Now I can quit")
}
실행과 동시에 job이 실행->1.3초 후에 cancle이 실행되고 정지함->(그럼 join은 어째서 존재하는가?)????
import kotlinx.coroutines.*
fun main() = runBlocking {
val startTime = currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while (i < 5) { // computation loop, just wastes CPU
// print a message twice a second
//yield()
if (currentTimeMillis() >= nextPrintTime) {
println("job: I'm sleeping ${i++} ...")
nextPrintTime += 500L
}
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")
}
cancleandjoin이 잘 작동되지 않는 이유는 delay같은 서스펜드 펑션이 없어서
yield()가 있으면 delay없이도 가능
import kotlinx.coroutines.*
fun main() = runBlocking {
val startTime = currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while (isActive) { // cancellable computation loop
// print a message twice a second
if (currentTimeMillis() >= nextPrintTime) {
println("job: I'm sleeping ${i++} ...")
nextPrintTime += 500L
}
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")
}
isActive는 코루틴이 돌아갈 수 있는 상태인지 아닌지를 판별하는 것인데 cancle로 인해 불허되어서 코드 정지
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
try {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")
delay(500L)
}
} finally {
println("job: I'm running finally")
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")
}
cancle이 익셒션을 발생시키고 그것으로 인해 try가 아닌 finally가 실행된다
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
try {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")
delay(500L)
}
} finally {
withContext(NonCancellable) {
println("job: I'm running finally")
delay(1000L)
println("job: And I've just delayed for 1 sec because I'm non-cancellable")
}
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")
}
rare한 케이스, 정지된 코루틴 안에서 코루틴이 실행되는 경우
import kotlinx.coroutines.*
fun main() = runBlocking {
withTimeout(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
}
}
0.5초씩 1000번 박복하는 코드인데 withTimeout을 통해 정지시킨다.(허나 메인 코드여서 그런지 익셒션이 발생한다고 한다.)
import kotlinx.coroutines.*
fun main() = runBlocking {
val result = withTimeoutOrNull(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
"Done" // will get cancelled before it produces this result
}
println("Result is $result")
}
이번에는 시간이 지날 시 null값으로 바뀌고 값이 "Done"으로 바뀌는 코드이다.
if문도 아닌데 사실 왜 "Done"값이 나오는지 잘 모르겠다.
ㄴ찾아보니 whitTimeOrNull이 마지막으로 나온 표현식의 값을 반환한다고 해서 "Done"이 출력되는 듯 하다.(if문 비숫한 것 같기도..?)
import kotlinx.coroutines.*
import kotlin.system.*
fun main() = runBlocking<Unit> {
val time = measureTimeMillis {
val one = doSomethingUsefulOne()
val two = doSomethingUsefulTwo()
println("The answer is ${one + two}")
}
println("Completed in $time ms")
}
suspend fun doSomethingUsefulOne(): Int {
delay(1000L) // pretend we are doing something useful here
return 13
}
suspend fun doSomethingUsefulTwo(): Int {
delay(1000L) // pretend we are doing something useful here, too
return 29
}
순차적으로 돌아가는 코드
import kotlinx.coroutines.*
import kotlin.system.*
fun main() = runBlocking<Unit> {
val time = measureTimeMillis {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
}
suspend fun doSomethingUsefulOne(): Int {
delay(1000L) // pretend we are doing something useful here
return 13
}
suspend fun doSomethingUsefulTwo(): Int {
delay(1000L) // pretend we are doing something useful here, too
return 29
}
순차적으로 ->비동기적으로 실행(실행시간 줄이기),async실행시 코드 실행 시작만 하고 바로 다음줄로 넘어감,await->async 작동될때까지 기다리기
import kotlinx.coroutines.*
import kotlin.system.*
fun main() = runBlocking<Unit> {
val time = measureTimeMillis {
val one = async(start = CoroutineStart.LAZY) { doSomethingUsefulOne() }
val two = async(start = CoroutineStart.LAZY) { doSomethingUsefulTwo() }
// some computation
one.start() // start the first one
two.start() // start the second one
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
}
suspend fun doSomethingUsefulOne(): Int {
delay(1000L) // pretend we are doing something useful here
return 13
}
suspend fun doSomethingUsefulTwo(): Int {
delay(1000L) // pretend we are doing something useful here, too
return 29
}
라인의 start에 LAZY를 걸었을 때 start를 걸어야 작동 시작되도록 코딩,만약 start를 하지 않을 시 await를 만나야 실행이 되서 2초가 걸림
import kotlinx.coroutines.*
import kotlin.system.*
fun main() = runBlocking<Unit> {
val time = measureTimeMillis {
val one = async(start = CoroutineStart.LAZY) { doSomethingUsefulOne() }
val two = async(start = CoroutineStart.LAZY) { doSomethingUsefulTwo() }
// some computation
one.start() // start the first one
two.start() // start the second one
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
}
suspend fun doSomethingUsefulOne(): Int {
delay(1000L) // pretend we are doing something useful here
return 13
}
suspend fun doSomethingUsefulTwo(): Int {
delay(1000L) // pretend we are doing something useful here, too
return 29
}
위 예시는 하지 말라는 예시이다. 위 코드를 실행할 시 큰일이 벌어짐(외부함수여서 익셒션이 발생하여도 중지가 되지 않는다)(더 생각 필요)
import kotlinx.coroutines.*
import kotlin.system.*
fun main() = runBlocking<Unit> {
val time = measureTimeMillis {
println("The answer is ${concurrentSum()}")
}
println("Completed in $time ms")
}
suspend fun concurrentSum(): Int = coroutineScope {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
one.await() + two.await()
}
suspend fun doSomethingUsefulOne(): Int {
delay(1000L) // pretend we are doing something useful here
return 13
}
suspend fun doSomethingUsefulTwo(): Int {
delay(1000L) // pretend we are doing something useful here, too
return 29
}
이 경우는 scope에서 쓰다 보니 익셒션이 발생하면 캔슬된다,스트럭쳐드 컨크러쉬 형태로 만들어서 하는게 좋다
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
try {
failedConcurrentSum()
} catch(e: ArithmeticException) {
println("Computation failed with ArithmeticException")
}
}
suspend fun failedConcurrentSum(): Int = coroutineScope {
val one = async<Int> {
try {
delay(Long.MAX_VALUE) // Emulates very long computation
42
} finally {
println("First child was cancelled")
}
}
val two = async<Int> {
println("Second child throws an exception")
throw ArithmeticException()
}
one.await() + two.await()
}
실행-one,two실행-two에서 출력 후 익셒션 발생-전파-one에서 finally로 연결-출력-전파로 인해 메인에서 catch로 이동-출력
순차적으로 작성했음에도 어떻게 비동기적으로 돌아가는지
ㄴ함수의 끝에 파라미터가 하나 추가되어 컨티뉴에이션이라는 객체를 넘겨주는 형태로 변경
ㄴ코드에서 실행할때 LABEL을 넣어서 중단 및 재생 기준을 만듬
ㄴswtich case문 같은 느낌
ㄴ컨티뉴에이션..?
ㄴ콜백(?)같은 느낌
코드 쓴 후에 Tools - Kotlin - Show Kotlin Bytecode - 디컴파일
코틀린 코드가 분해? 되어 보여짐
switch case문이 보인다.
GlobalScope.launch {
val userData = fetchUserData()
var userCache = cacheUserData(userData)
updateTextView(userCache)
}
ㄴwhen문 안에서 기준이 되는 변수의 값을 늘려가면서 순차적 실행처럼 보임
ㄴ또한 when문이기 떄문에 중단 및 재생이 가능하다
결론:콜백을 내가 하는것이 아닌 코틀린 컴파일러가 하는 것
(이래저래 이해하려면 여러번 봐야할듯 하다)
코루틴이 어떤 스레드에서 실행시킬지 결정해주는 요소
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
launch { // context of the parent, main runBlocking coroutine
println("main runBlocking : I'm working in thread ${Thread.currentThread().name}")
}
launch(Dispatchers.Unconfined) { // not confined -- will work with main thread
println("Unconfined : I'm working in thread ${Thread.currentThread().name}")
}
launch(Dispatchers.Default) { // will get dispatched to DefaultDispatcher
println("Default : I'm working in thread ${Thread.currentThread().name}")
}
launch(newSingleThreadContext("MyOwnThread")) { // will get its own new thread
println("newSingleThreadContext: I'm working in thread ${Thread.currentThread().name}")
}
}
코루틴 4개 실행순서:2-3-4-1
2번:main 스레드에서 실행
3번:worker-1에서 실행
4번:비용이 높은.한번 사용마다 스레드를 만듬,실사용시에는 newSingleThreadContext("MyOwnThread").use로 쓰는게 좋음
1번:runblocking에서 옵셔널 파라미터 없이 실행됨->그럴시 자신을 호출했던 곳(메인)에서 실행
디스페처:어디서 코루틴일 실행될지 정하며 코루틴 콘텍스트에 저장된다
import kotlinx.coroutines.*
fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")
fun main() = runBlocking<Unit> {
val a = async {
log("I'm computing a piece of the answer")
6
}
val b = async {
log("I'm computing another piece of the answer")
7
}
log("The answer is ${a.await() * b.await()}")
}
코틀린 라이브러리는 코루틴을 디버깅하기 위한 도구가 있다.
log라는 함수는 지금 어디서 실행되고 있는지 알려준다.
만약 오느 코루틴에서 실행되고 있는지 볼려면 jvm option에서 설정을 추가한다.
import kotlinx.coroutines.*
fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")
fun main() {
newSingleThreadContext("Ctx1").use { ctx1 ->
newSingleThreadContext("Ctx2").use { ctx2 ->
runBlocking(ctx1) {
log("Started in ctx1")
withContext(ctx2) {
log("Working in ctx2")
}
log("Back to ctx1")
}
}
}
}
withcontext를 통해 스레드간을 점프할 수 있다.
use를 통해 스레드를 클로즈 해줬다.(?)
ㄴ...(공부 필요)
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
println("My job is ${coroutineContext[Job]}")
}
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
// launch a coroutine to process some kind of incoming request
val request = launch {
// it spawns two other jobs
launch(Job()) {
println("job1: I run in my own Job and execute independently!")
delay(1000)
println("job1: I am not affected by cancellation of the request")
}
// and the other inherits the parent context
launch {
delay(100)
println("job2: I am a child of the request coroutine")
delay(1000)
println("job2: I will not execute this line if my parent request is cancelled")
}
}
delay(500)
request.cancel() // cancel processing of the request
println("main: Who has survived request cancellation?")
delay(1000) // delay the main thread for a second to see what happens
}
코루틴이 실행될시 코루틴은 부모의 자식 코루틴이 된다.
996줄의 코루틴은 앞선 launch의 자식이 되는 것
대신 globalscope의 경우는 예외이다.
위 코드의 경우는 globalscope는 예외의 경우라서 끝까지 실행이 되었지만 일반 자식 코루틴의 경우에는
부모가 cancle되어서 자식도 cancle되었다.
부모 코루틴은 모든 자식 코루틴들이 실행될때까지 기다려줌(직접 트랙킹(?) 필요없음)
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
// launch a coroutine to process some kind of incoming request
val request = launch {
repeat(3) { i -> // launch a few children jobs
launch {
delay((i + 1) * 200L) // variable delay 200ms, 400ms, 600ms
println("Coroutine $i is done")
}
}
println("request: I'm done and I don't explicitly join my children that are still active")
}
request.join() // wait for completion of the request, including all its children
println("Now processing of the request is complete")
}
부모 코드가 다 실행되었음을 알린 후에도 자식 코루틴이 끝날떄까지 기다림을 알려주는 코드
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
launch(Dispatchers.Default + CoroutineName("test")) {
println("I'm working in thread ${Thread.currentThread().name}")
}
}
여러개의 코루틴 엘리먼트(?)를 넣고 싶을때는 + 사용하기,(+는 자체적으로 더하는 기능이 있음)
import kotlinx.coroutines.*
class Activity {
private val mainScope = CoroutineScope(Dispatchers.Default) // use Default for test purposes
fun destroy() {
mainScope.cancel()
}
fun doSomething() {
// launch ten coroutines for a demo, each working for a different time
repeat(10) { i ->
mainScope.launch {
delay((i + 1) * 200L) // variable delay 200ms, 400ms, ... etc
println("Coroutine $i is done")
}
}
}
} // class Activity ends
fun main() = runBlocking<Unit> {
val activity = Activity()
activity.doSomething() // run test function
println("Launched coroutines")
delay(500L) // delay for half a second
println("Destroying activity!")
activity.destroy() // cancels all coroutines
delay(1000) // visually confirm that they don't work
}
ㄴ실행(dosomthing)을 하다가 멈추면(destroy) 실행 멈추기(cancle)
안드로이드 앱을 쓰다가 끄면은 작업을 멈추어야 한다. 그것을 위해 코루틴 스코프가 있다.