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.

Jaewoong

June 11, 2022
Tweet

More Decks by Jaewoong

Other Decks in Technology

Transcript

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

    View Slide

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

    View Slide

  3. Now in Android

    View Slide

  4. Now in Android

    View Slide

  5. Now in Android
    Exploring
    Tech Stacks

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  9. Now in Android
    Exploring
    App Architecture

    View Slide

  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

    View Slide

  11. Architecture - UI Layer

    View Slide

  12. Architecture - UI Layer

    View Slide

  13. Architecture - UI Layer

    View Slide

  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.

    View Slide

  15. Architecture - Data Layer

    View Slide

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

    View Slide

  17. Reads are performed from local
    storage as the single source of truth.
    Architecture - Data Layer

    View Slide

  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

    View Slide

  19. Architecture - Data Layer

    View Slide

  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

    View Slide

  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(
    ..

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  25. class SyncInitializer : Initializer {
    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>> =
    ..
    }
    Architecture - Sync
    Returns OneTimeWorkRequest

    View Slide

  26. android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    android:name="com.google.samples.apps.nowinandroid.sync.initializers.SyncInitializer"
    android:value="androidx.startup"
    tools:node="remove" />

    Architecture - Sync

    View Slide

  27. Architecture - ForYou Screen

    View Slide

  28. Architecture - ForYou Screen

    View Slide

  29. Architecture - ForYou Screen

    View Slide

  30. Architecture - ForYou Screen

    View Slide

  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.

    View Slide

  32. Now in Android
    Exploring
    UI Layers with Compose

    View Slide

  33. UI Layer - Material You

    View Slide

  34. UI Layer - Material You
    Dynamic color
    extraction
    algorithm

    View Slide

  35. UI Layer - Material You
    ● Material Theme
    ● Material Components
    ● Dynamic Color Scheme

    View Slide

  36. UI Layer - Material You

    View Slide

  37. UI Layer - Material You

    View Slide

  38. UI Layer - Material You

    View Slide

  39. UI Layer - Material You

    View Slide

  40. Material Theme (Color Scheme)
    ● Primary
    ● onPrimary
    ● PrimaryContainer
    ● onPrimaryContainer
    ● Secondary
    ● onSecondary
    ● Tertiary
    ● onTertiary
    ● Bacground
    ● onBackground
    ● Surface
    ● onSurface
    ● SurfaceVariant
    ● onSurfaceVariant

    UI Layer - Theming

    View Slide

  41. UI Layer - Theming
    Dynamic
    Theme
    Light Dark
    Custom
    Theme
    Default
    Theme
    Android 12
    Specific
    General

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  46. UI Layer - Large Screen
    The large screens of tablets, foldables, and Chrome OS devices

    View Slide

  47. UI Layer - Large Screen
    https://bit.ly/3Q0HGoS

    View Slide

  48. UI Layer - Large Screen

    View Slide

  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

    View Slide

  50. UI Layer - Large Screen

    View Slide

  51. UI Layer - Large Screen

    View Slide

  52. UI Layer - Large Screen

    View Slide

  53. UI Layer - Large Screen

    View Slide

  54. UI Layer - Large Screen
    Navigation Rail

    View Slide

  55. UI Layer - Large Screen
    Compact Medium, Expanded

    View Slide

  56. Now in Android
    Exploring
    App Performance

    View Slide

  57. UI Layer - Compose Phases
    Which UI to draw
    Where to place UI.
    Measurement and
    placement
    How it renders

    View Slide

  58. UI Layer - Recomposition

    View Slide

  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

    View Slide

  60. UI Layer - Remember

    View Slide

  61. UI Layer - Remember
    High costs in every recomposition

    View Slide

  62. UI Layer - Remember
    Recompose when key values changed

    View Slide

  63. UI Layer - Lazy Lists
    Column LazyColumn

    View Slide

  64. UI Layer - Lazy Lists

    View Slide

  65. UI Layer - Lazy Lists

    View Slide

  66. UI Layer - Lazy Lists

    View Slide

  67. UI Layer - Lazy Lists

    View Slide

  68. Performance - Baseline Profiles
    It seems to be janky for the
    first couple of seconds!

    View Slide

  69. Performance - Baseline Profiles
    Compose is a library — it
    does not participate in
    system resource sharing.

    View Slide

  70. Performance - Baseline Profiles
    Baseline Profiles
    Android
    Runtime

    View Slide

  71. Performance - Baseline Profiles

    View Slide

  72. Performance - Baseline Profiles

    View Slide

  73. Performance - Baseline Profiles
    Total: 7059 lines (baseline-prof.txt)
    HSPLandroidx/compose/animation/AndroidFlingSpline$FlingResult;->(FF)V
    HSPLandroidx/compose/animation/AndroidFlingSpline;->()V
    HSPLandroidx/compose/animation/AndroidFlingSpline;->()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;->(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;->()V
    HSPLandroidx/compose/animation/CrossfadeKt$Crossfade$2;->()V
    HSPLandroidx/compose/animation/CrossfadeKt$Crossfade$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
    HSPLandroidx/compose/animation/CrossfadeKt$Crossfade$3$1;->(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;->(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
    ….

    View Slide

  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

    View Slide

  75. Thank you!
    Android Developer Advocate @ GetStream
    Google Developer Expert
    skydoves
    [email protected]
    @github_skydoves
    Contacts
    Jaewoong Eum

    View Slide