com2ghz
com2ghz

Reputation: 2876

Android starting up application wait until asynchronous code execution is complete before opening activity

At startup of my application I would like to execute some initialization code.

I need to initialize:

As you know these are asynchronous tasks I need to perform.

What I currently have is my initialization code inside MainActivity.onCreate() with a callback method which will open CoreActivity. This works fine when I do a cold boot of the app.

The problem begins at the moment the app is moved to the background, Android can kill it to free memory. I force this behavior now by disallowing background processes under Developer options.

So if I have ExampleActivity opened and I move the app to the background, Android tries to reinitialize ExampleActivity when I move it to the foreground. This means my app doesn't initialize properly because MainActivity.onCreate() is never executed.

So I thought I could move the initialiation code to Application.onCreate(). The problem I face here is that onCreate() completes execution and opens the ExampleActivity even while the initialization code is still running.

I noticed that sleeping the main thread inside Application.onCreate() is bad. Also having a while loop watching a isInitialized boolean never gets true and ends in a infinite loop.

How should I handle this situation?

I would like to do these steps

  1. App starting up
  2. App is initializing and need to wait on my callback method
  3. Initializing is done callback method is called, open the activity.

Upvotes: 4

Views: 3117

Answers (3)

yvolk
yvolk

Reputation: 2411

I had a very close problem in my application for years. I.e. before starting almost any activity (or even onResume...) I need to make sure that my application is initialized. So far I had inserted a call to the synchronous initialization code to the onCreate method of many activities (in a case the initialization is done already, the call completes instantly, of course).

As gradually with an application grow the initialization takes more and more time, this started to cause ANRs sometimes in different activities, so I also came to the need to implement the asynchronous initialization on any activity start.

So, I recently implemented the below approach, which can be looked at / tested (see the GitHub project and, in particular, this class: MyContextHolder.java )

  1. Insert a call to my statically defined application initialization code into onCreate method of every activity, which requires application to be initialized, passing the calling Activity instance (this) to the initialization code. Basically it looks like:

    if (MyContextHolder.initializeThenRestartMe(this)) {
        return;
    }
    
  2. a. If initialization is already done, return false instantly. Done.

    b. If the initialization is needed, grab the Activity's launch parameters + the application's context so that the activity could be re-launched later. The simplest case: store the Activity's Intent. ( activity.getIntent() and activity.getApplicationContext() )

  3. Finish the Activity, which is not even shown yet, with its .finish() method.

  4. Optionally show some "Please wait..." activity to a User. (I didn't need this yet...)

  5. Do application initialization in a background thread ( specialized AsyncTask ) using the applicationContext. See MyFutureContext.java )

  6. On a completion re-launch the Activity using its stored parameters. E.g. using stored Intent and the Application's Context. Like this: applicationContext.startActivity(intent);

One useful tip: In order not to see blinking activities, which are being restarted, set default application's theme (in the Android Manifest) to a transparent one. Super!

The below style definition is from themes.xml file

<style name="Theme.Transparent" parent="Theme.AppCompat.NoActionBar">
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowIsFloating">true</item>
    <item name="android:backgroundDimEnabled">false</item>
</style>

Upvotes: 3

Neji
Neji

Reputation: 6839

Why not add a Splash Activity which will do the INIT or atleast listen to the callback - INIT_DONE. On receiving such a callback, you can then start your desired activity.

By this you are achieving following things

  • Good user experience
  • User knows the reason for delay in launching actual activity.
  • No Lag to see the desired activity.
  • Increase in probability that user wont uninstall app due to frustration :D

Update : Why not check the INIT_State from Application.java file. If app INIT not done then load the base Activity or Splash and then the application will then work only in state where app init is done.

For this to achieve, you should have a baseActivity which will be extended by every other activity in the flow other than the splash activity. So, on every activity Resume event, you can check the INIT state and depending on it you can continue loading the activity or can redirect user to splash screen so that the INIT process completes and then user lands on other activities.

Also, for this you can also start AppInit process from splash screen. This will help monitor the control or else you can even use events for the same purpose to fit your design guidelines.

Upvotes: 1

k3b
k3b

Reputation: 14755

I see two alternatives:

  • If "MainActivity is killed by os"

    • initiate the asyc loading again and loose the previous result
    • MainActivity=SplashActivity triggers an AsyncTask. When AsyncTask successfully finishes or finishes with an error an other activity (your MainGuiActivity) is started.
  • If it is not ok that Async-stuff is done again:

    • implement a LoginService that keeps on running even if the activity is canceled.
    • mainActivity checks if LoginService is already running and if not starts it.

Upvotes: 0

Related Questions