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

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

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. Tech Stacks • 100% Compose (activity, foundation, material3, accompanist) •

    Navigation (Compose, Hilt) • WindowManager • Coil User Interface 01
  2. 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
  3. 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
  4. 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
  5. 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.
  6. 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
  7. 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
  8. 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( ..
  9. 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
  10. @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
  11. @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
  12. 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
  13. 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.
  14. UI Layer - Material You • Material Theme • Material

    Components • Dynamic Color Scheme
  15. Material Theme (Color Scheme) • Primary • onPrimary • PrimaryContainer

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

    Default Theme Android 12 Specific General
  17. 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
  18. @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
  19. @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
  20. @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
  21. UI Layer - Compose Phases Which UI to draw Where

    to place UI. Measurement and placement How it renders
  22. 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
  23. Performance - Baseline Profiles Compose is a library — it

    does not participate in system resource sharing.
  24. 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 ….
  25. 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