Skip to content

An Android project implementing some good practices on starting an app.

License

Notifications You must be signed in to change notification settings

guichristovao/app-startup

Repository files navigation

Banner with the project name and the following description: An Android project implementing some good practices on starting an app. It also shows an animated preview of a sample app.

Table of contents

  1. Introduction
  2. Variations
  3. Architecture
  4. Implementation
  5. Roadmap

Introduction

This is an Android showcase project based on a simple feature using GitHub API.

The main goal of this project is to implement the best practices on how to start an app, as recommended by Google and seen here:

Variations

This project hosts two sample apps in separate repository branches.

Branch Description
main Default sample for this project. Uses profile feature as first loading screen.
lazy-initialization* Uses an static feature as first loading screen and implements lazy initialization of other components.

* work in progress

Architecture

App Startup is a multi-module project built with MVVM Architecture.

Structure

.
├── app                 # Project sample (android-application)
├── buildSrc            # Dependency management with Kotlin DSL
├── core
│   ├── network         # Network layer abstraction (android-library)
│   ├── network-impl    # Network layer implementation (android-library)
│   ├── splash-screen   # Splash screen configurations (android-library)
│   └── ui              # Theme and components (android-library)
└── feature
    ├── menu*           # Feature which doesn't depends on network requests (android-library)
    └── profile         # Feature which depends on network requests (android-library)

* the feature:menu module is used only on lazy-initialization variation

Diagram

Project's architecture flowchart

Stack

Testing

Implementation

It provides a performant way to initialize components and explicitly define their dependencies.

To automatically initialize components at startup, you must define a component initializer for each component that the app needs to initialize.
This can be done by implementing the Initializer<T> interface and setting the manifest entries.

Since this sample project uses the abstract network module to consume the GitHub API, the profile feature module initialization shall depend on the network-impl module to provide of a concrete implementation of a Retrofit interface.

ProfileInitializer
class ProfileInitializer : Initializer<Unit> {
    override fun create(context: Context) {
        ProfileFeature.init(NetworkImpl)
    }
    override fun dependencies(): List<Class<out Initializer<*>>> {
        //  This is only called after NetworkInitializer is initialized
        return listOf(NetworkInitializer::class.java)
    }
}
NetworkInitializer
class NetworkInitializer : Initializer<Network> {
    override fun create(context: Context): Network {
        return NetworkImpl
    }
    override fun dependencies(): List<Class<out Initializer<*>>> {
        // No dependencies on other components
        return emptyList()
    }
}

Preview of the splash screen opening, followed by a screen with a loading placeholder UI.

Includes an into-app motion at launch, a splash screen showing your app icon, and a transition to your app itself.
As recommended by Material Design, a branded indicator is displayed until a placeholder UI loads.

// feature:profile
override fun onCreate(savedInstanceState: Bundle?) {
    showSplashScreen { viewModel.uiState.value is ProfileViewModel.UiState.Default }
    super.onCreate(savedInstanceState)
    // ...
}

// core:splash-screen
fun Activity.showSplashScreen(condition: KeepOnScreenCondition? = null) {
    installSplashScreen().run {
        condition?.let { ::setKeepOnScreenCondition }
        setOnExitAnimationListener { /** Custom animation */ }
    }
}

It's also possible to keep the splash screen on-screen for longer periods by setting a condition on an observable value such as StateFlow or LiveData.

showSplashScreen { viewModel.uiState.value is ProfileViewModel.UiState.Success }

In this approach the Placeholder UI is not used, since the first screen will only be shown to the user when its contents has fully loaded.

A placeholder UI loading its content on a small white retangular container.

Also known as skeleton screen. It shows a content placeholder while network-based data is being fetched, instead of displaying a blank screen or a default loading animation.

This was done using Accompanist-placeholder, a library for Jetpack Compose.

As recommended here and here, the placeholder is most likely to be shown only when a user opens the app for the first time. The next time this user returns to the app, we can show a cached content while a more recent content is loaded.

Tests

Roadmap

About

An Android project implementing some good practices on starting an app.

Topics

Resources

License

Stars

Watchers

Forks

Languages