My activity calls an activity in another app for result. Now, while the called activity is running, there's a chance that my process will be quietly killed for lack of memory. If that happens, once the called activity finishes, the caller activity (mine) will be revived and onActivityResult() will be invoked some time after onCreate().
Question - in activity's onCreate, is there a way to tell if this is the case, or the caller activity is being invoked in the regular way?
If the other app returns a result code, then you can check for that in the onActivityResult. If the result code is different to what you expect, then you can assume that the activity did not finish correctly, i.e it was killed.
Related
My activity A starts one activity B in another app, and the user can spend quite some time on that activity. During this time, Android could possibly destroy my activity A (perhaps to reclaim memory), I am wondering:
If A starts B with startActivityForResult, and B calls setResult to return to A, If A needs to be recreated, will onActivityResult get called after A's onCreate?
If A starts B with startActivity, and B return to A by calling startActivity with flag FLAG_ACTIVITY_SINGLE_TOP, if A is alive, onNewIntent should be called. But if A is destroyed, will onNewIntent get called after onCreate?
I have read about android activity lifecycle posts on SO, but am not sure about this. And it's not easy to experiment on this.
1)It will, but the order of lifecycle calls may be a bit different than you expect. Be prepared for that possibility.
Let's say I have Activity A. I start Activity B from Activity A using startActivityForResult(), and whenever that's done, the setResult() fuction is called in Activity B, resulting in Activity's A onActivityResult() being called.
But what if while I'm in Activity B, the android OS kills the app and restarts it. When the app is restored, I'm back in Activity B and I then choose to return back to Activity A by calling setResult(). So my question is, is Activity A's view recreated(onCreate() called) before its onActivityResult() is called? In such a scenario, is Activity A's onActivityResult() even called?
Thanks for reading.
So my question is, is Activity A's view recreated(onCreate() called)
before its onActivityResult() is called? In such a scenario, is
Activity A's onActivityResult() even called?
Yes. If OS has killed the process of your app and Activity B is finished, the OS knows to recreate Activity A and deliver the result to onActivityResult(), just as if Activity A hadn’t died at all.
Note:
This is only possible if both of your activities are in different process. OS never kills activity directly when its under memory pressure. It simply kills the entire process hosting the activity. If both of your activities are hosted in same process then your scenario shouldn't happen.
Any callback class instances, member variables, and even static variables are destroyed along with Activity A, since whole process is killed.You have to re-create them in Activity A.
When you're in an an Activity (we'll call it A), and you invoke a subsequent Activity (B), perhaps as a result of clicking a button in A, and then RETURN to that prior Activity A, either by clicking the Back button or explicitly calling finish() from within B, it causes A to be completely rebuilt, calling its constructor and its OnCreate() method, etc.
Is there any way to prevent that from happening, so that it actually does return to the prior, already existing, Activity A?
Correct me if I'm wrong, but it should not call onCreate() here's a gross over simplification, but let's say activity's are managed much like a simple stack, let's call it AppStack
When a onCreate() for Activity A is called, the OS pushes the Activity Instance onto the AppStack
________ _________________
Activity|
___A____|_________________
When you click a button on Activity A, it launches a new intent to Activity B
Intent actB = new Intent(this, ActivityB.class);
and subsequently puts Activity A into Stopped state
When Activity B's onCreate() is called the OS pushes that Activity Instance onto the AppStack
________ __________________
Activity|Activity|
___A____|___B____|_________
Now if you call finish() or super.onBackPressed() in Activity B, the OS will pop() the Activity from the AppStack
________ __________________
Activity|
___A____|__________________
When the OS returns to the previous activity, it sees that it is Stopped and begins the process of Resuming it through onResume().
Now if there is some data that you require to be persistent, you can add it in by Overriding onResume()
Check out the activity lifecycle docs, for more info:
This is by design:
If an activity is paused or stopped, the system can drop it from memory either by asking it to finish (calling its finish() method), or simply killing its process. When the activity is opened again (after being finished or killed), it must be created all over.
See Activity Lifecycle. It's also why the Service class exists:
A Service is an application component that can perform long-running operations in the background and does not provide a user interface. Another application component can start a service and it will continue to run in the background even if the user switches to another application.
It's not a typical scenario but when onCreate() is called when going back to that activity that means the Android OS kills it in the background.
Reason: Android is experiencing some memory shortage so killing some of the background task will be a must.
Is there any way to prevent that from happening?
No, you don't have a control over it, there are many reasons why its having a memory shortage e.g. other app installed that certain device is consuming more than expected. Although you can handle this use-case by storing the current information in onSaveInstanceState() and recovering the value from onCreate().
Calling finish() on ActivityB or pressing back will just destroy ActivityB.
ActivityA will not be completely rebuilt. This means it will not call onCreate method. It will just call onResume.
This is the normal behaviour.
However, on special situations, the system could destroy ActivityA (maybe because it needs memory to perform another task), so when you go back to it, the system will have to rebuild it.
To simulate this situation, there is a setting that you can check/uncheck, called "Don't keep activities".
If you have it checked, you will be simulating the situation explained above, it will always destroy the ActivityA as soon as it is not shown, and when you come back to it, the system will have to rebuild it calling onCreate.
The main activity in my app sometimes calls startActivityForResult, expecting a result that will tell it (the main activity) what information to display next. Looking at the documentation for the process lifecycle, it appears that while the selection activity is active, the main activity is considered a "background" activity and could possibly be killed.
So what happens when the selection activity completes? I see that my activity will be re-created and onCreate is called with the SaveInstance Bundle, but then what? Is onActivityResult then called just as if my main activity had never exited and been re-created?
Also, is there any way to force this behavior in a testing environment, since it should otherwise be a very rare occurrence?
Hint: log statements
The paused state as described in docs is:
If an activity has lost focus but is still visible (that is, a new
non-full-sized or transparent activity has focus on top of your
activity), it is paused. A paused activity is completely alive (it
maintains all state and member information and remains attached to the
window manager), but can be killed by the system in extreme low memory
situations.
That means, under normal circumstances , your main activity should just transfer the control to onActivityResult() when the selection activity completes.
However, docs also state that:
A background activity (an activity that is not visible to the user and
has been paused) is no longer critical, so the system may safely kill
its process to reclaim memory for other foreground or visible
processes. If its process needs to be killed, when the user navigates
back to the activity (making it visible on the screen again), its
onCreate(Bundle) method will be called with the savedInstanceState it
had previously supplied in onSaveInstanceState(Bundle) so that it can
restart itself in the same state as the user last left it.
In such cases, the main activity can be redrawn.
One important point to note is that the docs have never mentioned onActivityResult() as one of their lifecycle methods here
So, it might also be the case where android system treats a sub activity and parent activity (read startActivityforResult() and onActivityResult()) in the same manner as it treats an activity - dialog as stated here:
A visible activity (an activity that is visible to the user but not in
the foreground, such as one sitting behind a foreground dialog) is
considered extremely important and will not be killed unless that is
required to keep the foreground activity running.
The answer is basically "yes": The activity is recreated and the control flows through onCreate(), onActivityResult(), onStart(), and so on.
An activity is also destroyed if the user rotates the device, e.g. from portrait to landscape, unless the application did not explicitly prevent this behavior. So simply rotate the device (CTRL-F11 on the emulator) to test.
Suppose there are two activities A and B and Activity A calls Activity B through startOnActivityResult(intent,200) then your Activity goes to background and Activity B is called (onCreate,onStart,onResume) depends on what you have overridden.
Whenever your B activity calls finish() then your Activity B is destroyed and Activity A comes to foreground. In this case now ie Activity A the call will be onActivityResult -> onStart -> onResume but your onCreate will not be called as it is only called when you call a certain Activity.
Suppose you have not called finish() from activity B and called Activity A through intent then only you onCreate() will be called.
Also onActivityResult() is very useful when you want to retain value of your spinners or you want to notifydatasetchanged() your ListView of your First activity after the event of Third activity. You just have to check your resultCode from the Activities in onActivityResult and perform your actions
I have an activity where the first thing it does is start another activity for result. when the result comes back, it should process it, then finish. I have the startActivityForResult() call in onCreate().
What I see is that sometimes when I return from the target activity i started, onCreate() in my activity is called again. this of course re-starts the target activity a second time.
This makes sense and I understand why this is the case, but I don't understand the correct pattern for what I'm trying to achieve. When I return from the activity i started, i don't want to re-start the target activity again obviously ... I just want to run onActivityResult() and finish.
I read where someone suggested setting a state preference, but that seems like a good source for bugs, for example, if it got stuck in the wrong state.
Any thoughts?
The key is to start the target activity in onResume(), not onCreate(). From the javadocs on onActivityResult(),
You will receive this call immediately
before onResume() when your activity
is re-starting.
In other words, you can be assured that onActivityResult() is called before onResume() ... So for example, set a flag that says "don't start target activity this time" in onActivityResult() so when onResume() is subsequently called, you can avoid re-starting the target activity.