In Android, how can I detect why my activity was resumed? - android

I'm developing an Activity that does some of its own state management. I'm trying to differentiate the following onResume cases:
New launch
task switch (home button long-click)
resume after other activity in the same application
wake-up after sleep
orientation change
Is there something in the Activity's intent, or elsewhere, that can help me differentiate these?
For the curious and some context... I'd like to preserve my internal history stack on 4 & 5. On cases 2 & 3, I would preserve the same current page, but erase the history (allow the normal back button functionality to take over at that point). Case 1 would initialize to the activity's internal start page (and can be detected easily enough with some help from onCreate).

Is there something in the Activity's intent, or elsewhere, that can help me differentiate these?
Item #4 has nothing to do with onResume(), AFAIK.
Item #5 would be better handled via android:configChanges and onConfigurationChange() though you could "detect" it by returning something from onRetainNonConfigurationInstance() and seeing if it is there in onResume() via getLastNonConfigurationInstance().
The others aren't just three cases, but probably twice that, once you start taking into account things like "being kicked out of memory to free up RAM" as a possibility.
Off the cuff, it feels like you made some unfortunate architectural decisions ("internal history stack...erase the history...allow the normal back button functionality to take over at that point"). Android is designed around lots of cheap activities, and you appear to be violating that precept. You are welcome to do so, but bear in mind that Android support for your chosen pattern may be limited.

Related

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.

Lifecycle when pausing AND rotating

I've got an app that is complete, apart from the lifecycle that, guiltily, I left as last.
Things seem to work good in all cases, but one:
When I pause the app by pressing the power button, it correctly pauses and restarts when resuming.
When I rotate the screen it correctly resizes.
When I pause AND THEN rotate the screen, its behaviour is different and the app resets.
Now, obviously it's a problem of my internal code, but to help me in the debugging, I would like to understand what's the difference between the three lifecycles. Or better, if the THIRD example is some how different from the first two.
With debugging I saw that all of three do this (I resemble by mind, I hope to be right):
onSaveInstanceState()
onPause()
onStop()
onStart()
onResume()
Is it right? Or do I miss some lifecycle steps in the third example, above?
Thank you.
Please specify what you mean by the app resets.
When you press the power button, most likely your app stops. That's why you see onStart() being called after stopping (not: pausing) your app.
Given that, is it really the case that the third scenario you describe is a combination of the first two? In other words, how do you pause your app in the third scenario?
Update Regarding your comment: There is a difference between your Activity being stopped and your Activity being destroyed.
Rotation is a configuration change. In this scenario, your Activity is going to be destroyed and re-created. You can handle this case separately if you must (it depends) through the onRetainNonConfigurationInstance() callback and the getLastNonConfigurationInstance() method. Android will call onPause(), onStop(), onRetainNonConfigurationInstance() and onDestroy() in this order, and then continue with onCreate() etc and it will not handle any events in the meantime so that they won't get lost.
Most likely, some of your code is located in the wrong callback, such that in the third scenario, something does not get initialized/attached/... This is really hard to tell without the code and the exception (if it is an exception).
I suggest you proceed as follows.
Complete your understanding of the lifecycle and be precise (creation, start, restart, resume, pause, stop, destroy)
Complete your code to override the other lifecycle callbacks as well with log messages (don't forget to call the super class' callback) and analyze pause, stop, rotation and combinations
Identify the point of the crash
Analyze the life cycle of the objects involvedd in the crash in conjunction with the Activity lifecycle
Or you can post some code and the exception here, of course.
Update 2 I have made a test on an AVD. I have observed the following:
When the AVD is in landscape orientation, and I press the power button, my app receives a configuration change to portrait orientation (presumably because the portrait-orientation-only lock screen takes over). Pressing power again and unlocking the AVD, the app receives a configuration change again back to landscape orientation.
While not solving your issue, it goes to show that code must be carefully placed in the respective callbacks, because in the case above, while being re-created, the Activity will still not be shown.
Drop me a comment if you update your question.
Hi you can check the documentation regarding Managing the Activity life cycles in android .
When orientation changes Activity is re-created.
Add android:configChanges="keyboardHidden|orientation" to your activity in the manifest.

Listeners and switching activities within an application

I have a multi-activity application. Say I set up a listener for some type of event in activity A, but then switch to a different activity B (within the same app) before the event has triggered the listener. What is the status of that listener? Does it always get destroyed? Or does it depend on the type of event? Or does it depend on whether the listener was set up within the main UI thread of activity A? Or some other conditions?
EDIT: The reason I ask is that I wish to interrogate the purchase states of a variety of in-app purchase items right at the start of my app's splash screen. This involves instigating some code and setting up a listener for "ok_here_is_the_answer()".. the problem is I'm worried that it may take longer to get the answer than the duration of the splash-screen activity. In that case do I have to start all over again in my application's second activity?
If your listener is created within Activity A and is tight to its context, then it would be destroyed when the activity pause i.e. go to the background.
If you want to do operation that should survive across activities, you can define it within the application context or in a dedicated service.
This might not be the answer to your question but you shouldn't use a splash activity (or even a splash) for many good reasons. I'd recommend you to use a full screen dialog instead which also would solve your problem.
But about your question it depend on what kind of listener we are talking about? Anything involving the context is over and done. Handlers, threads etc. is still running though (afaik).

How to deal with retained data after Activity comes to foreground when using more than one Activity?

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);
}
}
});

Confused about activity lifecycle usage in the notepad example

I am confused about activity lifecycle usage in the notepad example,notepad example use "edit in place" user model,inserting new record in onCreate method,
saving persistent state in onPause method,and save away the original text in onSaveInstanceState method.
I am a J2EE programer,I can not understand the logic described above. why not make things simple as following:
1.Not inserting new record in onCreate method.
2.When user pressing BACK,it is equal as pressing save button in the editorform,so execute inserting or updating in onPause method if activity.isFinishing() is true.
don't persiste use data if activity.isFinishing() is not true.
3.Not save the original text in onSaveInstanceState method,It is no necessary.If the activity is killed and back,restore user inputing data in the editorForm is adequately.
I think this logic is more traditional and natural.
Maybe I not understand the essence of the activity lifecycle.Please air your's opinion.
Thanks
L.J.W
the lifecycle of an adroid app under various conditions (e.g. switching screens, freezing, stopping etc.) is described in an excellent video tutorial by google. You may also want to refer to the slides of that talk, in particular, slide 16ff may be of great interest for you.
In any case you are right in thinking that understanding the lifecycle of an android app is the key to coding for android.

Categories

Resources