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).
Related
I'm making a simple e-book reader app, and an activity can be called by many cases.
I'd like to distinguish callee activity to know its origin action in case of
From my another activity: this can be easily solved by
StartActivityForResult from calling activity.
Called by back button click from other package app after share action ("whoops, I missed to click share button, and then back.").
Switched by user's multitasking choice.
Called by user click at the start screen: this might be known by MAIN entry point at the android manifest.
How to know above cases?
I have no idea why you would need to do this but...
1.From my another activity: this can be easily solved by StartActivityForResult from calling activity.
Yes, as long as the calling Activity is your own as you can't guarantee any 3rd-party code will use startActivityForResult(...). You can, however, use getCallingPackage() and getCallingActivity() in other cases.
2.Called by back button click from other package app after share action ("whoops, I missed to click share button, and then back.").
When the user presses the BACK button your Activity isn't being "called" - it's simply being resumed or re-started. The original calling app/Activity/method will still hold true - there is no way to ascertain that this has happened as the normal Activity life-cycle methods (onStart() and onResume()) are always called even when an Activity is first created.
3.Switched by user's multitasking choice.
If you mean using the "Recent" apps view, the same applies for my answer to 2. above.
4.Called by user click at the start screen: this might be known by MAIN entry point at the android manifest.
In this case onCreate() will be called although if your Activity is simply stopped for whatever reason, it may simply be restarted depending on the launch mode you use.
In short, I can't see you being able to gather much in the way of any accurate information as to how your Activity becomes active.
I am not too sure about the actual way for the above question as I am too a new guy in android.
But to the best of my knowledge... called by back button and switched by user's multitasking leads the activity to enter pause state.
So you can access it from "onPause" method in your activity.
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.
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.
What I have:
I have 3 activities in my app with a basic nested structure:
Activity 1 --> Activity 2 --> Activity 3
Activity 2 contains data embedded in an ArrayList<CustomClass>. CustomClass extends application (as taught here) and Activity 3 uses this to modify the CustomClass data for Activity 2.
What I want:
When the user hits the Home button and then opens my App again later, I want to return to whichever activity was on the top of my stack. (I thought this is supposed to be Android default behaviour.)
It works fine on my Eclipse emulator but on both my Google Nexus One and Huawei Honour, the task is completely restarted.
I have tried setting my activities to run in standard, singleTop and singleTask launch modes. The best result I could get on the phones was to return to Activity 1 (which is just a form) and still display what I had entered into the text fields before creating Activity 2.
I did also notice that if I hit Home then check the Running Applications under Settings, my app is not listed. Maybe Android might be destroying my task and I need to save the activity state. But this doesn't make sense if I press home and immediately re-enter the app?
Advice is appreciated.
There is no guarantee that once you hit the Home button and your Activity goes in onPause() it will keep its state (even it it keeps the same state, there is no guarantee on the actual amount of time the activity will remain in memory). This depends on the device in particular and how it handles its internal memory. (see this figure which shows the Android Activity Lifecycle).
The best practice is to always save your state in the onSaveInstanceState method. This post provides a good and easy example on how to do that.
Now that I have researched this even more I am rewriting this to make it clearer. If you are looking for more info, there is some available in older edits.
What is happening:
(This refers to an application that has not set any launchMode
settings and so is using the defaults)
You launch an app from the Market or from the Installer. This
launches the root/main activity of the application with the
FLAG_ACTIVITY_NEW_TASK flag and no categories. Right now the
applications stack is [ A ]
Then you proceed to the next activity in the application. Now the
stack in this task is [ A > B ]
Then you press the home key and then relaunch the same application
by pressing it's icon from either the home screen or the app tray.
What is expected at this point is that activity B will show, since
that is where you left off. However A is shown and the tasks stack is
[ A > B > A ] This second instance of A is launched with the
following flags: FLAG_ACTIVITY_NEW_TASK,
FLAG_ACTIVITY_RESET_IF_NEEDED, and FLAG_ACTIVITY_BROUGHT_TO_FRONT. It
also has the android.intent.category.LAUNCHER category.
At this point, if you hit the back key, it will return you to B, as it
was when you left it.
Looking at the documentation it seems as if
FLAG_ACTIVITY_BROUGHT_TO_FRONT should only be set for activities that
use the singleTask or singleTop launchModes. However, this
application has not set any launchModes and is therefore using the
default standard launchMode.
I assume this is not suppose to happen in this case?
I should also note, that once it gets into this weird state, then it happens everytime the app is launched from the home screen or app tray. If the task is finished (restarting the
phone, force stopping the app, or hitting back all the way through the
stack) will fix this issue and will no longer launch it incorrectly.
It only happens if you launch the app from the installer or market and
then try to launch it from the launcher.
So in summary, why is this happening? Is there a way to prevent it?
Here is a workaround I have come up with so far. Some other workarounds I have seen involved looking at the currently running tasks. However, I really did not want to have to ask for another permission (GET_TASKS) from the user just to make a work around.
Please let me know if you see any holes in this.
In the main/root activity's onCreate method, check if the intent has
the FLAG_ACTIVITY_BROUGHT_TO_FRONT set and if so, call finish(). This
then pops the extra instance of A off the stack [ A > B > A ] becomes
[ A > B ] and from the users perspective, it launches into the
activity they were expecting.
It seems to work in all of my tests so far. My only worry is that if
there is some weird case where someones launcher would always flag a
launch with FLAG_ACTIVITY_BROUGHT_TO_FRONT even if the app wasn't
already in a task, and therefore would completely lock them out
because it would call finish() and not have anything in the stack to
return to.
--
As requested in the comments here is how you can check if an intent a specific flag:
int flags = intent.getFlags();
boolean hasFlag = flags & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT == Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT;
--
Also I should note that I am still seeing this problem occur sometimes with this fix in place. It doesn't seem to be a perfect solution.
Overriding of onConfigurationChanged() should help you to retain the state.
http://developer.android.com/reference/android/app/Activity.html
Configuration Changes
If the configuration of the device (as defined by the Resources.Configuration class) changes, then anything displaying a user interface will need to update to match that configuration. Because Activity is the primary mechanism for interacting with the user, it includes special support for handling configuration changes.
Unless you specify otherwise, a configuration change (such as a change in screen orientation, language, input devices, etc) will cause your current activity to be destroyed, going through the normal activity lifecycle process of onPause(), onStop(), and onDestroy() as appropriate. If the activity had been in the foreground or visible to the user, once onDestroy() is called in that instance then a new instance of the activity will be created, with whatever savedInstanceState the previous instance had generated from onSaveInstanceState(Bundle).
This is done because any application resource, including layout files, can change based on any configuration value. Thus the only safe way to handle a configuration change is to re-retrieve all resources, including layouts, drawables, and strings. Because activities must already know how to save their state and re-create themselves from that state, this is a convenient way to have an activity restart itself with a new configuration.
In some special cases, you may want to bypass restarting of your activity based on one or more types of configuration changes. This is done with the android:configChanges attribute in its manifest. For any types of configuration changes you say that you handle there, you will receive a call to your current activity's onConfigurationChanged(Configuration) method instead of being restarted. If a configuration change involves any that you do not handle, however, the activity will still be restarted and onConfigurationChanged(Configuration) will not be called."