Android Activity Lifecycle: restoring app after kill - design problem - android

atm I try to get my app working around "onSaveInstanceState" and "onRestoreInstanceState", but the deeper I dig, the the more problems occur.
So, for example, I just realized, that restoring one activity via those two functions is pretty useless. Because, if I press the back button and return to the activity before, this one doesn't get its "savedInstanceState" bundle and instead gets recreated completely.
Is there a way to restore the whole application instead of just a single activity? Or is this just a weird design and I shouldn't even bother with restoring one activity?
Kind regards,
jellyfish
Edit: ok, stupid me...
the bundle my main activity got was not null, but only in "onRestore...". In "onCreate" it was null indeed, but this was true all the time. (No matter if I came back from another activity after kill or not, for example)
So now I'm confused in another way: I have tested this before in another activity, and there, the savedInstanceState bundle of "onCreate" and "OnRestoreInstanceState" where the same! Is this just random or something special of the main activity? (Tried different launch modes as well, but they had no impact).

So, for example, I just realized, that restoring one activity via those two functions is pretty useless.
No, it is very useful, when used properly.
Because, if I press the back button and return to the activity before, this one doesn't get its "savedInstanceState" bundle and instead gets recreated completely.
No, it doesn't.
If it already exists and is on the back stack, it will be started (onStart()) and resumed (onResume()), but it is not created. If Android had to close the previous activity (e.g., due to a shortage of memory), the previous activity will be created (onCreate()) and will be passed a Bundle containing the data it populated in onSaveInstanceState().
The only way those statements would not be true is if you are monkeying with the BACK button processing.
Is there a way to restore the whole application instead of just a single activity?
No.
Or is this just a weird design and I shouldn't even bother with restoring one activity?
You most certainly should restore one activity.
onSaveInstanceState() is used for two scenarios:
If the user changes configuration (e.g., rotates the screen), your activity will destroyed and recreated. You use onSaveInstanceState() to pass data from the old activity instance to the new one.
The BACK button scenario I outlined above.
I have tested this before in another activity, and there, the savedInstanceState bundle of "onCreate" and "OnRestoreInstanceState" where the same!
Of course. They are supposed to be the same. If the activity is being created totally from scratch, onCreate() will be passed null and onRestoreInstanceState() will not be called. But if there is instance state, that state (Bundle) will be passed to both onCreate() and onRestoreInstanceState().
Is this just random or something special of the main activity?
Neither. They are supposed to be the same.

Related

Using onSaveInstanceState With Multiple Activities

I have three activities in my application.
From the first one i started the second, and from the second i started the third. When the process of my app is killed by the system and i launch it again i see only the last active activity is being created. And it seems the only one that gets Bundle object as a parameter to its onCreate method.
There are two things I am curious of:
If the activity at the top of the stack is the only one that gets its bundle, why each of my activities got their callback (onSaveInstanceState) called just before their onStop() method.
If only one of my activities can retain original state, what about the others? Did they lose all their state just because system decided to kill my app process? Should i restore them manually? What about views on them? (normally views get their state back without needing me to put something into the bundle and later restore if i am not mistaken)
The default behavior is this (tested on 4.1.1):
When you start a new activity, onSaveInstanceState of the previous activity is being called
When the system kills the app, the state of activities back-stack is being saved
When you restart the app, the last seen activity is being shown and its onRestoreInstanceState is being called
When you navigate back and pop activities from the back stack, state of each of them will be restored with a call to onRestoreInstanceState passing the bundle that was obtained from the first step above.
Therefore, the answers to your questions are:
All activities will be restored, but not at once - the last one seen is restored immediately, while the others will be restored when you navigate back.
All simple views (e.g. EditText) will automatically restore their state. In order for this to happen, you need to make sure that a) you did not override onSaveInstanceState or onRestoreInstanceState without call to super implementations b) the views that should be restored have unique IDs in view hierarchy
onStop is called on previous activity when you create a new activity, so you are saving the state! Your previous activity's onCreate will be called, if it's destroyed, so don't worry about recreating views. Are you sure app is killed? An app is not killed when it loses view. Android will keep it in memory as long as it doesn't need to free memory.

Saving the transient state DIY + Has the activity been called or recreated?

