3

Say I have a custom application in class in my Android app that makes a network call, asynchronously, on startup. Then I have a Main activity that needs the results of the network call on startup. I can handle this with a Splash activity that waits on app startup. But when the process is recreated, we go straight to the Main activity, which expects the results of the network call to be there, and it crashes.

See the code below for an example.

What's the best way to handle this? I want to keep the splash screen for normal startup situations. But in the second situation, where the app is recreated, I'm not sure how to handle it. Is there a way to show the splash screen again, and wait, before returning to the recreated Main activity?

class MyApplication : Application() {

    private val scope = CoroutineScope(Dispatchers.Main)

    companion object {
        lateinit var version: Integer
        var startupFinishedListener: (() -> Unit)? = null
    }

    override fun onCreate() {
        super.onCreate()
        scope.launch {
            version = getVersionFromNetwork() //Fake suspending network call
            startupFinishedListener?.invoke()
        }
    }
}
class SplashScreen : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.splash)
        MyApplication.startupFinishedListener = {
            startMainActivity()
        }
    }

    fun startMainActivity() {
        val intent = Intent(this, MainActivity::class.java)
        startActivity(intent)
    }
}
class MainActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)

        //This line will crash if we are coming from an activity recreation
        Timber.d("Version is: ${MyApplication.version}")
    }
}

I would prefer not to solve this by doing work in onResume in all of my activities instead of onCreate(). Ideally I could tell Android to launch my Splash activity before it launches the restored activity.

PhillyTheThrilly
  • 752
  • 8
  • 18
  • 2
    " Is there a way to show the splash screen again, and wait, before returning to the recreated Main activity?" -- not really. There is nothing stopping you from detecting your missing data, then displaying the splash screen (whether as an activity or a fragment) yourself. "It is unreasonable for my app to be able to save all of its state in onSaveInstanceState, there is simply too much data" -- then cache it on disk, so you can reload it rapidly and can skip the splash screen. – CommonsWare Feb 16 '20 at 21:56

2 Answers2

2

You will have to handle this in your onResume() lifecycle. You can create an onResume and then call the SplashActivity intent and finish() your MainActivity. This way it will just go back to Splash Screen where it will load the data and call the Main Activity again

override fun onResume() {
     super.onResume()
     val intent = Intent(this,SplashActivity)
     startActivity(intent)
}
Yunus Kulyyev
  • 905
  • 12
  • 24
  • I could make this work, but then I lose the recreated state for the MainActivity, since we are restarting it. Ideally I would maintain the state. – PhillyTheThrilly Jan 03 '20 at 20:03
  • In that case, instead of calling splash activity, put your network logic inside onResume() and populate your views. I will assume you are calling it in a separate thread, in that case, just populate your views with empty fields so that they dont complain, and once your network logic is done reading, it will update fields – Yunus Kulyyev Jan 03 '20 at 20:47
  • I would prefer not to do that for every activity – PhillyTheThrilly Feb 16 '20 at 17:14
2

"This looks like an Architectural Problem."

Calling an API into application class and registering it's listener to your activities is a bad idea!

There are few options for you to handle your requirement:

  1. If the data you want is really that important in MainActivity, you can call the API in MainActivity onCreate() itself with some loading indicator before your actual data is loaded.

What will happen if process restarts in this case?

Your MainActivity will recreate and call the API again. So, you're good.

  1. If you're required to have that data as soon as your MainActivity starts, without waiting, you need to do caching. Call your API in splash activity and save the data to a file, shared preference or database. Then access cached data in your MainActivity.

What will happen if process restarts in this case?

Since you've already cached your data into a persitent storage, it'll be there even after process restart. So, you're good to get your data back.

Bonus

If you don't want to use your cached data everytime you open your app, just clear the cache in your SplashActivity and call the API everytime to have the latest data with you before moving to MainActivity.

Maulik Hirani
  • 949
  • 8
  • 16