I have researched numerous posts regarding Activity back stacks, as well as the Android Developer website, but still can't find a solution to a problem I'm having.
Scenario:
I have Activity A, I navigate to Actvity B, from A and then press the back button to go to Activity A again:
Actvity A --> Activity B --> Actvity A
Nothing out of the ordinary..
Problem
When I press the back button to go to from Activity B --> Activity A, Activity B is not destroyed straight away, as expected it goes into a pause state and this is where I have strange problem. If I want to return to Activity B from Activity A and IF Activity B is still in a pause state all its life cycle methods are called when use startActivity(B) from Activity A:
Activity B - onCreate() > onPause > onStop > onDestroy <-- why is this happening
At this point, to me it shouldn't exist anymore, and I can't explain why it went through all its lifecycle methods, rather than just the start initialisation lifecycle methods. The fall out from this strange behaviour is that the Activity is still visible on screen but doesn't populate a RecyclerView which in first initialisation did so as expected. At this point if I press back Activity B enters a pause state again.
If Activity B is in a pause state (Activity A is at top of stack) and the framework ends Activity B through lifecycle callbacks and I navigate to Activity B again from A it works as expected (RecyclerView is populated), basically a fresh instance always works fine.
All I can assume, when referring to the Activity Lifecycle diagram, is that Activity B enters a pause state, however is destroyed without calling onStop, onDestroy etc.. meaning any Activity clean up operations I have in those callbacks aren't happening?
Things I've Tried
Changing various Intent filters, and combinations, when starting Activity B:
FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_SINGLE_TOP
Calling finish() when onBackPressed() is called in Activity B
Various other fingers crossed and hope changes, nothing seems to work.
Can anyone help please!
Ok, so I found the answer to the problem - not obvious at first, but now I understand what was happening.
The root cause of the problem was to do with 2 instances of the same Activity (Activity B) referencing the same Objects (Objects supplied from DI library).
Firstly when returning to Activity A from Activity B, Activity B was not immediately destroyed, and this causes a complication - this instance would never be reused, however still existed for a period of a few seconds. In this situation when using startActivity(B) from Activity A it would create a new instance and destroy the old (hence why I was seeing logging in both creation and activity ending callbacks), if it still existed. In this scenario both instances were sharing the same object, and this object "cleaned up" the Activity when destroyed. So the object (Presenter in this case) was being told by the old instance that it should clean up the Activity as its being destroyed, however this was not the case, because a new instance had been created.
The solution
Quite simple really, every time a new instance of Activity B was created, store in the Presenter a unique number (startId), and when Activity B called onDestroy() pass its current unique number, and check they match - if they don't match its not the latest instance, so do nothing. Very similar idea when you want to stop a Service, and check its the latest Activity calling the service from the startId.
Personally I don't know why Activities aren't destroyed straight away on pressing back, but thats the reason why this was happening.
Related
I create a child activity "B" from activity "A". if the user should leave the app for any reason (most likely hitting the home button), I would like activity "B" to end and the app to be at activity "A" once the user resumes.
If I call finish() manually, activity B ends and it returns to activity A. This is the behaviour I would like to happen when the user leaves the app.
I have tried to call finish() in the onPause(), onStop() and in the onUserLeavingHint() of activity B. In each case, this appears to work correctly, and I can see mParent.finishFromChild(this); being called inside activity B.
However, as soon as the user switches back to the app, the onCreate() of activity B gets called and the user ends up in activity B.
How can I ensure I end up in the parent activity when I call finish() from within an onStop() (or similar) handler?
UPDATE: It appears that the issue is related to activity B being declared as using a SingleInstance launch mode. Removing this feature seems to have resolved the issue. Changing this has introduced other issues that I have since managed to fix.
The reason for this happening is that Activity B is set as a SingleInstance Launch Mode. The reason it was set to this (by another developer) is somewhat related to the reason I had wished the activity was ended when the app is in the background - it was to ensure the user could not reach this activity by hitting back on any other activities subsequently dispatched from Activity B.
To resolve this. I first ensured no activities could be created from B. To instead return from B and pass any required Intents on to A. Simplifying the back stack. (Calling activity B with startActivityForResult() is one possible way of doing this.)
Now, the reason SingleInstance causes this issue to arise in this scenario, is because Activity B is launched in a seperate new task. When the user attempts to resume, they re-enter this single-activity task. The rest of the app is running in a seperate task. The only thing the task can reasonably be expected to do is relaunch the activity. When the user presses back, the only thing it can do from there is to close the task (and hence appear to exit the app). For the expected behaviour to occur the user would have had to have selected the other, first task (through a long click of the task list).
Hopefully this self-answer can help someone who has encountered a similar issue.
I wrote an Android app with several Activities and a Main Activity. When I go from the Main Activity to lets say Activity B, I want to "pause" the Main Activity, when the back button is pressed it should go back and reactivate the Main Activity instead of calling onCreate().
This shall work with all Activities, so if I click on a button to start Activity B, it shall also reactivate the old Activity B instead of creating a new state with onCreate().
How can I realize this?
PS: I already tried it with parcelable, but this do only work, if I close the application or something unexpected happens.
It's always possible that the first activity may be destroyed when you start another activity. It will be recreated when you go back to it. Every app should be written with this possibility in mind. To make sure your activities can handle being destroyed and recreated, turn on the "don't keep activities" developer option.
It is by default in Android. If you Start Activity B from Activity A then activity A goes in stopped state. Below methods will be called of Activity A
onPause()
onStop()
When you tap on Back key on Activity B. Below methods of Activity A will be called.
onRestart()
onStart()
onResume()
For reactivating Activity B, you can set Activity B launch mode as singleInstance. This will make sure that only single instance of Activity B will be created. onCreate will be not be called again. onNewIntent will be called when that activity is reactivated.
Refer: http://developer.android.com/guide/topics/manifest/activity-element.html#lmode
Activities live in stack order. Each activity has its life cycle. When you close an activity (Activity B in your example) it eventually reaches onDestroy() method and removed from the stack order. It is by default in Android and there's nothing you can do about it.
What you can do is rewrite the onStop() method in Activity B and save some activity data (like text in EditText, for example) in SharedPreferences - read here. Then in your onCreate() method you can call for SharedPreferences, check if it's not empty and restore the text in the EditText by pulling appropriate value by key.
In answering a question here on SO about when onDestroy is called what appears to be an inconsistency in the Android docs arose.
According to the android docs regarding the task and backstack
"When the user presses the Back button, the current activity is popped
from the top of the stack (the activity is destroyed) and the previous
activity resumes (the previous state of its UI is restored). "
However at the same time, the android lifecycle suggests that activities are not automatically destroyed but rather paused if the UI is partially hidden, stopped if the UI is totally hidden, and destroyed only if the system is low on resources.
These are two opposite positions. So my questions is - which is it?
Shout out to #Raghunandan for going back and forth with me in comments for a while. Hopefully we will get an answer.
They are both correct in their context. Maybe the lifecycle should say "destroyed only if system is low on resources OR it is popped form the stack".
Consider this for example. You start with activity A, then start activity B from A, then start activity C from B.
Now both A and B are paused and stopped, but not destroyed.
If C is heavy on resources, A or B may be destroyed.
Now press Back button from Activity C, you are back to Activity B
Activity C is paused, stopped and destroyed .
Now press Back button again, you are back to Activity A
Activity B is paused, stopped, and destroyed
System tries to keep all the activity instances so they can be re-opened quickly. But when an activity is popped from the stack, there is no way for user to re-open them in future, at least not the same instance.
There is no contradiction in these two statements.
onDestroy is called when you press BACK unless you override onBackPressed not to call finish() or do a strange thing of overriding finish and not calling super.finish().
If you don't stop the call to Activity.finish, onDestroy is always called.
The other statement speaks nothing about pressing BACK and I can find nowhere under the link you have provided here that
and destroyed only if the system is low on resources.
Additinally to onDestroy being called when app is low on resources (which strangely doesn't happen on my phone; I get OOM) it is called when 20 other Activities globally (from all applications) are started after this one.
I also encouarge you to check out yet another answer on when onDestroy is called again for a real reason of onDestroy not being called.
I was following these four topics Creating a Fragment, Handling the Fragment Lifecycle , Managing the Activity Lifecycle and Pausing and Resuming an Activity. So I am in a little doubt about this.
My question is
If A Activity call B Activity through Intent but A does not call finish() method then A will be in Pause state if B is Transparent or SemiTransparent and in Stop state if B is Opaque. Am I right?
If A Activity contains Fragment F then if A will go to Pause state then F will go to Pause state and if A will be in Stop state then F will be in Stop state too. Am I right?
If A calls B Activity and B is Transparent then A will be in Pause state and F will too. If B call finish() then A will come to Resume state but what will happen to F? will it come to resume from pause? If it is then how and what steps because I have not seen any direct link in Fragment life cycle which indicates onPause() to onResume() directly as Activity can do.
Hope I am able to ask what I want. Sorry for my bad Englsh.
You can't be sure that only onPause will be called on A if B is SemiTransparent or partially visible as I understand it:
Paused
Another activity is in the foreground and has focus, but this
one is still visible. That is, another activity is visible on top of
this one and that activity is partially transparent or doesn't cover
the entire screen. A paused activity is completely alive (the Activity
object is retained in memory, it maintains all state and member
information, and remains attached to the window manager), but can be
killed by the system in extremely low memory situations.
Yes, you are right:
The lifecycle of the activity in which the fragment lives directly
affects the lifecycle of the fragment, such that each lifecycle
callback for the activity results in a similar callback for each
fragment. For example, when the activity receives onPause(), each
fragment in the activity receives onPause().
However, the opposite is not true, meaning that if a fragment receives onStop, that does not guarantee that the Activity's onStop will be called.
I am not quite sure what you mean by your last sentence or how you have tested this. According to the Fragment documentation:
public void onResume ()
Called when the fragment is visible to the user
and actively running. This is generally tied to Activity.onResume of
the containing Activity's lifecycle
It says generally because it depends on how the fragment is handled by the activity.
If A Activity call B Activity through Intent but A does not call finish() method then A will be in Pause state if B is Transparent or SemiTransparent and in Stop state if B is Opaque. Am I right?
Yes true
If A Activity contains Fragment F then if A will go to Pause state then F will go to Pause state and if A will be in Stop state then F will be in Stop state too. Am I right?
Yes correct
If A calls B Activity and B is Transparent then A will be in Pause state and F will too. If B call finish() then A will come to Resume state but what will happen to F? will it come to resume from pause? If it is then how and what steps because I have not seen any direct link in Fragment life cycle which indicates onPause() to onResume() directly as Activity can do.
What you understood is correct, even in this scenario also fragment will be moved from onPause to onResume state just like an activity.
But unfortunately there is not much documentation about this in developer android.
This might be because they wanted to avoid complicated diagrams which can create more confusion.
So, I have written an app that has a Main activity (A), and various other sub-activities that all do something, report the work back to main, and finish.
What I am running into is that if the user leaves my app at Screens B or C, when they come back at a later time and dalvik has destroyed my activities for more RAM... the app starts back at B or C, and reads all the initialization stuff from the saved intent, and continues on as if nothing happened. Until they return back to activity A, where we get a force close from unexpected things.
I've twiddled with overriding onStop() and calling finish, but i think this is a bit scorched earth, I'd like to let them leave on that activity and come back. But i'd also like to detect if the underlying main activity has been destroyed, and then destroy my entire activity stack.
From reading around, I get the idea that onDestroy() is not always called, so it isnt reliable for me to insert some logic into Activity A's onDestroy.... Any ideas?
Try starting your sub activities B and C with the flag FLAG_ACTIVITY_NO_HISTORY.