In my application, an activity A calls the activity B (via an explicit intent). Lifecycle, B may get killed and recreated etc.
When B is called (from A) it initializes some things. But when recreated it needs to just pick up from where it left. It will finish() some time, then I'm back to A, which might later call B again...
B needs to save a very small amount of data, usually just a few int values. (The rest that I need to restore it is in getIntent() which seems to be still there after the activity has been killed and recreated.) I've heard that onSaveInstanceState and onRestoreInstanceState are expensive and that they are not guaranteed. Plus, I don't need to save the state of views. (For that reason, should I override these two methods with blank ones, preventing the parent ones to get called?)
What's the efficient way to store just a few int values? Should I store the values as static fields of the activity B itself, or of the application itself, or of another class written just for that?
Also, how does the activity B know whether it's been recreated rather than called?
I've heard that onSaveInstanceState and onRestoreInstanceState are expensive and that they are not guaranteed.
They are not particularly expensive. And, for your scenario, they will be used most of the time. The exception would be if your task is not being restarted, such as:
the user got rid of your task by swiping it off the recent-tasks list
at least pre-Android 5.0, your app has not be run in ages and falls off the end of the recent-tasks list
the user force-stops you from Settings
But in those cases you will not be restarting at B, since I assume B is not your launcher activity.
For that reason, should I override these two methods with blank ones, preventing the parent ones to get called?
Not unless you are experiencing actual problems, not just rumors of hints of potential problems.
What's the efficient way to store just a few int values?
For your case, probably use the saved instance state Bundle.
If you cannot afford to lose those values even if your task goes away (and you will not be restarting at anyway), store them in a file, database, or SharedPreferences.
Should I store the values as static fields of the activity B itself, or of the application itself, or of another class written just for that?
No, because none of those will work. You specifically state that your concern is "Lifecycle, B may get killed and recreated etc.". Part of that lifecycle is that your process may be terminated, and in that case, static data members go "poof".
Also, how does the activity B know whether it's been recreated rather than called?
See if the saved instance state Bundle passed into onCreate() is not null. Or, see if a retained fragment exists, if you're using one of those.

Keeping object's state when user presses "Back" button

