Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

About me @_a_akira AAkira CyberAgent, Inc. Akira Aratani

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

良かったこと

Slide 6

Slide 6 text

コード量が減る

Slide 7

Slide 7 text

コード量が減る 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]); }

Slide 8

Slide 8 text

コード量が減る 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") }

Slide 9

Slide 9 text

コード量が減る Everything is stream https://speakerdeck.com/aakira/flux-with-kotlin-abema-dev-con-2016

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

コード量が減る Observable.from(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) .filter(new Func1() { @Override public Boolean call(Integer i) { return (i % 2) == 0; } }) .map(new Func1() { @Override public Integer call(Integer i) { return i * 10; } }) .subscribe(new Observer() { @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

Slide 12

Slide 12 text

コード量が減る 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 { override fun onNext(integer: Int?) { Log.d("TAG", integer.toString()) } override fun onCompleted() {} override fun onError(e: Throwable) {} })

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

コード量が減る 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 })

Slide 15

Slide 15 text

拡張関数便利

Slide 16

Slide 16 text

拡張関数便利 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 Iterable.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 Iterable.forEach(action: (T) -> Unit): Unit { for (element in this) action(element) }

Slide 17

Slide 17 text

拡張関数便利 • run • let • apply • (with) Standard.kt

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

拡張関数便利 FRESH!内で定義されている 拡張関数の数

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

Swiftなんとなくわかる

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

コードの意図が伝わる

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

コードの意図が伝わる 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")

Slide 31

Slide 31 text

コードの意図が伝わる 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") 定数が一目瞭然

Slide 32

Slide 32 text

コードの意図が伝わる 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) } } …… }

Slide 33

Slide 33 text

コードの意図が伝わる 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に意味がある

Slide 34

Slide 34 text

アプリがcrashしない

Slide 35

Slide 35 text

アプリがcrashしない

Slide 36

Slide 36 text

アプリが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

Slide 37

Slide 37 text

アプリが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

Slide 38

Slide 38 text

アプリがcrashしない var hoge: String? = “hoge" // ΋͠hoge͕nullͩͬͨΒ0 if (hoge?.length ?: 0 > 0) hoge = "replace" // ΋͠hoge͕nullͩͬͨΒॳظԽ hoge = hoge ?: "init"

Slide 39

Slide 39 text

アプリが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 = { } } }

Slide 40

Slide 40 text

良くなかったこと

Slide 41

Slide 41 text

ない

Slide 42

Slide 42 text

強いて言うなら…

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

メソッド数が微妙に増える 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

Slide 45

Slide 45 text

動作が不安定(だった)

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

人材の流動性

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

まとめ

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

でしょうね

Slide 53

Slide 53 text

何が言いたいか

Slide 54

Slide 54 text

まとめ Kotlin

Slide 55

Slide 55 text

まとめ Kotlin RxJava

Slide 56

Slide 56 text

まとめ Dagger Kotlin RxJava

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

No content

Slide 60

Slide 60 text

まだ時間ある…?

Slide 61

Slide 61 text

時間あったらおまけ

Slide 62

Slide 62 text

よく使ってる拡張関数

Slide 63

Slide 63 text

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) } }) }

Slide 64

Slide 64 text

Animator Listener ObjectAnimator.ofFloat(this, "alpha", 1.0, 0.0) .setDuration(300) .setListener(onAnimationStart = { backgroundView.toVisible() }) .start()

Slide 65

Slide 65 text

Visibility fun View.toVisible() { visibility = View.VISIBLE } fun View.toInvisible() { visibility = View.INVISIBLE } fun View.toGone() { visibility = View.GONE }

Slide 66

Slide 66 text

Visibility view.toGone()

Slide 67

Slide 67 text

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 }

Slide 68

Slide 68 text

Apply margin view.applyMargin(top = 16, right = 48)