$30 off During Our Annual Pro Sale. View Details »

Android開発者のための Kotlin Multiplatform入門

Android開発者のための Kotlin Multiplatform入門

Taro Nagasawa

July 02, 2024
Tweet

More Decks by Taro Nagasawa

Other Decks in Programming

Transcript

  1. Button(onClick = { showContent = !showContent }) { Text("Click me!")

    } AnimatedVisibility(showContent) { val greeting = remember { Greeting().greet() } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Image(painterResource(Res.drawable.compose_multiplatform), null) Text("Compose: $greeting") } } Button(onClick = { showContent = !showContent }) { Text("Click me!") } AnimatedVisibility(showContent, enter = scaleIn(), exit = scaleOut()) { val greeting = remember { Greeting().greet() } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Image(painterResource(Res.drawable.compose_multiplatform), null) Text("Compose: $greeting") } }
  2. 1. Kotlinの復習  ・Kotlinの登場  ・Android開発における採用  ・Kotlinの便利なところ 2. AndroidのGUIプログラミングの歴史  ・初期  ・MVP  ・Flux

     ・MVVM  ・Jetpack Compose 3. 最新のKotlin事情  ・Kotlin 2.0  ・Kotlin Multiplatform  ・Compose Multiplatform  ・コードを交えた実装例    ・Fleet    ・Android, iOS
  3. 1. Kotlinの復習  ・Kotlinの登場  ・Android開発における採用  ・Kotlinの便利なところ 2. AndroidのGUIプログラミングの歴史  ・初期  ・MVP  ・Flux

     ・MVVM  ・Jetpack Compose 3. 最新のKotlin事情  ・Kotlin 2.0  ・Kotlin Multiplatform  ・Compose Multiplatform  ・コードを交えた実装例    ・Fleet    ・Android, iOS
  4. Kotlinの登場 • 2011年7月 JVM Language Summitにて、開発中であることが発表された • IntelliJ IDEAなどの統合開発環境を開発・提供するJetBrains社 •

    当時のJava言語の抱える課題に対して、Better Javaとして登場 ◦ Java仮想マシン(Java Virtual Machine)の上で走る言語 • JVMだけでなく、JavaScriptへのトランスパイルも視野に入れていた • 2016年にバージョン1.0がリリース
  5. Android開発における採用 • KotlinはAndroid開発には最適な言語だった • 当時のAndroid Javaは、Java SE 7に相当する言語機能だった ◦ コレクション操作やOptional型:

    代わりに、GuavaやLightweight Stream APIを使った ◦ ラムダ式: 代わりに、Retrolambdaを使った List<String> nameLengths = new ArrayList<>(); for (String name: names) { nameLengths.add(name.length()); } List<String> nameLengths = Stream.of(names) .map(name -> name.length()) .toList();
  6. Android開発における採用 • Kotlinは、上記の問題をすべてクリアしていた • 2017年、Androidの開発言語としてKotlinが正式サポートされた • KotlinはAndroid開発には最適な言語だった • 当時のAndroid Javaは、Java

    SE 7に相当する言語機能だった ◦ コレクション操作やOptional型: 代わりに、GuavaやLightweight Stream APIを使った ◦ ラムダ式: 代わりに、Retrolambdaを使った
  7. ラムダ式とコレクション操作 Project Euler Problem 1「1000未満の3か5の倍数となる数の合計」 var sum: Int = 0

    for (var i: Int = 0; i < 1000; i++) { if (i % 3 != 0) continue if (i % 5 != 0) continue sum += i } println(sum) val sum: Int = (0 until 1000) .filter { it % 3 == 0 || it % 5 == 0 } .sum() println(sum) ※擬似コードです Kotlinでは、このようなforループは提供されていません ラムダ式
  8. 静的型付けと型推論 • 静的型付け: コンパイル時に、データ型の整合性を確認する • 型推論: 文脈から式の型を推論することで、型の明示的な記述を省略できる val list: MutableList<Int>

    = mutableListOf<Int>(1, 2) list.add(3) list.add("4") // 型の不一致によるコンパイルエラー val list = mutableListOf(1, 2) list.add(3) list.add("4") // 型の不一致によるコンパイルエラー
  9. null安全 • 式がnullである可能性が、あるかないかを厳格に区別する • 式がnullである可能性がある場合、メンバーの呼び出しが制限される val name: String = "Kotlin"

    println(name.length) val color: String? = "Red" println(color.length) // コンパイルエラー if (color != null) { println(color.length) // OK } println(color?.length) // OK
  10. 拡張関数 • 型を変更せずに、メソッドを追加しているように見せる機能 fun hello(name: String): String { return "Hello,

    $name!" } fun main() { val message = hello("World") println(message) // Hello, World! } fun String.hello(): String { return "Hello, $this!" } fun main() { val message = "World".hello() println(message) // Hello, World! }
  11. プロパティ • オブジェクトのデータを表現するインタフェース • つまり、データの実体がメモリに格納されているなどの詳細は不問 • デフォルトでは、データの実体はメモリに格納される class Name(val value:

    String) { val length: Int = value.length } fun main() { val name = Name("Kotlin") val len = name.length println(len) } class Name(val value: String) { val length: Int get() { return value.length() } }
  12. 1. Kotlinの復習  ・Kotlinの登場  ・Android開発における採用  ・Kotlinの便利なところ 2. AndroidのGUIプログラミングの歴史  ・初期  ・MVP  ・Flux

     ・MVVM  ・Jetpack Compose 3. 最新のKotlin事情  ・Kotlin 2.0  ・Kotlin Multiplatform  ・Compose Multiplatform  ・コードを交えた実装例    ・Fleet    ・Android, iOS
  13. 1. Kotlinの復習  ・Kotlinの登場  ・Android開発における採用  ・Kotlinの便利なところ 2. AndroidのGUIプログラミングの歴史  ・初期  ・MVP  ・Flux

     ・MVVM  ・Jetpack Compose 3. 最新のKotlin事情  ・Kotlin 2.0  ・Kotlin Multiplatform  ・Compose Multiplatform  ・コードを交えた実装例    ・Fleet    ・Android, iOS
  14. 初期 class MainActivity: Activity() { override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) setContentView(R.layout.main) val textView = findViewById(R.id.text_view) as? TextView val button = findViewById(R.id.button) as? Button button?.setOnClickListener { textView?.setText("Clicked") } } }
  15. MVP (Model, View, Presenter) Activityとか <<interface>> View <<interface>> Presenter <<interface>>

    Model PresenterImpl ModelImpl • Model = 業務ロジック, View = 表示, Presenter = ModelとViewの紐付け • 結合が疎になるので、テスタビリティ、メンテナビリティが向上 矢印の先に、依存している(呼び出している) 矢印の先を実装している
  16. MVVM (Model, View, ViewModel) • ViewModel = UIロジック、Model呼び出し、Viewへの更新通知 • Android

    Architecture Componentsにより実装が容易 View ViewModel Model データの流れ DataBinding / LiveData
  17. Jetpack Compose • Kotlinによる宣言型UIツールキット • 考え方: 現在の状態から、現在のUIへと変換をする @Composable fun Greeting(name:

    String) { Text(text = "Hello $name!") } @Composable fun GreetingList(names: List<String>, modifier: Modifier) { Column(modifier = modifier) { names.forEach { name -> Greeting(name = name) } } }
  18. 1. Kotlinの復習  ・Kotlinの登場  ・Android開発における採用  ・Kotlinの便利なところ 2. AndroidのGUIプログラミングの歴史  ・初期  ・MVP  ・Flux

     ・MVVM  ・Jetpack Compose 3. 最新のKotlin事情  ・Kotlin 2.0  ・Kotlin Multiplatform  ・Compose Multiplatform  ・コードを交えた実装例    ・Fleet    ・Android, iOS
  19. 1. Kotlinの復習  ・Kotlinの登場  ・Android開発における採用  ・Kotlinの便利なところ 2. AndroidのGUIプログラミングの歴史  ・初期  ・MVP  ・Flux

     ・MVVM  ・Jetpack Compose 3. 最新のKotlin事情  ・Kotlin 2.0  ・Kotlin Multiplatform  ・Compose Multiplatform  ・コードを交えた実装例    ・Fleet    ・Android, iOS
  20. Kotlin 2.0 • K2コンパイラが安定版に。従来の2倍の速度 • 文法の変更は、なし • バージョン2.1以降で、文法の変更・言語機能の追加が予定されている ◦ コンテキストパラメータ

    ◦ when内のガード条件 ◦ インライン関数の中でのcontinue, breakをサポート ◦ 文字列テンプレートにおける $ のエスケープ
  21. Kotlin Multiplatform (KMP) • Kotlinコードが、複数のプラットフォーム向けの実行コードになる • 理想: 1つのKotlinコードが、どこでも動く • 現実

    ◦ ロジックなど、プラットフォームに依存しないコードは、各プラットフォームで共有可能 ◦ UIなど、プラットフォームに依存するコードは、プラットフォームごとに記述する • expect / actual
  22. Kotlin Multiplatform (KMP) expect fun getName(): String fun greet(): String

    = "Hello, ${getName()}!" actual fun getName(): String = "JVM" fun main() { println(greet()) } actual fun getName(): String = "Android" class MainActivity: Activity() { override fun onCreate(state: Bundle?) { super.onCreate(state) Log.i("Greet", greet()) } } 共有コード commonMain JVM固有コード jvmMain Android固有コード androidMain
  23. Compose Multiplatform (CMP) • Jetpack Composeベースの宣言型UI構築フレームワーク • 対応プラットフォーム ◦ Android

    ◦ デスクトップ(Windows, MacOS, Linux -> JVM) ◦ iOS(ベータ版) ◦ Wasm(アルファ版)
  24. commonMain/kotlin/App.kt @Composable @Preview fun App() { MaterialTheme { var showContent

    by remember { mutableStateOf(false) } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Button(onClick = { showContent = !showContent }) { Text("Click me!") } AnimatedVisibility(showContent) { val greeting = remember { Greeting().greet() } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Image(painterResource(Res.drawable.compose_multiplatform), null) Text("Compose: $greeting") } } } } }
  25. commonMain/kotlin/App.kt @Composable @Preview fun App() { MaterialTheme { var showContent

    by remember { mutableStateOf(false) } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Button(onClick = { showContent = !showContent }) { Text("Click me!") } AnimatedVisibility(showContent) { val greeting = remember { Greeting().greet() } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Image(painterResource(Res.drawable.compose_multiplatform), null) Text("Compose: $greeting") } } } } }
  26. commonMain/kotlin/App.kt @Composable @Preview fun App() { MaterialTheme { var showContent

    by remember { mutableStateOf(false) } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Button(onClick = { showContent = !showContent }) { Text("Click me!") } AnimatedVisibility(showContent) { val greeting = remember { Greeting().greet() } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Image(painterResource(Res.drawable.compose_multiplatform), null) Text("Compose: $greeting") } } } } }
  27. commonMain/kotlin/App.kt @Composable @Preview fun App() { MaterialTheme { var showContent

    by remember { mutableStateOf(false) } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Button(onClick = { showContent = !showContent }) { Text("Click me!") } AnimatedVisibility(showContent) { val greeting = remember { Greeting().greet() } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Image(painterResource(Res.drawable.compose_multiplatform), null) Text("Compose: $greeting") } } } } }
  28. commonMain/kotlin/App.kt @Composable @Preview fun App() { MaterialTheme { var showContent

    by remember { mutableStateOf(false) } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Button(onClick = { showContent = !showContent }) { Text("Click me!") } AnimatedVisibility(showContent) { val greeting = remember { Greeting().greet() } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Image(painterResource(Res.drawable.compose_multiplatform), null) Text("Compose: $greeting") } } } } }
  29. commonMain/kotlin/App.kt @Composable @Preview fun App() { MaterialTheme { var showContent

    by remember { mutableStateOf(false) } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Button(onClick = { showContent = !showContent }) { Text("Click me!") } AnimatedVisibility(showContent) { val greeting = remember { Greeting().greet() } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Image(painterResource(Res.drawable.compose_multiplatform), null) Text("Compose: $greeting") } } } } }
  30. iosMain/kotlin/MainViewController.kt fun MainViewController() = ComposeUIViewController { App() } 上記コードは、Composeとの繋ぎ込みに過ぎず、iOSアプリそのものはSwiftで記述されている //

    <project_root>/iosApp/iosApp/ContentView.swift struct ComposeView: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIViewController { MainViewControllerKt.MainViewController() } func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} } struct ContentView: View { var body: some View { ComposeView().ignoresSafeArea(.keyboard) } }
  31. 共通コード class Greeting { private val platform = getPlatform() fun

    greet(): String { return "Hello, ${platform.name}!" } } interface Platform { val name: String } expect fun getPlatform(): Platform commonMain/kotlin/Greeting.kt commonMain/kotlin/Platform.kt
  32. プラットフォーム固有のコード class AndroidPlatform : Platform { override val name: String

    = "Android ${Build.VERSION.SDK_INT}" } actual fun getPlatform(): Platform = AndroidPlatform() androidMain/kotlin/Platform.andoird.kt class IOSPlatform: Platform { override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion } actual fun getPlatform(): Platform = IOSPlatform() iosMain/kotlin/Platform.ios.kt
  33. まとめ • Android開発は今やKotlinで記述する利点が大きい • その恩恵を特に受けているのが Jetpack Compose • KMPによって、KotlinコードだけでAndroid, iOS,

    サーバーサイド, その他で 走るアプリを作れるし、コードの共有・分離が適切に行える • CMPによって、Android, iOS向けのUIを同時に構築できる ご清聴ありがとうございました 長澤 太郎 @ngsw_taro