1年半プロジェクトでKotlinを使ってみて良かったこと、良くなかったこと(2017-01 CA.apk)

Daec7e5cd5fae384eda88037d937343b?s=47 AAkira
January 12, 2017

1年半プロジェクトでKotlinを使ってみて良かったこと、良くなかったこと(2017-01 CA.apk)

Daec7e5cd5fae384eda88037d937343b?s=128

AAkira

January 12, 2017
Tweet

Transcript

  1. 1年半プロジェクトでKotlinを使ってみて 良かったこと、良くなかったこと CA.apk

  2. About me @_a_akira AAkira CyberAgent, Inc. Akira Aratani

  3. About FRESH! • 生放送配信プラットフォーム • FRESH! ≒ AbemaTV

  4. Released map M1 2012-04-12 M11 2015-03-19 M14 2015-10-01 1.0-beta4 2015-12-22

    M13 2015-09-16 1.0 2016-02-16 1.0-RC 2016-02-04 2016-01-21 Release 2015-04 開発開始 kotlin FRESH
  5. 良かったこと

  6. コード量が減る

  7. コード量が減る for (int i = 0; i < 10; i++)

    { Log.v("tag", "i=" + i); } int[] array = {0, 10, 20, 30, 40, 50, 60}; for (int item : array) { Log.v("tag", "item=" + item); } for (int i = 0; i < array.length; i++) { Log.v("tag", "item[" + i + "]=" + array[i]); }
  8. コード量が減る convert to Kotlin for (i in 0..9) { Log.v("tag",

    "i=" + i) } val array = intArrayOf(0, 10, 20, 30, 40, 50, 60) array.forEach { Log.v("tag", "item=" + it) } array.forEachIndexed { index, item -> Log.v("tag", "item[$index]=$item") }
  9. コード量が減る Everything is stream https://speakerdeck.com/aakira/flux-with-kotlin-abema-dev-con-2016

  10. コード量が減る Observable.from(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8,

    9, 10}) .filter(new Func1<Integer, Boolean>() { @Override public Boolean call(Integer i) { return (i % 2) == 0; } }) .map(new Func1<Integer, Integer>() { @Override public Integer call(Integer i) { return i * 10; } }) .subscribe(new Observer<Integer>() { @Override public void onNext(Integer integer) { Log.d("TAG", integer.toString()); } @Override public void onCompleted() { } @Override public void onError(Throwable e) { } });
  11. コード量が減る Observable.from(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8,

    9, 10}) .filter(new Func1<Integer, Boolean>() { @Override public Boolean call(Integer i) { return (i % 2) == 0; } }) .map(new Func1<Integer, Integer>() { @Override public Integer call(Integer i) { return i * 10; } }) .subscribe(new Observer<Integer>() { @Override public void onNext(Integer integer) { Log.d("TAG", integer.toString()); } @Override public void onCompleted() { } @Override public void onError(Throwable e) { } }); 20, 40, 60, 80, 100
  12. コード量が減る convert to Kotlin Observable.from(arrayOf(1, 2, 3, 4, 5, 6,

    7, 8, 9, 10)) .filter { i -> i % 2 == 0 } .map { i -> i * 10 } .subscribe(object : Observer<Int> { override fun onNext(integer: Int?) { Log.d("TAG", integer.toString()) } override fun onCompleted() {} override fun onError(e: Throwable) {} })
  13. コード量が減る Observable.from(arrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9,

    10)) .filter { it % 2 == 0 } .map { it * 10 } .subscribe(object : Observer<Int> { override fun onNext(integer: Int?) { Log.d("TAG", integer.toString()) } override fun onCompleted() {} override fun onError(e: Throwable) {} })
  14. コード量が減る Observable.from(arrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9,

    10)) .filter { it % 2 == 0 } .map { it * 10 } .subscribe({ Log.d("TAG", it.toString()) }, { // onError }, { // onCompleted })
  15. 拡張関数便利

  16. 拡張関数便利 forEachの正体もこれ /** * Performs the given [action] on each

    element, providing sequential index with the element. * @param [action] function that takes the index of an element and the element itself * and performs the desired action on the element. */ public inline fun <T> Iterable<T>.forEachIndexed(action: (Int, T) -> Unit): Unit { var index = 0 for (item in this) action(index++, item) } /** * Performs the given [action] on each element. */ @kotlin.internal.HidesMembers public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit { for (element in this) action(element) }
  17. 拡張関数便利 • run • let • apply • (with) Standard.kt

  18. 拡張関数便利 run, let, apply, withの使用回数

  19. 拡張関数便利 • run • let • apply • (with) 146

    箇所
  20. 拡張関数便利 • run • let • apply • (with) 146

    箇所 442 箇所
  21. 拡張関数便利 • run • let • apply • (with) 146

    箇所 442 箇所 293 箇所
  22. 拡張関数便利 • run • let • apply • (with) 146

    箇所 442 箇所 293 箇所 0 箇所
  23. 拡張関数便利 FRESH!内で定義されている 拡張関数の数

  24. 拡張関数便利 58 個 fun\s[A-z]*\.[a-z]*\(.*\)\s\{

  25. Swiftなんとなくわかる

  26. Swiftなんとなくわかる iOSの方が優先されがち → 先に実装終わってる この仕様どうしよう?

  27. Swiftなんとなくわかる iOSのコードパクろう

  28. コードの意図が伝わる

  29. コードの意図が伝わる 師匠 < コードは小説だ

  30. コードの意図が伝わる val hoge = 10 var foo = 100 val

    bar: String if (hoge > 5) { foo = 200 bar = “fresh!" } else { foo = 300 bar = "abema" } Log.v("TAG", "foo=$foo, bar=$bar")
  31. コードの意図が伝わる val hoge = 10 var foo = 100 val

    bar: String if (hoge > 5) { foo = 200 bar = “fresh!" } else { foo = 300 bar = "abema" } Log.v("TAG", "foo=$foo, bar=$bar") 定数が一目瞭然
  32. コードの意図が伝わる class HogeActivity : Activity { companion object { private

    const val EXTRA_HOGE = "extra_hoge" private const val EXTRA_FOO = "extra_foo" private const val EXTRA_BAR = "extra_bar" fun createIntent(activity: Activity, hoge: Int) = Intent(activity, HogeActivity::class.java).apply { putExtra(EXTRA_HOGE, hoge) } fun createIntent(activity: Activity, foo: String, bar: String? = null) = Intent(activity, HogeActivity::class.java).apply { putExtra(EXTRA_FOO, foo) putExtra(EXTRA_BAR, bar) } } …… }
  33. コードの意図が伝わる class HogeActivity : Activity { companion object { private

    const val EXTRA_HOGE = "extra_hoge" private const val EXTRA_FOO = "extra_foo" private const val EXTRA_BAR = "extra_bar" fun createIntent(activity: Activity, hoge: Int) = Intent(activity, HogeActivity::class.java).apply { putExtra(EXTRA_HOGE, hoge) } fun createIntent(activity: Activity, foo: String, bar: String? = null) = Intent(activity, HogeActivity::class.java).apply { putExtra(EXTRA_FOO, foo) putExtra(EXTRA_BAR, bar) } } …… } nullに意味がある
  34. アプリがcrashしない

  35. アプリがcrashしない

  36. アプリがcrashしない Androidのcrash ranking https://www.apteligent.com/developer-resources/top-5-crashes-on-android/ 1. java.lang.NullPointerException 2. java.lang.OutOfMemoryError 3. android.view.WindowManager.BadTokenException

    4. java.lang.IllegalException 5. android.database.sqlite.SQLiteException
  37. アプリがcrashしない Androidのcrash ranking https://www.apteligent.com/developer-resources/top-5-crashes-on-android/ 1. java.lang.NullPointerException 2. java.lang.OutOfMemoryError 3. android.view.WindowManager.BadTokenException

    4. java.lang.IllegalException 5. android.database.sqlite.SQLiteException
  38. アプリがcrashしない var hoge: String? = “hoge" // ΋͠hoge͕nullͩͬͨΒ0 if (hoge?.length

    ?: 0 > 0) hoge = "replace" // ΋͠hoge͕nullͩͬͨΒॳظԽ hoge = hoge ?: "init"
  39. アプリがcrashしない class Hoge { var onHogeChangedListener: (() -> Unit)? =

    null fun doSomething() { onHogeChangedListener?.invoke() } } class HogeActivity: Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val hoge = Hoge() hoge.onHogeChangedListener = { } } }
  40. 良くなかったこと

  41. ない

  42. 強いて言うなら…

  43. メソッド数が微妙に増える

  44. メソッド数が微妙に増える 5142 kotlin
 2 kotlin._Assertions
 12 kotlin.annotation
 2519 kotlin.collections
 65

    kotlin.comparisons
 31 kotlin.concurrent
 6 kotlin.internal
 296 kotlin.io
 549 kotlin.jvm
 24 kotlin.jvm.functions
 506 kotlin.jvm.internal
 2 kotlin.jvm.internal.unsafe
 23 kotlin.properties
 227 kotlin.ranges
 61 kotlin.reflect
 423 kotlin.sequences
 3 kotlin.system
 762 kotlin.text https://github.com/KeepSafe/dexcount-gradle-plugin Kotlin version : 1.06
  45. 動作が不安定(だった)

  46. 動作が不安定(だった) Kotlin 86.5% Java 13.4% Other 0.1%

  47. 人材の流動性

  48. 人材の流動性 < Kotlinだからなぁ〜 ɹ FRESHに来てください! (実話)

  49. 人材の流動性 < Welcome!! ɹ Kotlin書きたい! (実話)

  50. まとめ

  51. まとめ メリットの方が多いので Kotlinで開発した方が良い

  52. でしょうね

  53. 何が言いたいか

  54. まとめ Kotlin

  55. まとめ Kotlin RxJava

  56. まとめ Dagger Kotlin RxJava

  57. まとめ 好きなLibraryなんでも Dagger Kotlin RxJava

  58. まとめ Androidエンジニアを募集しております &

  59. None
  60. まだ時間ある…?

  61. 時間あったらおまけ

  62. よく使ってる拡張関数

  63. Animator Listener fun Animator.setListener( onAnimationStart: (Animator) -> Unit = {},

    onAnimationEnd: (Animator) -> Unit = {}, onAnimationCancel: (Animator) -> Unit = {}, onAnimationRepeat: (Animator) -> Unit = {} ) = apply { addListener(object : Animator.AnimatorListener { override fun onAnimationStart(animation: Animator) { onAnimationStart(animation) } override fun onAnimationEnd(animation: Animator) { onAnimationEnd(animation) } override fun onAnimationCancel(animation: Animator) { onAnimationCancel(animation) } override fun onAnimationRepeat(animation: Animator) { onAnimationRepeat(animation) } }) }
  64. Animator Listener ObjectAnimator.ofFloat(this, "alpha", 1.0, 0.0) .setDuration(300) .setListener(onAnimationStart = {

    backgroundView.toVisible() }) .start()
  65. Visibility fun View.toVisible() { visibility = View.VISIBLE } fun View.toInvisible()

    { visibility = View.INVISIBLE } fun View.toGone() { visibility = View.GONE }
  66. Visibility view.toGone()

  67. Apply margin fun View.applyMargin(left: Int = 0, top: Int =

    0, right: Int = 0, bottom: Int = 0) = (layoutParams as ViewGroup.MarginLayoutParams).apply { leftMargin = left topMargin = top rightMargin = right bottomMargin = bottom }
  68. Apply margin view.applyMargin(top = 16, right = 48)