Upgrade to Pro — share decks privately, control downloads, hide ads and more …

2022 I/O Extended Korea Android - Exploring Now in Android

2022 I/O Extended Korea Android - Exploring Now in Android

2022 I/O Extended Korea Android - Exploring Now in Android.

D4df9c152982182ce1125e49fe83527c?s=128

Jaewoong

June 11, 2022
Tweet

More Decks by Jaewoong

Other Decks in Technology

Transcript

  1. Android Developer Advocate @ Stream Exploring Now in Android Jaewoong

  2. skydoves @github_skydoves Android Developer Advocate @ Stream Jaewoong Eum Google

    Developer Expert
  3. Now in Android

  4. Now in Android

  5. Now in Android Exploring Tech Stacks

  6. Tech Stacks • 100% Compose (activity, foundation, material3, accompanist) •

    Navigation (Compose, Hilt) • WindowManager • Coil User Interface 01
  7. Tech Stacks • 100% Compose (activity, foundation, material3, accompanist) •

    Navigation (Compose, Hilt) • WindowManager • Coil User Interface 01 • DataStore • Room Database • Retrofit • Kotlin Serialization • Kotlin Coroutines • WorkManager Business Works 02
  8. Tech Stacks • 100% Compose (activity, foundation, material3, accompanist) •

    Navigation (Compose, Hilt) • WindowManager • Coil User Interface 01 • DataStore • Room Database • Retrofit • Kotlin Serialization • Kotlin Coroutines • WorkManager Business Works 02 • Baseline Profiles • Macrobenchmark • Hilt • App Startup Other Jetpacks 03
  9. Now in Android Exploring App Architecture

  10. Architecture Overview Unidirectional Data Flow • Higher layers react to

    changes in lower layers. • Events flow down. • Data flows up with data streams. (Kotlin Flows) Unidirectional Event Flow
  11. Architecture - UI Layer

  12. Architecture - UI Layer

  13. Architecture - UI Layer

  14. Architecture - UI Layer Modeling UI state • UI state

    always represents the underlying app data. • UI elements handle all possible states. Transforming streams into UI state • View models receive streams of data as Flows from data layers. • The single flow is converted to StateFlows, which enables UI elements to read the last state. Processing user interactions (Unidirectional event flow) • User actions are communicated from UI elements to view models. • View models execute business logic following the user interactions.
  15. Architecture - Data Layer

  16. Architecture - Data Layer UI layers must be prepared to

    react to data changes.
  17. Reads are performed from local storage as the single source

    of truth. Architecture - Data Layer
  18. A single source of truth principle is a philosophy of

    aggregating the data from many systems within an organization to a single location. Architecture - Data Layer
  19. Architecture - Data Layer

  20. interface Syncable { /** * Synchronizes the local database backing

    the repository with the network. * Returns if the sync was successful or not. */ suspend fun syncWith(synchronizer: Synchronizer): Boolean } Architecture - Sync
  21. interface Syncable { /** * Synchronizes the local database backing

    the repository with the network. * Returns if the sync was successful or not. */ suspend fun syncWith(synchronizer: Synchronizer): Boolean } Architecture - Sync interface TopicsRepository : Syncable class OfflineFirstTopicsRepository @Inject constructor( .. ) : TopicsRepository { override suspend fun syncWith(synchronizer: Synchronizer): Boolean = synchronizer.changeListSync( ..
  22. interface Syncable { /** * Synchronizes the local database backing

    the repository with the network. * Returns if the sync was successful or not. */ suspend fun syncWith(synchronizer: Synchronizer): Boolean } Architecture - Sync interface TopicsRepository : Syncable class OfflineFirstTopicsRepository @Inject constructor( .. ) : TopicsRepository { override suspend fun syncWith(synchronizer: Synchronizer): Boolean = synchronizer.changeListSync( .. Versioning each model
  23. @HiltWorker class SyncWorker @AssistedInject constructor( ... ) : CoroutineWorker(appContext, workerParams),

    Synchronizer { override suspend fun doWork(): Result = withContext(ioDispatcher) { // First sync the repositories in parallel val syncedSuccessfully = awaitAll( async { topicRepository.syncWith(this) }, async { authorsRepository.syncWith(this) }, async { newsRepository.syncWith(this) }, ).all { it } if (syncedSuccessfully) Result.success() else Result.retry() } Architecture - Sync
  24. @HiltWorker class SyncWorker @AssistedInject constructor( ... ) : CoroutineWorker(appContext, workerParams),

    Synchronizer { override suspend fun doWork(): Result = withContext(ioDispatcher) { // First sync the repositories in parallel val syncedSuccessfully = awaitAll( async { topicRepository.syncWith(this) }, async { authorsRepository.syncWith(this) }, async { newsRepository.syncWith(this) }, ).all { it } if (syncedSuccessfully) Result.success() else Result.retry() } Architecture - Sync Synchronize all repositories
  25. class SyncInitializer : Initializer<Sync> { override fun create(context: Context): Sync

    { WorkManager.getInstance(context).apply { // Run sync on app startup and ensure only one sync worker runs at any time enqueueUniqueWork( SyncWorkName, ExistingWorkPolicy.REPLACE, SyncWorker.startUpSyncWork() ) } return Sync } override fun dependencies(): List<Class<out Initializer<*>>> = .. } Architecture - Sync Returns OneTimeWorkRequest
  26. <provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" android:exported="false" tools:node="merge"> <meta-data android:name="com.google.samples.apps.nowinandroid.sync.initializers.SyncInitializer" android:value="androidx.startup" tools:node="remove" />

    </provider> Architecture - Sync
  27. Architecture - ForYou Screen

  28. Architecture - ForYou Screen

  29. Architecture - ForYou Screen

  30. Architecture - ForYou Screen

  31. Architecture - ForYou Screen The recommendations and best practices present

    in this architecture can be applied to a broad spectrum of apps to allow them to scale, improve quality and robustness, and make them easier to test. However, you should treat them as guidelines and adapt them to your requirements as needed.
  32. Now in Android Exploring UI Layers with Compose

  33. UI Layer - Material You

  34. UI Layer - Material You Dynamic color extraction algorithm

  35. UI Layer - Material You • Material Theme • Material

    Components • Dynamic Color Scheme
  36. UI Layer - Material You

  37. UI Layer - Material You

  38. UI Layer - Material You

  39. UI Layer - Material You

  40. Material Theme (Color Scheme) • Primary • onPrimary • PrimaryContainer

    • onPrimaryContainer • Secondary • onSecondary • Tertiary • onTertiary • Bacground • onBackground • Surface • onSurface • SurfaceVariant • onSurfaceVariant … UI Layer - Theming
  41. UI Layer - Theming Dynamic Theme Light Dark Custom Theme

    Default Theme Android 12 Specific General
  42. private val LightDefaultColorScheme = lightColorScheme( primary = Purple40, onPrimary =

    Color.White, … private val DarkDefaultColorScheme = darkColorScheme( primary = Purple80, onPrimary = Purple20, … private val LightAndroidColorScheme = lightColorScheme( primary = Green40, onPrimary = Color.White, … private val DarkAndroidColorScheme = darkColorScheme( primary = Green80, onPrimary = Green20, …. UI Layer - Theming
  43. @Composable fun NiaTheme( darkTheme: Boolean = isSystemInDarkTheme(), dynamicColor: Boolean =

    false, androidTheme: Boolean = false, content: @Composable() () -> Unit ) { val colorScheme = when { dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { val context = LocalContext.current if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) } androidTheme && darkTheme -> DarkAndroidColorScheme androidTheme -> LightAndroidColorScheme darkTheme -> DarkDefaultColorScheme else -> LightDefaultColorScheme } UI Layer - Theming
  44. @Composable fun NiaTheme( darkTheme: Boolean = isSystemInDarkTheme(), dynamicColor: Boolean =

    false, androidTheme: Boolean = false, content: @Composable() () -> Unit ) { val colorScheme = when { dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { val context = LocalContext.current if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) } androidTheme && darkTheme -> DarkAndroidColorScheme androidTheme -> LightAndroidColorScheme darkTheme -> DarkDefaultColorScheme else -> LightDefaultColorScheme } UI Layer - Theming
  45. @Composable fun NiaTheme( darkTheme: Boolean = isSystemInDarkTheme(), dynamicColor: Boolean =

    false, androidTheme: Boolean = false, content: @Composable() () -> Unit ) { val colorScheme = when { dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { val context = LocalContext.current if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) } androidTheme && darkTheme -> DarkAndroidColorScheme androidTheme -> LightAndroidColorScheme darkTheme -> DarkDefaultColorScheme else -> LightDefaultColorScheme } UI Layer - Theming
  46. UI Layer - Large Screen The large screens of tablets,

    foldables, and Chrome OS devices
  47. UI Layer - Large Screen https://bit.ly/3Q0HGoS

  48. UI Layer - Large Screen

  49. dependencies { implementation "androidx.compose.material3:material3-window-size-class:1.0.0-alpha10" Implementation "androidx.window:window:1.0.0" } UI Layer -

    Large Screen
  50. UI Layer - Large Screen

  51. UI Layer - Large Screen

  52. UI Layer - Large Screen

  53. UI Layer - Large Screen

  54. UI Layer - Large Screen Navigation Rail

  55. UI Layer - Large Screen Compact Medium, Expanded

  56. Now in Android Exploring App Performance

  57. UI Layer - Compose Phases Which UI to draw Where

    to place UI. Measurement and placement How it renders
  58. UI Layer - Recomposition

  59. Composable functions can use the remember API to store an

    object in memory. A value computed by remember is stored in the Composition during initial composition, and the stored value is returned during recomposition. UI Layer - Remember
  60. UI Layer - Remember

  61. UI Layer - Remember High costs in every recomposition

  62. UI Layer - Remember Recompose when key values changed

  63. UI Layer - Lazy Lists Column LazyColumn

  64. UI Layer - Lazy Lists

  65. UI Layer - Lazy Lists

  66. UI Layer - Lazy Lists

  67. UI Layer - Lazy Lists

  68. Performance - Baseline Profiles It seems to be janky for

    the first couple of seconds!
  69. Performance - Baseline Profiles Compose is a library — it

    does not participate in system resource sharing.
  70. Performance - Baseline Profiles Baseline Profiles Android Runtime

  71. Performance - Baseline Profiles

  72. Performance - Baseline Profiles

  73. Performance - Baseline Profiles Total: 7059 lines (baseline-prof.txt) HSPLandroidx/compose/animation/AndroidFlingSpline$FlingResult;-><init>(FF)V HSPLandroidx/compose/animation/AndroidFlingSpline;-><clinit>()V

    HSPLandroidx/compose/animation/AndroidFlingSpline;-><init>()V HSPLandroidx/compose/animation/AndroidFlingSpline;->flingPosition(F)Landroidx/compose/animation/AndroidFlingSpline$FlingResult; HSPLandroidx/compose/animation/CrossfadeKt$$ExternalSyntheticOutline0;->m(Landroidx/compose/runtime/Composer;)V HSPLandroidx/compose/animation/CrossfadeKt$Crossfade$1;-><init>(Ljava/lang/Object;Landroidx/compose/ui/Modifier;Landroidx/comp ose/animation/core/FiniteAnimationSpec;Lkotlin/jvm/functions/Function3;II)V HSPLandroidx/compose/animation/CrossfadeKt$Crossfade$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; HSPLandroidx/compose/animation/CrossfadeKt$Crossfade$2;-><clinit>()V HSPLandroidx/compose/animation/CrossfadeKt$Crossfade$2;-><init>()V HSPLandroidx/compose/animation/CrossfadeKt$Crossfade$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLandroidx/compose/animation/CrossfadeKt$Crossfade$3$1;-><init>(Landroidx/compose/animation/core/Transition;)V HSPLandroidx/compose/animation/CrossfadeKt$Crossfade$3$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLandroidx/compose/animation/CrossfadeKt$Crossfade$4$1$$ExternalSyntheticOutline0;->m(Landroidx/compose/runtime/Compose r;III)V HSPLandroidx/compose/animation/CrossfadeKt$Crossfade$4$1$1$1;-><init>(Landroidx/compose/runtime/State;)V HSPLandroidx/compose/animation/CrossfadeKt$Crossfade$4$1$1$1;->invok HSPLandroidx/compose/animation/CrossfadeKt;->Crossfade(Landroidx/compose/animation/core/Transition;Landroidx/compose/ui/Modifier; Landroidx/compose/animation/core/FiniteAnimationSpec;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Landroidx/compose /runtime/Composer;II)V ….
  74. References • Now in Android ◦ https://github.com/android/nowinandroid • Exploring App

    Architecture ◦ https://github.com/android/nowinandroid/blob/main/docs/ArchitectureLearningJourney.md • Exploring UI Layer ◦ https://material.io/blog/start-building-with-material-you ◦ https://getstream.io/blog/material-you-jetpack-compose/ ◦ https://getstream.io/blog/jetpack-windowmanager-foldable/ ◦ https://developer.android.com/guide/topics/large-screens/support-different-screen-sizes ◦ https://youtu.be/EOQB8PTLkpY ◦ https://developer.android.com/jetpack/compose/performance • Performance ◦ https://developer.android.com/jetpack/compose/phases ◦ https://developer.android.com/topic/performance/baselineprofiles ◦ https://developer.android.com/topic/performance/benchmarking/macrobenchmark-overview
  75. Thank you! Android Developer Advocate @ GetStream Google Developer Expert

    skydoves skydoves2@gmail.com @github_skydoves Contacts Jaewoong Eum