Slide 1

Slide 1 text

<< DroidKaigi 2022 カンファレンスアプリを 眺める DroidKaigi.collect{ #1@Tokyo }

Slide 2

Slide 2 text

● 株式会社ゆめみ(Androidエンジニア) ● Twitter: @mono33__ みかみ

Slide 3

Slide 3 text

カンファレンスアプリを眺め 気になるトピックを2つ深堀

Slide 4

Slide 4 text

アジェンダ 1. カンファレンスアプリについて 2. カンファレンスアプリを眺める 3. 感想

Slide 5

Slide 5 text

アジェンダ 1. カンファレンスアプリについて 2. カンファレンスアプリを眺める 3. 感想

Slide 6

Slide 6 text

カンファレンスアプリの概要 ● イベント当日に重宝するアプリ ○ セッションのスケジュール確認や管理、 会場マップの表示などの機能 ● 毎年多くのエンジニアによってOSS開発 ○ 今年は約150名ほど

Slide 7

Slide 7 text

カンファレンスアプリの技術特徴 ● モダンな技術要素 ○ Jetpack ComposeやFlow、Compose Material 3 ● 挑戦的な要素 ○ Kotlin Multiplatform[1] ■ iOSまでもリリース [1] Kotlin Multiplatform, https://kotlinlang.org/docs/multiplatform.html

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

アジェンダ 1. カンファレンスアプリについて 2. カンファレンスアプリを眺める 3. 感想

Slide 10

Slide 10 text

ビルドロジック

Slide 11

Slide 11 text

[2] Tech Stacks, https://github.com/DroidKaigi/conference-app-2022

Slide 12

Slide 12 text

モジュール構成 Now in Android をベースにした マルチモジュール構成 app, feature, core が主 benchmarkやpreview-screenshots iOS関連のものも

Slide 13

Slide 13 text

モジュール構成 Now in Android をベースにした マルチモジュール構成 app, feature, core が主 benchmarkやpreview-screenshots, iOS関連のものも

Slide 14

Slide 14 text

増えるボイラープレート スケーラビリティや再利用性の点でメリットがあるが、 モジュールが多いとGradleの記述・管理が大変に モジュール数が 多いほど記述が 増える

Slide 15

Slide 15 text

マルチモジュールの注意点 スケーラビリティや再利用性の点でメリットがあるが、 モジュールが多いとGradleの記述が大変に モジュール数が 多いほど記述が 増える 効率的にビルドスクリプトを 管理したい🤔

Slide 16

Slide 16 text

ビルドスクリプトの一元化 ● ビルドスクリプトを一元化してボイラープレートを 削減 ● buildSrcはいくつかの問題点[3] ○ ビルド毎に不必要なタスクが実行されてしまう ○ buildSrcに変更があった場合再構築される [3] Herding Elephants, https://developer.squareup.com/blog/herding-elephants/

Slide 17

Slide 17 text

build-logic の登場 ● Version Catalog[4]とComposite Build[5]の組み合わせ build-logicを作る ● Now in Android[6] でも採用 [4] Sharing dependency versions between projects, https://docs.gradle.org/current/userguide/platforms.html [5] Composing builds, https://docs.gradle.org/current/userguide/composite_builds.html [6] Now in Android, https://github.com/android/nowinandroid

Slide 18

Slide 18 text

[7] https://github.com/DroidKaigi/conference-app-2022/tree/main/gradle

Slide 19

Slide 19 text

@Suppress("unused") class AndroidPlugin : Plugin { override fun apply(target: Project) { with(target) { with(pluginManager) { apply("com.android.library") } androidLibrary { setupAndroid() } } } } primitive @Suppress("unused") class AndroidKotlinPlugin : Plugin { override fun apply(target: Project) { with(target) { with(pluginManager) { apply("org.jetbrains.kotlin.android") } android { /* ... */} } } } }

Slide 20

Slide 20 text

@Suppress("unused") class AndroidPlugin : Plugin { override fun apply(target: Project) { with(target) { with(pluginManager) { apply("com.android.library") } androidLibrary { setupAndroid() } } } } primitive @Suppress("unused") class AndroidKotlinPlugin : Plugin { override fun apply(target: Project) { with(target) { with(pluginManager) { apply("org.jetbrains.kotlin.android") } android { /* ... */} } } } }

Slide 21

Slide 21 text

