In my App there is three activities: A, B, and C. Activity A is the main activity.
When I start Activity B from A using Intent, A is automatically destroyed. However, I am not finished with A when I start B. Then, when I press the back button in the device the A is started using onCreate and I've lost all my data since I am not using an Intent to go back.
The same happens when opening C from B.
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
startActivity(new Intent(ActivityA.this,
ActivityB.class));
}
});
What's going on? How do I fix this?
This was a mistake by me due to this the Activities were automatically destroying. I Checked the option under Setting=>Developer Option=>Don't Keep Activities. Due to that destroyed every activity as soon as the user leave it.
By Unchecked this option my app working fine.
If you are using 'Activity B' to get some sort of one-off result then it sounds like you need to be using the startActivityForResult construct. See [the docs](http://developer.android.com/reference/android/app/Activity.html#startActivityForResult(android.content.Intent, int))
However, to maintain state information about 'Activity A' without persisting it anywhere you want to be bundling all the information (fields the user has filled in etc.) into the Intent bundle passed to 'Activity B'. This bundle is delivered to 'Activity B's onCreate (and a few other lifecycle points) where you can continue populating any pertinent information from 'Activity B' and writing that information back to the intent bundle before restarting 'Activity A' with the modd'ed bundle when you are done with 'Activity B'. But here be dragons. This specific mechanism is employed in our app with a strict (wrong?) control over the back-stack. Specifically our 'Activity A' has the special manifest declaration of android:noHistory="true". This means that we also control what happens when the back button is pressed by overriding onBackPressed() at each activity and sending that same bundle through to a new instance of the activity we just came from. It looks like a normal back stack but since we are wanting to remember user input without persisting that information anywhere then it's a usable solution.
This allows for a wizard like input with each stage of the wizard input being it's own activity and only validated and correct information being persisted at the end.
An alternative would be to temporarily stored 'Activity A's data in the SharedPreferences area so that on restarting Activity A you can pull out the appropriate bits reassembling the original state of 'Actvity A' when you left it. Rest assured there is very little you can do when Android wants to kill your Activity A but you can accommodate it's very reasonable killing with the approaches outlined here.
Maybe it is not you that is destroying your activity.
From the page about Activity Life cycle: (Emphasis mine)
If an activity is paused or stopped, the system can drop the activity
from memory by either asking it to finish, or simply killing its
process. When it is displayed again to the user, it must be completely
restarted and restored to its previous state.
There explicit says that the system can kill your activity process.
Some possibilities is your device is low on memory, or your activities is memory hungry, making the system kill it.
Edit
In order to maintain your data, between the activities even if it gets destroyed. One way is to use the Application class, check this answer on Using the Android Application class to persist data, maybe that's what you're looking for.
Related
My application is launched using a tag, and based on the information contained in tag, it further proceeds. Now my app can also be started by using touching icon, and later it asks user to touch the tag. Small flow would be as below.
So MainActivity may contains tag data(if started from TagProcessorActivity), or may not contain data (if started from icon launch). Data is passed as intent extra value from TagProcessorActivity to IconLaunchActivity then to MainActivity. After main activity, app operation proceeds. When I leave the main activity, all my previous activities gets finish. I have checked onDestroy() is called for each activity. Now if I logout after MainActivity, (Logout simply a feature that closes all existing activity), and relaunch my application from recent app, my tag details still appears in MainActivity, which I dont know why.
To make is more clear my questions are:
1) Why activity which was destroyed still contains the information from previous launch.
2) I know about removeExtra() method, but is there some better options to tackle this problem.
3) and none the less, is there some thing wrong in my code or android is keeping that instance of intent extra?
PS: Not clear which piece of code to post, so if required feel free to ask for code.
Applications never exit in Android. onDestroy only destroys the activity, not any static variables left in the app. These will keep their value the next time an Activity is launched. This can be combined with some other features (like launching from the recent tasks menu causing you to launch the same intent) and this is the behavior you will get. The answer I always used was to detect this case (by checking the intent, there's a field that says if this is a restart or fresh), and ignoring the intent extras if so.
A finished task launched from Recents (as opposed to home screen launcher icon) will receive the old Intent, including its Extras, Data, etc. There is a way to know that it was launched from Recents so you can handle the Intent appropriately.
protected boolean wasLaunchedFromRecents() {
return (getIntent().getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY;
}
In my humble opinion, that flag is poorly named (other flags referencing the Recents list actually use that word, e.g. FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS, FLAG_ACTIVITY_RETAIN_IN_RECENTS) and the documentation was never updated to reflect the fact that many popular Android devices have a dedicated button for Recents:
This flag is not normally set by application code, but set for you by the system if this activity is being launched from history (longpress home key).
I ran into a similar issue with the recent list in that there seem to a handful of conditions where an app will be completely destroyed but some other process will launch the app cold from the last activity that was being used. Since my app has a state that is built up over several designated activities I need to prevent this (i.e. null refs from onCreate()).
Without checking for state is all my onCreate() functions is there a way to just prevent this?
Also, other than the launcher, recents, the back button from other apps - are there more conditions where another thing can launch my app if I have not giving manifest permission to launch it explicitly with an intent?
Thanks!
If I understand this correctly, you have initializations in activity B that rely on activity A having passed them in. If an intent launches activity B first, without it being active, or being launched by A first, your activity will crash.
Easiest solution I can come up with is make your activity A (I assume your main activity) the broadcast listener for all the intents you wish to handle, and based off the intent action, dispatch to the appropriate child activities (B, C, whatever). That way activity A does all your initialization, and you can still launch into the appropriate activity to handle the original intent you wanted to.
Alternatively. If you detect your children activities are in an invalid state, you could put initialization into a parent activity that all your activities extend from. That way you should be able to initialize properly if the activity is a fresh launch. I'm not really a fan of this, I prefer making sure my activities are dependency injected with appropriate data.
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.
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.