Android - Tell if parent activity has been destroyed - android

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.

Related

Android Activity Back Stack Navigation issue

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.

Calling finish() in onPause in child activity so that user refocuses into parent activity. Child activity gets recreated instead

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.

Reconsiling the lifecycle methods and back 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.

Multiple activities from different applications

I have two activites...one in A application and other in B application.
I am calling activity from A app to B app so A app activity will be in pause and throw an intent to B app activity
and after B app activity, I will go back again to A app activity so A app activity should start from onrestart or onresume() as it was in onPause() but it is going to oncreate(). I am sure that the A app acitivity was not destroyed because when I hit back button it starts from resume or restart state.
Basically the two app activities runs in circular fashion like
A app activity- B app Activty- A app activity - B app activity ......................... so on
One more think I see is none of them are destroyed when I hit back button then it goes to resume state so nothing is killed.
Is there any way so that my transition shld be -
A app activity - B app activity ( A pause state- B on create state)
B app activity - A app activity ( B on pause state- A resume state)
Note: two activities from in diff apps
A lot of it will depend how you go from one activity to another and how you declared them in the manifest.
Without more code, I suggest you read the documentation here, especially pay attention to taskAffinity and launchMode
I will go back again to A app activity so A app activity should start from onrestart or onresume() as it was in onPause() but it is going to oncreate()
as #Eigor mentioned - the operating system can shout activities which stopped (not visible, and somewhere in the activity stack of the current process) in this case - Android OS will shout down the Activity without going throughonDestroy() , but will go through the onSaveInstanceState() callback for giving you a chance preserving any data state you'd like to restore when the activity will re-created when you'll hit the back button. the data you saved previously will be stored in the bundle param in the onCreate()
read - http://developer.android.com/reference/android/app/Activity.html#onSaveInstanceState(android.os.Bundle)
I guess because you don't knew that that's how the OS behaves it felt strange.
knowing that knowledge and taking it into consideration should solve your problem
I am sure that the A app acitivity was not destroyed because when I hit back button it starts from resume or restart state.
the fact that A app activity was not destroyed in this scenario is not something you can count on, and it does not contradict what I've said before. it still in some cases be shout down by the OS without onDestroy been call from the same reason.
about all the circulation you want to do between A and B: I think what I wrote can help you understand why it doesn't worked as you expect...

Android back button behaviour

Let's say we have a default, empty activity with default behaviour, launched with default intent flags. User presses back button on the device. The activity disappear... but how, actually?
Is pressing back button behaving the same way like finish()?
Is the activity immedietely destroyed (onDestroy is called)?
Is the activity guaranteed to be destroyed, but not immedietely?
Is there any chance the activity won't be destroyed and this instance will be reused when this activity is launched in the future? (so only onPause and onStop -> onStart and onResume are called?)
I'm looking for a reliable answer, so please do not answer if you are not absolutely sure what happens here.
http://developer.android.com/training/basics/activity-lifecycle/recreating.html
This is a subchapter from the official Android documentation that addresses your question. It is a subchapter of the topic Managing the Activity Lifecycle, which can be read here:
http://developer.android.com/training/basics/activity-lifecycle/index.html
It is definitely worth reading the whole chapter to get to know the details about Androids Activity behaviour. But the subchapter ( first link ) is the relevant part to this question.
you use should look into this try this
and please tell specific what you wish to do with back button for your default activities ......
When you press back, (if not intercepted by anything like the keyboard, fragment, activity, etc) the OS (via ActivityManager probably) will try to show the user the previous activity in your current task (again, ignoring fragments' back stack).
If there is no such activity, the task will be terminated and you'll go to the previous task - the home screen most of the times or some other application that might have launched your app.
You'll get onDestroy called soon (it depends on how long it takes to start the next activity but on a good phone it should be under 100-200ms).
Your activity instance won't be reused after onFinish. This happens before the activity is destroyed so if you need another activity of the same type, the OS will create another instance.
When the user presses the BACK key, the current activity is popped from the top of the stack (the activity is guaranteed to be destroyed, but not immediately, may be when the system resources are low) and the previous activity resumes (the previous state of its UI is restored).
Which actions does the back button/back key on Android trigger?
Definitly onDestroy() is called .....There are a few scenarios in which your activity is destroyed due to normal app behavior, such as when the user presses the Back button or your activity signals its own destruction by calling finish().

Categories

Resources