0

I am not understanding how android activities are managed.

I have an activity and every once in a while i have noticed that, when my app goes into the background, android destroys whatever the current activity is (say Activity3) and several other singletons and objects etc. Thats fine. The problem is when the app is resumed then intuition tells me that since android has destroyed the activity and objects for memory or whatever, then android would just restart the app completely from Activity1 so all the objects and data members would get properly initalized.

NOT SO!

It seems that when my app is resumed, the Activity3 is recreated and onCreate is called with the same parameters as it was the first time (when it was called from Activity2) only this time all the singletons and other objects that were initialized in Activity1 and Activity2 are recreated with their default values and are rendered useless.

How is this a safe policy/technique? How can android just randomly destroy objects and activities and then when the user resumes just call onCreate on the recent activity and expect everything to be hunky doory and NOT have to go through the proper startup procedure/initialization?

UPDATE / SOLUTION

Thanks to the commentors for their excellent info.

ACCORDING TO ANDROID DOCUMENTATION

onCreate

Bundle: If the activity is being re-initialized after previously being shut down then this Bundle contains the data it most recently supplied in onSaveInstanceState(Bundle). Note: Otherwise it is null.

THEREFORE what I ended up doing is I set TWO flags. One in onSaveInstanceState in the Bundle so to know that it is a valid Bundle set by me. The other in the class itself to determine if onCreate was called because of recreation or Auto-Rotation. And so in onCreate I checked to see if onSaveInstanceState is not null, check the Bundle flag, and check bInit (which defaults to false). If both flags are true then it means android dumped and destroyed our apps memory and the safest way to ensure everything is initialized again in a linear-style application is to just restart it and launch the beginning activity.

public class SomeMiddleActivity extends AppCompatActivity
{
    private static boolean bInit = false; // only way it will be false again is if android cleared our memory and we are recreating

    @Override
    public void onSaveInstanceState(Bundle state)
    {
        // set a flag so that onCreate knows this is valid
        state.putBoolean("StateSaved", true);
        super.onSaveInstanceState(state);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        // this must be called first always for some reason
        super.onCreate(savedInstanceState);

        if (savedInstanceState != null)
        {
            if (savedInstanceState.getBoolean("StateSaved", false) && !bInit)
            {
                // we were recreated... start app over
                Intent intent = new Intent(getApplicationContext(), Startup.class);
                startActivity(intent);
                finish();
                return;
            }
        }

        bInit = true; // this will stay true until android has cleared our memory

        .......
    } 

Although this has worked thus far, if anyone has a different suggestion let me know. I will be posting another article on this.

And FYI: the onSaveInstanceState(Bundle, PersistableBundle) version of onSaveInstanceState is never called ever so I dont know why they even implement it. (?)

@goldenb @Rishabh Thanks to goldenb and Rishabh for the insight.

zdanman
  • 508
  • 1
  • 3
  • 13

2 Answers2

1

Android, if destroys, also gives you tools to handle it.

Mobile devices have limited amount of memory which needs to be shared among Applications running simultaneously. Thus, smart resource allocation is necessary. The Apps running on foreground are used by End-User and gain high priority for better performance and user experience. Thus, applications running in background need to release the resources to suffice the memory requirements for foreground applications. Hence, background applications are destroyed (not completely) sometimes (in case of low memory).

Android Activities have Callbacks likes onSaveInstanceState() and onRestoreInstanceState() which enable you to save your current state of Activity (i.e., values of variables) when it is destroyed and retrieve them when the Activity is recreated.

You can get more information from here: How to save and retrieve the state of Activity using onSaveInstanceState and onRestoreInstanceState.

You can perform validations on the retreived state to ensure the Activity performs exactly as it was doing pre-destruction. You would find it very easy and logical once you get hands-on it.

  • is there any way to have the app completely restart when an instance is resumed? – zdanman Sep 29 '16 at 19:37
  • 1
    There are workarounds (e.g., to kill the process and restart it) but they are not recommended. Rather, you can Clear the Activity Task Stack (using Intent flags) and start the Launcher Activity (may be Notifying the User, if required). – Rishabh Dutt Sharma Sep 29 '16 at 20:19
  • Let me run this by you. What if I call **System.exit(0)** or **finish()** in **onSaveInstanceState** ??? From my understanding **onSaveInstanceState** is called and _only_ called when the app is in the background and probably being cleared from memory. This will force the app to start whenever the user presses the app icon again. – zdanman Sep 29 '16 at 23:21
1

Just giving my 50 cents on the issue. The correct way to deal with the issue of an activity being killed by the system for its resources in background is a common problem in android and according to Google the solution for this is:

onPause() is where you deal with the user leaving your activity. Most importantly, any changes made by the user should at this point be committed (usually to the ContentProvider holding the data).

Emphasis is mine. But what this means is that the Android lifecycles are designed so that under normal conditions onPause should be called as an Activity or Fragment is sent to the background. They hint at this in several of the android documentation pages:

As your activity enters the paused state, the system calls the onPause() method on your Activity, which allows you to stop ongoing actions that should not continue while paused (such as a video) or persist any information that should be permanently saved in case the user continues to leave your app.

Also worthy of your attention: if you wish that views are restored during Activity recreation, you should have set the ID attribute of all views ;)

Note: In order for the Android system to restore the state of the views in your activity, each view must have a unique ID, supplied by the android:id attribute.

PS. You were wondering why onSaveInstanceState(Bundle, PersistableBundle) is not called, one possibility is that you do not have the right activity attribute set

This is the same as onRestoreInstanceState(Bundle) but is called for activities created with the attribute persistableMode set to persistAcrossReboots..

HenriqueMS
  • 3,864
  • 2
  • 30
  • 39
  • 1
    by the way you should also be experiencing some of these problems when you rotate your device. You should definitely stick to persisting your data during onPause() ;) – HenriqueMS Sep 30 '16 at 16:04
  • Thank you for bringing this up. I forgot that that **onRestoreInstanceState** was called during rotate as well. What I did was add a flag **bInit** in memory which is by default false. This will tell me if onCreate was called because of Rotation or Recreation. I updated the article above. – zdanman Oct 01 '16 at 13:23