Skip to content

Instantly share code, notes, and snippets.

@Mercandj
Last active June 26, 2020 14:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Mercandj/5956309145a212c40831ddef79104bdc to your computer and use it in GitHub Desktop.
Save Mercandj/5956309145a212c40831ddef79104bdc to your computer and use it in GitHub Desktop.

Android: Application initialization

On Android, there is a common issue that developers must solve: Deal with heavy load at application start.

1. Application.onCreate()

The easier solution is to put the initialization on the Application

class MainApplication: Application() {

   override fun onCreate() {
      super.onCreate()
      heavyInitialization()
      // Here the initialization is done
      ApplicationGraph.initialize(this) // Light initialization
   }
}
class MainActivity: Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Here the initialization is done
    }
}

This solution is perfecly fine for "light" initializations. Initialization is done synchronously on the MainThread so at the Activity.onCreate() step, that's fine, your task will be completed.

For heavy job, this solution will slow down your app performances on cold starts (time between Application.onCreate() and the end of you Activity.onCreate() and the views inflate. As your initialization is on the mainThread, impossible for you to have a loader.

Moreover, with this solution, anything that wake-up you app will do the job:

  • Notification display
  • Any BroadCastReceiver

To avoid that, multiple solutions exist.

2. LoadingActivity

This solution consists of keeping light in the Application.onCreate() but to move heavy job and manage it on a WorkerThread via a LoadingActivity as first activity of your application.

interface HeavyInitializerManager {

   fun initialize()

   fun getStatus(): Status
   
   // Crash if not initialized.
   fun getResult(): Result
   
   fun addListener(listener: Listener)
   
   fun removeListener(listener: Listener)
   
   interface Listener {
   
      fun onStatusChanged()
   }
   
   enum class Status {
      IDLE,
      INITIALIZING,
      INITIALIZED
   }
}
class MainApplication: Application() {

   override fun onCreate() {
      super.onCreate()
      ApplicationGraph.initialize(this) // Light initialization
   }
}
class LoadingActivity: Activity() {

    private val heavyInitializerManager by lazy { ApplicationGraph.getHeavyInitializerManager() }
    private val heavyInitializerListener = createHeavyInitializerListener()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        heavyInitializerManager.addListener(heavyInitializerListener)
        updateProgress()
    }
    
    override fun onDestroy() {
        heavyInitializerManager.removeListener(heavyInitializerListener)
    }
    
    private fun updateProgress() {
       when (heavyInitializerManager.getStatus()) {
          HeavyInitializerManager.Status.IDLE,
          HeavyInitializerManager.Status.INITIALIZING -> /* Nothing */
          HeavyInitializerManager.Status.INITIALIZED -> MainActvity.start(this)
       }
    }
    
    private fun createHeavyInitializerListener() = object : HeavyInitializerManager.Listener {
        override fun onStatusChanged() {
           updateProgress()
        }
    }
}

This solution is perfeclty viable and inside your MainActivity you will be able to use heavy initialization results.

But be carefull, on Android, when a user go back to the AppLauncher, if your last activity is the MainActivity, the Android system may kill your app JVM and you will lose your HeavyInitializerManager initialization.

To deal with that, you will be forced to have this kind of code:

class MainActivity: Activity() {

    private val heavyInitializerManager by lazy { ApplicationGraph.getHeavyInitializerManager() }

    override fun onCreate(savedInstanceState: Bundle?) {
        if (heavyInitializerManager.getStatus() != HeavyInitializerManager.Status.INITIALIZED) {
           super.onCreate(null)
           LoadingActivity.start(this)
           finish() // Will skip onStart onResume onPause onStop BUT will call onDestroy
           return
        }
        super.onCreate(savedInstanceState)
        setContentView(/* layout */)
    }
}

The super.onCreate(null) is required to avoid android.app.SuperNotCalledException: Activity did not call through to super.onCreate() and with null to avoid your views/fragments DOM to be restored and potentially use headyInitialization results on your views/fragments.

3. Mono Activity

  • Loading view full screen on top
  • Views/Fragment that are able to work without HeavyInitializerManager result and that listen status to update there UI.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment