there is my situation. I have same activities which goes one by another, no matter what they are doing. Lets name them from the start of alphabet. When users uses my application, he goes through activities and makes his own path between them, so he could with back button go back in respecitve back order.
He starts with act. A - D - F and with back button he goes back as from F to D and A. Ok. Now, when Android system resolves the application is no longer in use or needs lot of RAM in some particular time, system kills it. My goal is to find, how to restore application to its former state including order of opened activities?
It might not be clear, so here is the example:
User has open activities A (login) - D - F - G, minimize it, after some time, app is killed. When he start this application again, he needs to login at activity A and than he has to see activity G (= he was there last time), and when he push back button, he will go do activity F, then activity D and so on... Is like revieving an row of activites. I know I have to persist all the information stored in my activities (D, F, G), but is it acutally possible to persist app state like that?
Thanks for any comment on this
Solution:
I am tracking flag, which identifies the state my application is in. If it is s 0, it means I am opening new activity normally. On start of each activity I put into shared preferences string, which contains all my activity history. Each activity has it's own id (again sharedPref). In another shared pref I am saving as a String formular data (or data with GUI), when onPause occurs. I set flag as a 1. When app starts and flag is 1, I revive application stack from sharedPref. Set data for each of them from another Shared Pref. And that's it, application state is revived :-)
You can persist anything you need to, it just a matter of how and what is going to be beneficial. There are multiple techniques that have been used to persist state over the years. Nearly all of them are available to you, but will require careful management on your part. Depending on what your application does, there may be special tricks available to you, as well.
Step 1
Determine what each Activity needs in order to run effectively. Determine what you can recalculate and what you absolutely should not recalculate. For instance, if one of your Activities is a Cursor Adapter of some kind and works according to a key to a table, you don't need to persist the entire Activity, you simply need to hold onto whichever _id relates to that particular Activity run.
Step 2
Since you are wanting to track Activity history, you will need some representation of that history. What you are proposing is a stack model, so you will want to write your own stack object and find an easy way to identify each activity in that stack. Do not try and save the actual Activity references as this will invariably lead to leaks. You can then save this stack to a database, shared preferences, file or even parcel it to a bundle. Integer constants that identify each Activity might be one way to accomplish this.
Step 3
Decide on your method of save, and build the appropriate save and load methods for your stack and each Activity.
Step 4
Override the Back button to retrieve the top Activity identifier and its appropriate data on the stack. (As a note: your stack might be better placed in an extended Application) Then start the next Activity with its required data.
Step 5
When your "login" Activity (or Application) starts, load the stack. When authentication completes, reload the top Activity on the stack, passing its required data through Intent Extras. You don't have to open ALL of the Activities at once, just the ones that the user is on.
Step 6
In your onCreate or onWindowAttached for each Activity, have it add itself to the stack. In your onDestroy for each Activity, have it remove itself from the stack. Since you are persisting your data, you can easily finish() to indicate that the Activity is complete.
Step 7
In your onPause for each Activity have it save the state that you feel is important. You can even save the scroll position and just have it rescroll when the Activity restarts. In your onCreate have it regain its state via the extras that you supplied.
It is really as simple as all of that. If you need some samples, I can gladly provide.
FuzzicalLogic
Assuming all you need to do is reconstruct the path of activities from A to Z (or whatever), you don't need to make things too complicated. if you want to do it the right way, do the suggestion by Fuzzical Logic. if you want to get it running quickly and complicate things after that, you can start with this simple method.
Basically, you map each activity to a code, and maintain a simple text file. Each time an activity is invoked, it should append it's code to the text file. So you're really just writing to a file exactly what you explained in your question. In your example, you'd have "ADFG" in a text file.
When you exit an activity and go back, just read the file, chop off the last letter, and write it out. In your example, if you had "ADFG", pressed back, the file would now contain "ADF".
When your app starts, simply read the file and for each character, create the associated activity as you would normally. Read the file once and pass the string to each activity as it is created. So the root activity would read "A" and start that activity (passing the string "DFG" in the bundle), then that activity would read the next character and start the D activity (passing "FG"), and so on until the last activity sees that there's no characters left in the string.
Once that's all working, you can worry about how to store state information for each activity. Fuzzy's solution is by far the most elegant, but elegant and ASAP don't usually mix, so it's your call. I'd separate the stack data from the state data for each item in the stack. It's just easier that way IMO.
Hope that helps!
Related
I hope you can help me understanding the Android Lifecycle and how I can manage navigation between several kinds if activities. Suppose the following scenario:
Activity A "MainMenu" is started: User clicks on a menuentry
Activity B is started and UI is loaded with content from local database
user interacts with Activity B
user presses Backbutton and returns to Activity A
user presses again the same menuentry for returning to activity B
At the moment: The activity is loaded from scratch
Whished: The previous instance of Activity B with all its UI entries and User interactions should resume
I thought of saving all activities in a static manager. If a activity is started, the manager looks if it has already been started previously. If so, resume the previous activity else startActivity(ActivityA). Is that a good way of handling those navigations? How can I directly resume via code an instance of an activity?
Or is there another way to do this in a better way?
Thanks in advance!
greetings,
faiko
Your not going to be able to "cache" an activity once its off the stack. Activities are very quick to start, Im guessing its your database stuff that is slowing you down. That you could do something about.
In theory, if you knew the user was going to click on entries multiple times going from A -> B, you might want to just load the DB stuff into memory in Activity A, and then send it via Parcel into B. Then when you go back to A, its still in memory, and it will be to go back to B instantly.
If you are unsure of whether this is true, try debugging it and stepping through. You will see the activity is launched fairly quickly, but you need to identify which operations are lagging. From your description above it is probably your database calls, but it could be other things like network calls, or other long standing calls
The life cycle of android activities makes it not possible to start a new intent but keep the old state.
You should save the state of the activity by saving the fields and restore it to this state when needed.
You cannot resume an instance of an activity in the scenario that you have posted. Because due to the android lifecycle, when you press the back button on activity B that activity is destroyed, with all its states and information.
One option to get the information to persist is to extend the Application class and save the information into some variables inside the application class. Although alot of what you do even then can be garbage collected if the system needs the memory. I dont believe there is any good way to have that information persist through the activities even using Jameo's solution, if activity A is killed then if you return to Activity A from activity B then that activity is reloaded and the database access has to happen again.
I've got four activities which the application cycles through. The first one fetches huge amount of data, so I don't wish to do that more than once. Thing is that if user presses back key on last activity, I want to return to the first one without reloading the activity. I'm currently thinking startActivityForResult methods and finish the two previous ones, but there may be a better solution?
Scenario:
Hasslarn,
The first thing that you must understand is that you have very little choice as to whether an Activity will be reloaded or not. That is determined by the system (largely). With that said, there are a number of things you can do to limit the system's desire to kill the Activity. Additionally, you may use a number of tools at your disposal to limit the impact of such a possible closure
Finish every child Activity as it becomes unimportant. This will free resources lowering the need to get rid of unused Activities (even temporarily).
Find a simple, but clever way to limit loads.
Based on the information provided, your proposed solution is a viable way to accomplish both. However, I would ask "Is Activity B required to be active while Activity C is open?" If not, you may want to do the following:
startActivityForResult(Activity B)
When Activity B is done, send result back to Activity A and startActivityForResult(Activity C)
Finally, when Activity C is done, you may startActivityForResult(Activity D) and back will work with no effort and you won't have to close the other activities.
Furthermore, if you press Back on Activity C, you can supply a cancelled result to Activity A forcing a restart of Activity B if you so desire. Depending on the required processing for Activity B, this may not be any hassle at all for your app.
Regardless of how you approach this, I recommend finding a way to cache this data such that onResume(), you may reload quickly if needed. This is because there is NO way to ensure that your original Activity will not be released to make way for the others.
Hope this helps,
FuzzicalLogic
Try using this:
Intent a = new Intent(this,A.class);
a.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(a);
return true;
Activity A will get reordered to front, without creating a new instance. If you want to pass extras through intent , you can get the intent extras in onNewIntent(intent) in Activity A.
At the moment I'm a little bit confused about the lifecycle management in Android. There are at least 4 possibilities to resume retained data after an Activity comes back to the foreground:
Android handling: If there is enough memory, Android stores and resumes the important data (checked radio buttons, text of EditText,-... and so on) after Activity restart, the user has the same state as before as the Activity went into background.
onPause, onResume: Overriding onPause and save the important data to a database or text file and resume it when onResume is executed next time.
onSavedInstance(Bundle), onRestoreInstance(Bundle): I can save the data as key-value-pair into bundles and restore them after onRestoreInstance is executed.
onRetainNonConfigurationInstance(), getLastNonConfigurationInstance(): I handle all my storage issues in one big object and read getLastNonConfigurationInstance() out when onCreate is executed.
Although it is confusing which approach is best, I guess it relies on development experience to know when to use which possibility. If you have some good examples for each I would be glad, but this is not my question. I wonder how to deal with all that when I have different Activities and one Activity will be killed by Android when it pauses in background:
In my case I have a MainActivity and a MessageActivity. The MessageActivity consists of a ViewSwitcher which consists of two states. State one is a radio button choice list. State two is an EditText with two buttons (send and abort). When I monkey test each state, hit the Android home button, and restart the application, the right Activity with the right state and the old data comes into foreground, when I leave the handling to Android. So that works.
But what happens when Android destroys the MessageActivity in background:
If I use the Android way, the data is lost and I guess MainActivity (instead of MessageActivity->state(1 or 2)) will start next time after I relaunch the application (is that correct?). So when I'd like to keep the data of MessageActivity, I have to use one of the other three possibilities.
How to do that neatly, when the application entry point (so the MainActivity) differs from the last active Activity. The problem is that I have to resume a special Activity with a special state of ViewSwitcher. I could start MessageActivity out of MainActivity with startActivity(Intent) in onStart() or onResume() method (because MainActivity is probably the entry point) but then I run into a lot of logical problems in Lifecycle management. Due to this fact I don't think that this is the right way to do that.
But, what's the right and best way to do that?
I guess MainActivity (instead of MessageActivity->state(1 or 2)) will start next time after I relaunch the application (is that correct?)
No, I don't believe this is correct, depending on what your code does in onCreate(). It certainly doesn't need to be correct if you go about things the right way. A simple way to test this is to rotate your screen, which recreates the running activities, unless you have overridden the default configuration change behaviour.
I recommend reading this section in the android docs carefully:
http://developer.android.com/guide/topics/fundamentals/activities.html#SavingActivityState
In particular:
even if you do nothing and do not implement onSaveInstanceState(), some of the activity state is restored by the Activity class's default implementation of onSaveInstanceState(). Specifically, the default implementation calls onSaveInstanceState() for every View in the layout, which allows each view to provide information about itself that should be saved. Almost every widget in the Android framework implements this method as appropriate, such that any visible changes to the UI are automatically saved and restored when your activity is recreated. For example, the EditText widget saves any text entered by the user and the CheckBox widget saves whether it's checked or not. The only work required by you is to provide a unique ID (with the android:id attribute) for each widget you want to save its state. If a widget does not have an ID, then it cannot save its state.
What this means is, that so long as you don't force any UI state in any onCreate() calls, your activity stack and UI state will be restored.
Personally, my preferred approach is to keep as little state as possible in member variables of my activities, saving and restoring it with onSave/RestoreInstanceState(), and relying on the default implementations to save the rest of the UI state (text box contents, etc). Data that should persist between sessions I commit straight to my DB or preferences as soon as it's changed (e.g. in the on-click handler). This means I don't need to worry about the activity lifecycle for that. As much as possible, my UI just presents a view of the data in my DB (using CursorAdapter etc.).
Edit:
Regarding restoration of the whole activity stack:
When the user leaves a task by pressing the HOME key, ... The system retains the state of every activity in the task. If the user later resumes the task by selecting the launcher icon that began the task, the task comes to the foreground and resumes the activity at the top of the stack.
(See http://developer.android.com/guide/topics/fundamentals/tasks-and-back-stack.html)
It's not my attempt for a best answer, but it's too long to get in the comments section.
First I will suggest not to rely on the "Android way" - this will result in inconsistent application behavior depending on the free memory of the device - bad practice.
My suggestion is to save your state-dependent data in key-value pairs in SharedPreferences, every time you go into onPause() in your MessageActivity. Store a flag in SharedPreferences, which indicates which was the Activity that was last opened (if you only have two Activities you can easily go 0/1 or true/false flags).
When you re-launch your application, it's normal to start the Activity marked in your AndroidManifest.xml as "entry point". So naturally you'll check the flag in onResume() in your MainActivity and start the other Activity if needed. In MessageActivity's onResume() check the values in SharedPreferences and fill in what's necessary...
If your application is "resumed" to the last Activity in the ActivityStack this will call onResume() in the last Activity in the ActivityStack.
The way I have handled an issue like this in the past, is to have a service running in the background, which handles the flow of information from different activities via either Intents and listeners (preferable, since they are the most easily decoupled solution), or if you are extremely careful, and the only viable solution for some reason is to store the data through direct property access or method calls, you can use static properties/methods on the service class as well. However, I would strongly recommend using the Intent/listener method as it is generally more flexible, thread safe, and decoupled. Additionally, it is wise to make sure that not much is happening at any point in time (in other words, only use this service for Intent handling) when it's not needed, otherwise the Service will tend to hog CPU time as well as RAM, when it's not really needed.
Some resources to look at when it comes to this approach would be IntentService and its related classes, including the superclass, Service. IntentService, however, it is worth noting handles a few more things about async Intent processing, etc that Service does not automatically come with.Hope this helps you!
login.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
String name=username.getText().toString();
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
SharedPreferences.Editor editor = settings.edit();
editor.putString("username", name);
if(name.equals("xxx")) {
Intent intent=new Intent(currentactivity.this,nextactivity.class);
intent.putExtras(bundle);
startActivityForResult(intent,0);
}
}
});
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.
I have a reservation wizard in android, when i say wizard i mean i have multiple activities (meaning screens) that are passed from one to another until booking is complete. in each step of the way my info may be invalid and so ill have to back track 2-3 activities and start all over, all the activities i back tracked should be destroyed, they may need to invalidate a singleton data container that they filled with their data.
I thought of 2 ways to do this:
start all activities related to the wizard with startActivityForResult() so i get notification when an activity is finished and with which error code (finished OK or error occurred) and act accordingly (for instance: if in the middle an activity fails it will return failed and the previous one will return failed as well until i get to the wizard's first activity where i either declare the error with a dialog or show the 'thank you' screen.)
I can send an react on intents with certain parameters so i have a 'close and clean' intent message that close down the previous activity etc...
which way is better, any other interesting\efficient way to do this ?
I Got a hint that got me thinking in a different direction:
use an invisible Activity, or alternatively , a local service,that is the 'brains' that known on each turn of the way what action is to be taken. to remove a complete stack of unneeded activities i should use the FLAG_ACTIVITY_CLEAR_TOP on the bottom most activity i want to use next.
Another options is to enclose all
the activities i want to play with
their pposition on the stack inside
an ActivityGroup Object which keeps
all activities alive (like in Tab)
and on the stack, but you control
which one is displayed and which is
not. the activity group will contain
the state machine logic regarding
which Actvity should not be
displayed and which should be
disposed.