I have an app in which I programmatically create an EditText view. I assign an ID to that view using setId()
myEditText.setId(100);
so that Android automatically saves that object's state when pausing/stopping the app (as I was advised to do here). It works in these cases:
(1) When I leave the app using the "Home" button: if I then come back to the app, the object's state (displayed text) is restored, as expected.
(2) On a screen orientation change (which involves Android automatically destroying the activity and restoring it through a Bundle). The object state is also kept.
However, it doesn't work in this case:
(3) When I leave the app using the "Back" button: if I then come back to the app, the EditText object is empty.
Any explanation as to why this happens? Does Android really distinguish between leaving the app with "Home" and with "Back"? According to the documentation, the object's state should be automatically preserved, through a Bundle, even when the activity is destroyed. And that clearly happens in case (2). But not in case (3)!
If this is normal behaviour, how could I have the app's state automatically saved and restored when the user presses "Back"? I know I could use the SharedPreferences for that, but I'd rather have Android do that automatically, just as it does in cases (1) and (2).
This happens at least in Android 4.0 and 4.2 (I haven't tested others).
You really should study activity life cycles as there are many many ways to solve the problem. Since your activity is typically pulled off of the stack and destroyed when you navigate back one quick but not necessarily the best way is to make sure your activity flagged as singleTop or singleInstance in the manifest that way it is not pulled off of the stack and recreated when you navigate back and forth. You could also use the singleton Application class. Or pass the text back and forth as params. Or use a database. Or use MVC or some other programming paradigm that will allow your views to be destroyed and recreated with out the data populating them going with it. Lots of "or's". Study activity life cycles and then look at how you have your application architecture setup already and choose the method that will work best for you.
http://developer.android.com/training/basics/activity-lifecycle/index.html
http://developer.android.com/guide/components/tasks-and-back-stack.html
I think I found the explanation. I only needed to read the doc more carefully (thanks to #lentz for one of the links); see here and here:
When your activity is destroyed because the user presses Back or the activity finishes itself, the system's concept of that Activity instance is gone forever because the behavior indicates the activity is no longer needed.
If the user presses the Back button, the current activity is popped from the stack and destroyed. The previous activity in the stack is resumed. When an activity is destroyed, the system does not retain the activity's state.
The above explains behaviour (3) in my question.
However, if the system destroys the activity due to system constraints (rather than normal app behavior), then although the actual Activity instance is gone, the system remembers that it existed such that if the user navigates back to it, the system creates a new instance of the activity using a set of saved data that describes the state of the activity when it was destroyed. The saved data that the system uses to restore the previous state is called the "instance state" and is a collection of key-value pairs stored in a Bundle object.
The above probably explains behaviour (1) and (2).
What I don't see is why the user pressing Back should be interpreted as "the activity is no longer needed" ("its state needs not be preserved"). But that's a different matter.

Pausing/Resuming Activity outside of an activity

I have 2 activities. Main Activity A & Activity B
I do not want Activity A to destroy. I am starting Activity B in a new task.
public static void startActivity(Class<?> startClass) {
Intent intent = new Intent(Constants.getActivity(), startClass);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Constants.getActivity().startActivity(intent);
}`
Constants.getActivity() returns the Context on current activity
startClass is the either activity "A" or activity "B"
Thing is they create/destroy the activities and they leak. Am I doing it wrong? How can I start activity "B" from activity "A" and vice versa keep them both in background when I dont need them.
First of all, what are you trying to do? You should always separate things you want to do in the background from your UI. Think of your Activities are simply a container to show the UI, everything else can be stored and restored from persistent storage or savedinstance bundles.
It is very crucial that you understand the difference between Activity lifecycle and Service lifecycle here.
I'm going to refer to my answer from another question here:
Your app will be in the background, onResume and onPause will both be called, provided that the OS have enough memory to keep the new app and all the old apps.
If you have a long running process that you need while the user not looking at it, use a service.
If you need the user to return the app in the same state, you need to do the work in onResume and onPause to save the state information and initialize your UI again when the user comes back. If you are really worried that it can get killed (well, it shouldn't lose the bundle I think), you can store them in SharePreferences for your app.
If you want to know when the app returns from that specific share intent, use startActivityForResult
You cannot keep an activity "alive" as you said. When an activity is paused, Android system is always able to claim its memory and unload it, at any time.
But if you want to switch from A to B, then maybe A and B can be different views inside a single activity. Maybe you'll want to have a look at http://developer.android.com/reference/android/widget/ViewFlipper.html
When you use tasks, cleanup is very important. You need to cleanup all tasks in the activity. readthis
If the activity does not have a lot of crazy initialization, just use finish and onCreates. Else be aware that onResume will be called often as you switch between activity's. Cleanup will be crucial here. If you dont cleanup, its possible one of your activities (with dead object reference from the other cleaned up activity) may come back up from the activity stack and throw exceptions. Its very difficult to debug this kinda exception.

does object die navigating between activities?

I have an application that navigates to the same activity but each time the activity loads with different parameters. In my application it's a parsed data content retrieved from url. First thing I want to ask: When I push the backbutton of my device I get my earlier activity without being recreated. Is the objects in this activities alive and can I reference them?
Second question is if my first question doesn't make sense, what do you advice me to do?
If you look at the Activity life cycle, you'll notice that as long as your phone has enough memory, your first activity is kept in memory, and with it any member with the data it contains.
But if your phone needs to have some memory, it can kill any activity kept in background (any activity but the one being shown to the user), which means that you'll loose any data that was in your first activity.
To know which happened, keep in mind that the onResume() method will always be called when your activity is brought to foreground (either on creation, or when you navigate back to it), but onCreate() will be called only when your application is created from scratch (meaning you don't have any saved data).
You should use the bundle mechanism to save data when your activity is paused, and load it when you come back to it. Read the paragraph about Saving Activity state in Android doc to see how to use this.
You are not guaranteed that in memory data will be around once you leave an Activity. Read through this part of the dev guide thoroughly to understand the lifecycle of an Activity: http://developer.android.com/guide/topics/fundamentals/activities.html
If you need to persist information, you should override the onPause, onStop, and/or onDestroy methods of your Activity. You can then save your state using SharedPreferences, the SQLite database, or even a flat file.
In the manifest file add the following to the activity:
android:launchMode="singleTop"
In your example what is happening is when you navigate back to the activity using the back button you are bringing up the activity from the stack. When you navigate to the activity inside of the app what is happening is a NEW activity is being created, while the original is still on the stack. The singleTop launch mode will pop the activity out of the stack if it is there when you navigate to it in the app, and only create a new activity if it is not on the stack.
Without singleTop each time you launch the activity in the app it will create a new instance and you will find there are times you have to hit the back button on the same activity more than once due to multiple instances.

Categories

Resources