Kotlin で 非同期処理 Coroutine #1 ~ launch(), async()

ネット上を調べてみてもよくわかりません。

難しい言葉や experimental な仕様の変更などあったりして。

少しづつ試してみながらマスターしていきましょう。

// build.gradle

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.22.2"

//gradle.properties

kotlin.coroutines=enable

kotlinx.coroutines/coroutines-guide-ui.md at master · Kotlin/kotlinx.coroutines

まず、これ。


for (i in 1..10) {
  Timber.d("$i")
  Thread.sleep(1000)
}

非同期にしたいですよね。
launch から始めます。


launch { // @
  for (i in 1..10) {
    Timber.d("$i")
    Thread.sleep(1000)
  }
}


launch {
  for (i in 1..10) {
    Timber.d("$i")
    delay(1000)  // @
  }
}

引数をつけて渡す。

UI :
UIスレッドで実行。

CommonPool :
バックグランドスレッドで実行。


launch(UI) {  // @
  for (i in 1..10) {
    Timber.d("$i")
    delay(1000)
  }
}


launch(CommonPool) {  // @
  for (i in 1..10) {
    Timber.d("$i")
    delay(1000)
  }
}


launch(UI + CommonPool) {  // @
  for (i in 1..10) {
    Timber.d("$i")
    delay(1000)
  }
}

launch() の戻りからキャンセルできます。


val job = launch(UI) {  // @
  for (i in 1..10) {
    Timber.d("$i")
    delay(1000)
  }
}

fab.setOnClickListener {
  job.cancel() // @
}

 

まとめ

UIスレッドに限定されたコルーチンは、UIスレッドをブロックすることなく、UI内の何かを自由に更新して中断することができます。

delay が待っている間UIスレッドはブロックされないのでUIはフリーズしません。ただ単にコルーチンを中断します。

Job.cancelは完全にスレッドセーフでノンブロッキングです。
実際に終了するのを待つことなく、コルーチンがそのジョブをキャンセルするように通知するだけです。 どこからでも呼び出すことができます。

基本的な非同期呼び出しは、launch() と async() の2つ。似ているが戻りが異なる。


E を出力させない Double.toPlainString()

こういうの。助かります。


double d1 = 0.00000000000000000000000000000000000000000123456789012345678901234567890d;
double d2 = 1234567890123456789012345678900000000000000000000000000000000000000000000d;
double d3 = 1234567890.1234567890123456789d;

System.out.println(BigDecimal.valueOf(d1).toPlainString());
// 0.0000000000000000000000000000000000000000012345678901234568
System.out.println(BigDecimal.valueOf(d2).toPlainString());
// 1234567890123456800000000000000000000000000000000000000000000000000000000
System.out.println(BigDecimal.valueOf(d3).toPlainString());
// 1234567890.1234567

Java の double を素直な String にしたい、E 要らない - Qiita

直感的には表示できなく、ネット上を放浪していました。

Kotlin では、Extention Functions に追加しておきますわ。


fun Double.toPlainString(): String
    = BigDecimal.valueOf(this).toPlainString()


val d1 = 0.00000000000000000000000000000000000000000123456789012345678901234567890
val d2 = 1234567890123456789012345678900000000000000000000000000000000000000000000.0
val d3 = 1234567890.1234567890123456789
val d4 = 4.34e-05
val d5 = 5.6e-07

println(d1.toPlainString())
println(d2.toPlainString())
println(d3.toPlainString())
println(d4.toPlainString())
println(d5.toPlainString())


I/System.out: 0.0000000000000000000000000000000000000000012345678901234568
I/System.out: 1234567890123456800000000000000000000000000000000000000000000000000000000
I/System.out: 1234567890.1234567
I/System.out: 0.0000434
I/System.out: 0.00000056

Java Double に toPlainString() がないのは何か理由があるのかも。とは思うが。


【Kotlin】sortedWith + compareBy で並び替え

「C# でこんなの書けるけど Kotlin ではどう書くの?」 という話。


var list = new List<Person>();
list.Add(new Person(25, "Tom"));
list.Add(new Person(25, "Dave"));
list.Add(new Person(20, "Kate"));
list.Add(new Person(20, "Alice"));

// will produce: Alice, Kate, Dave, Tom
var sortedList = list
    .OrderBy(person => person.Age)
    .ThenBy(person => person.Name)
    .ToList();

Javaのコードから考えるとKotlinでももちろんシンプルな記述となります。


val sortedList = list
    .sortedWith(compareBy({ it.age }, { it.name }))


val sortedList = list
    .sortedWith(compareBy(Person::age, Person::name))


list.sortedWith(compareBy<Person> { it.age }.thenBy { it.name }.thenBy { it.address })


list.sortedWith(compareBy({ it.age }, { it.name }, { it.address }))

java - Sort collection by multiple fields in Kotlin - Stack Overflow

大文字小文字を無視する場合。


places
    .sortedWith(
        compareBy(String.CASE_INSENSITIVE_ORDER, { it.name })
     )


places
    .sortedWith(
        compareBy(String.CASE_INSENSITIVE_ORDER) { it.name }
     )


places
    .sortedWith(
        compareBy(String.CASE_INSENSITIVE_ORDER, Place::name)
     )

kotlin - How to sort objects list in case insensitive order? - Stack Overflow

降順昇順の対応やモデルに入れたり。


val sortedList = list
    .sortedWith(compareBy({ it.a }, { it.b }, { it.c }))


list.sortedWith(
    compareBy<Foo> { it.a }
        .thenByDescending { it.b }
        .thenBy { it.c }
)


class Foo(val a: String, val b: Int, val c: Date) : Comparable<Foo> {

  override fun compareTo(other: Foo)
      = compareValuesBy(this, other, { it.a }, { it.b }, { it.c })
}
val sortedList = list.sorted()


class Foo(val a: String, val b: Int, val c: Date) : Comparable<Foo> {

  override fun compareTo(other: Foo) = comparator.compare(this, other)

  companion object {
    val comparator = compareBy(Foo::a, Foo::b, Foo::c)
  }
}
val sortedList = list.sorted()

comparable - How to sort based on/compare multiple values in Kotlin? - Stack Overflow

シンプルで便利になりつつも複数の記述で書くことができてしまう最近の言語のパターン。