build-logicのbuild.gradle.kts gradlePlugin { plugins { // Primitives register("android") { id = "droidkaigi.primitive.android" implementationClass = "io.github.droidkaigi.confsched2022.primitive.AndroidPlugin" } register("androidKotlin") { id = "droidkaigi.primitive.android.kotlin" implementationClass = "io.github.droidkaigi.confsched2022.primitive.AndroidKotlinPlugin" } } }

Slide 22

Slide 22 text

convention class AndroidFeaturePlugin : Plugin { override fun apply(target: Project) { with(target) { with(pluginManager) { apply("droidkaigi.primitive.android") apply("droidkaigi.primitive.android.kotlin") apply("droidkaigi.primitive.android.compose") /* ... */ } } } }

Slide 23

Slide 23 text

convention class AndroidFeaturePlugin : Plugin { override fun apply(target: Project) { with(target) { with(pluginManager) { apply("droidkaigi.primitive.android") apply("droidkaigi.primitive.android.kotlin") apply("droidkaigi.primitive.android.compose") /* ... */ } } } }

Slide 24

Slide 24 text

build-logicのbuild.gradle.kts gradlePlugin { plugins { // Primitives /* ... */ // Conventions register("androidFeature") { id = "droidkaigi.convention.androidfeature" implementationClass = "io.github.droidkaigi.confsched2022.convention.AndroidFeaturePlugin" } register("kmp") { id = "droidkaigi.convention.kmp" implementationClass = "io.github.droidkaigi.confsched2022.convention.KmpPlugin" } } }

Slide 25

Slide 25 text

プロジェクト配下のsettings.gradle.kts pluginManagement { includeBuild("gradle/build-logic") repositories { gradlePluginPortal() google() mavenCentral() } }

Slide 26

Slide 26 text

build.gradle.kts plugins { id("droidkaigi.convention.androidfeature") } android.namespace = "io.github.droidkaigi.confsched2022.feature.about" dependencies { /* ... */ }

Slide 27

Slide 27 text

Single source of truth for your build scripts [7] Single source of truth for your build scripts, https://proandroiddev.com/single-source-of-truth-for-your-build-scripts-72d584534949

Slide 28

Slide 28 text

ビルド周りについての所感 ● Version Catalogはクセはあるが... ○ Renovateに続きDependabotの依存関係自動更新対応[8] ● build logic は色々な難しい ● Gradle 8.0.xのアップデート[9] ○ buildSrcのincludeBuild対応をはじめとして パフォーマンス周りで改善 ● 今後まだまだ変更があるかも🤔 [8] Dependabot version updates keeps Gradle version catalogs up-to-date [9] Gradle Release Notes, https://docs.gradle.org/current/release-notes.html

Slide 29

Slide 29 text

appモジュールとCompose

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

結合を低く保つ [10] Common modularization patterns, https://developer.android.com/topic/modularization/patterns#communication

Slide 32

Slide 32 text

@AndroidEntryPoint class MainActivity : AppCompatActivity() { /* ... */ override fun onCreate(savedInstanceState: Bundle?) { /* ... */ setContent { KaigiApp( windowSizeClass = calculateWindowSizeClass(this), sessionIdFromNotification = sessionIdString ) } } /* ... */ } :app-android

Slide 33

Slide 33 text

@Composable fun KaigiApp(/* ... */) { KaigiTheme(/* ... */) { KaigiAppDrawer(/* ... */) { NavHost( modifier = Modifier, navController = kaigiAppScaffoldState.navController, startDestination = SessionsNavGraph.sessionRoute, ) { sessionsNavGraph(/* ... */) contributorsNavGraph(/* ... */) aboutNavGraph(/* ... */) /* ... */ } } } } :app-android

Slide 34

Slide 34 text

@Composable fun KaigiApp(/* ... */) { KaigiTheme(/* ... */) { KaigiAppDrawer(/* ... */) { NavHost( modifier = Modifier, navController = kaigiAppScaffoldState.navController, startDestination = SessionsNavGraph.sessionRoute, ) { sessionsNavGraph(/* ... */) contributorsNavGraph(/* ... */) aboutNavGraph(/* ... */) /* ... */ } } } } :app-android

Slide 35

Slide 35 text

@Composable fun KaigiApp(/* ... */) { KaigiTheme(/* ... */) { KaigiAppDrawer(/* ... */) { NavHost( modifier = Modifier, navController = kaigiAppScaffoldState.navController, startDestination = SessionsNavGraph.sessionRoute, ) { sessionsNavGraph(/* ... */) contributorsNavGraph(/* ... */) aboutNavGraph(/* ... */) /* ... */ } } } } :app-android

Slide 36

Slide 36 text

fun NavGraphBuilder.aboutNavGraph( onStaffListClick: () -> Unit, /* ... */ ) { composable(route = AboutNavGraph.aboutRoute) { AboutScreenRoot( onStaffListClick = onStaffListClick, /* ... */ ) } } :feature:about

Slide 37

Slide 37 text

fun NavGraphBuilder.aboutNavGraph( onStaffListClick: () -> Unit, /* ... */ ) { composable(route = AboutNavGraph.aboutRoute) { AboutScreenRoot( onStaffListClick = onStaffListClick, /* ... */ ) } } :feature:about fun onStaffListClick() { navController .navigate( StaffNavGraph.staffRoute, ) } :app-android

Slide 38

Slide 38 text

fun NavGraphBuilder.aboutNavGraph( onStaffListClick: () -> Unit, /* ... */ ) { composable(route = AboutNavGraph.aboutRoute) { AboutScreenRoot( onStaffListClick = onStaffListClick, /* ... */ ) } } :feature:about fun onStaffListClick() { navController .navigate( StaffNavGraph.staffRoute, ) } :app-android

Slide 39

Slide 39 text

@Composable fun KaigiApp(/* ... */) { KaigiTheme(/* ... */) { KaigiAppDrawer(/* ... */) { NavHost( modifier = Modifier, navController = kaigiAppScaffoldState.navController, startDestination = SessionsNavGraph.sessionRoute, ) { sessionsNavGraph(/* ... */) contributorsNavGraph(/* ... */) aboutNavGraph(/* ... */) /* ... */ } } } } :app-android

Slide 40

Slide 40 text

@Composable fun KaigiApp(/* ... */) { KaigiTheme(/* ... */) { KaigiAppDrawer(/* ... */) { NavHost( modifier = Modifier, navController = kaigiAppScaffoldState.navController, startDestination = SessionsNavGraph.sessionRoute, ) { sessionsNavGraph(/* ... */) contributorsNavGraph(/* ... */) aboutNavGraph(/* ... */) /* ... */ } } } } :app-android

Slide 41

Slide 41 text

Composeにおける制限 ● 子のComposableの領域は親の Composableの領域以下 ○ Scaffoldの中から全画面に ModalBottomSheetを呼び出すと 全画面に表示できない

Slide 42

Slide 42 text

Composeにおける制限 ● 子のComposeの生存期間 ○ Snackbarを子Composableで呼び出すと 子のComposableと共に消えてしまうことがある

Slide 43

Slide 43 text

まとめると... ● appモジュール ○ それぞれのfeatureモジュールへの参照をもつ ○ DrawerやScaffoldやThemeなどアプリ全体に関わるも の ● featureモジュール ○ appモジュールにNavGraphを公開 ○ appモジュールで実装されている遷移実装の呼び出し

Slide 44

Slide 44 text

アジェンダ 1. カンファレンスアプリについて 2. カンファレンスアプリを眺める 3. 感想

Slide 45

Slide 45 text

2021年のカンファレンスアプリと比較して ● Jetpack Composeは継続 ○ Accompanist[11]も含めて開発が進んでいる ● Compose Material 3[12]が登場 & 実装にも ● 全体的に実装の方針が定まりつつある印象 ○ Google公式のドキュメントの充実 ○ Now in AndroidなどのOSSプロジェクト [11] Accompanist, https://google.github.io/accompanist/ [12] Compose Material 3, https://developer.android.com/jetpack/androidx/releases/compose-material3

Slide 46

Slide 46 text

めちゃくちゃ勉強に なりました!

Slide 47

Slide 47 text

来年度もAndroid & DroidKaigi やっていきましょう🙌

Slide 48

Slide 48 text

CREDITS: This presentation template was created by Slidesgo, including icons by Flaticon, infographics & images by Freepik. Thanks!

Slide 49

Slide 49 text

References 1 ● [1] Kotlin Multiplatform, https://kotlinlang.org/docs/multiplatform.html ● [2] Tech Stacks, https://github.com/DroidKaigi/conference-app-2022 ● [3] Herding Elephants, https://developer.squareup.com/blog/herding-elephants/ ● [4] Sharing dependency versions between projects, https://docs.gradle.org/current/userguide/platforms.html ● [5] Composing builds, https://docs.gradle.org/current/userguide/composite_builds.html ● [6] Single source of truth for your build scripts, https://proandroiddev.com/single-source-of-truth-for-your-build-scripts-72d584534949 ● [7] Single source of truth for your build scripts, https://proandroiddev.com/single-source-of-truth-for-your-build-scripts-72d584534949

Slide 50

Slide 50 text

References 2 ● [8] Dependabot version updates keeps Gradle version catalogs up-to-date, https://github.blog/changelog/2023-03-13-dependabot-version-updates-keeps-gradle-version-cata logs-up-to-date/ ● [9] Gradle Release Notes, https://docs.gradle.org/current/release-notes.html ● [10] Common modularization patterns, https://developer.android.com/topic/modularization/patterns#communication ● [11] Accompanist, https://google.github.io/accompanist/ ● [12] Compose Material 3, https://developer.android.com/jetpack/androidx/releases/compose-material3