I have the following flow in my code:
Activity1 : startActivity(Activity2) --->
Activity2: startActivity(Activity3) --->
Activity3: startService(MyService) --->
MyService: startActivity(Activity4)
Each Activity above shows a single view and represents a step in a 4-step setup. The final Activity - Activity 4 - is started after some setup work is done inside MyService, which basically tells the user,
"The service has started, you can close the application by pressing Back or Home button"
When the user presses Back or Home, I want to destroy Activities 1-4 , and only have MyService running. Also, after stopping the Application as above, when the user navigates back to the Application via the menu and starts it, I'll be checking if MyService is already running. If it is already running, I don't want to show Activities 1-3, I want to show another Control Panel View (Another Activity), which says,
"Dude, the service is already running, do you want to Stop or Restart it?"
This view will have a Stop and Restart button, to do the appropriate tasks.
My Questions:
How do I stop Activities 1-4 from inside Activity 4 when Back or Home is pressed,safely? My thought was to add a static stopActivity() method to each Activity, and calling Activity[1-3].stopActivity() from onBackPressed() or onPause() of Activity4. Then inside each stopActivity(), I'll call finish(), thus ending each Activity. But is it safe and efficient to do it this way?
The flow I have illustrated above, is it the optimal way of doing things, or is there a cleaner way? I have BroadcastReceivers registered in these Activities, so I need to perform clean exits for each Activity, without leaked receivers, or worse, crashing the App or affect the User's phone due to unclean exit strategies.
Thanks for your suggestions.
You don't need to stop activities, Android will do it for you. Start your activities using intents with the flag FLAG_ACTIVITY_NO_HISTORY so they won't appear when the user presses back. Those activities will be stopped as soon as the user leaves them.
In the onStop method of each of your activities, write any code you want to deallocate memory if there is something you want to deallocate manually, although that wouldn't be necessary because Android will deallocate it for yourself when the device is short on memory. In those onStop methods unregister any BroadcastReceiverpreviously registered.
Related
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.
I have a flow in my android app where it is possible to open a chain of user profile activities, one activity from another.
Example : User profile A is opened where it contains a list of other user profiles. clicking an item from this list will open user profile B. Again, it lists other user profiles where user profile A might be part of. So clicking this item will open another activity of user profile A.
The app user can perform an action on the current activity of user profile A which needs to be reflected on all other user profile A activities in the back stack. So what i did was registered a receiver in the user profile activity that checks for the activity user id against the one coming from the broadcast and perform the relevant actions on the UI.
The problem is, that i cannot unreigster the receiver on onPause() or onStop() (according to a lot of threads recommendations here) since this is kindda counterproductive to what im trying to build here. And according to the documentation onDestroy() is not guarunteed to be called every time the activity terminates.
So what im basically asking here is - Is it a good practice to register all activity receivers on onCreate() and unregister them both on onDestroy() and onSaveInstanceState() so i will be 100% sure they are cleaned up on activity destruction ?
The only mention i saw in the documentation that recomments not to unregister receivers in onSaveInstanceState() was here - BroadcastReceiver - and it only says
Do not unregister in Activity.onSaveInstanceState(), because this won't be called if the user moves back in the history stack
EDIT:
Ok, i just saw this quote in the onDestroy() spec :
There are situations where the system will simply kill the activity's hosting process without calling this method (or any others) in it, so it should not be used to do things that are intended to remain around after the process goes away.
So onSaveInstanceState() will not be called also.
But arent receivers qualified as things that are NOT "intended to remain around after the process goes away" ? I dont get why onDestroy() is not called in such situations. What happens to other resources that are released there (not just receivers) ?
In your case, using broadcast receiver to detect which Activity instance is running is not a good idea. You should use single instance for every Activity. Another solution is that you can create view stack in one Activity instead of Activity stack. Switching view is easier than switching Activity. As to how to use onDestroy and onSaveInstanceState(), it depends on your scenario. If you finish your Activity, the onDestroy will be called. If your Activity is destroyed by system in some situation, like screen rotation, the onSaveInstanceState will be triggered.
I'm trying to kill 2 activities on the onclick of a button. The current activity and the previous activity. Using their pids. I'm just able to kill one activity. Why does this happen?
public void onClick(View v) {
android.os.Process.killProcess(pidofmain);
android.os.Process.killProcess(android.os.Process.myPid());
}
If I see in my logcat, The activity with pid "pidofmain" is getting killed whereas the current activity is not getting killed.
"pidofmain" is an integer i received from the previous activity using an intent.
Leave process killing to the OS. This is bad for any kind of program in a timesharing OS. If you want to conserve memory or something like that, let the OS handle it.
Also you can't really know if the process was correctly killed because well, if it is you wouldn't know, and if it doesn't you were not supposed to do it.
What do you want to do this for?
A much better way to do this is to call finish() for the current activity. You can also signal the previous activity to finish if it calls the current activity using startActivityForResult(Intent). The current activity would call setResult(int) before calling finish() to send a return code back to the previous activity. The previous activity can test the return code in onActivityResult(int, int, Intent) and also call finish() based on the result code.
Killing processes should be left to the OS. Once the activities finish, the will kill it off if it needs the resources. Otherwise it can let it around, which might help speed up a relaunch of your app if the user wants to run it again.
This isn't a definitive answer, but more like some thoughts that I have but it's too late for my to fire up Eclipse and prototype them. If it doesn't help you let me know and I'll try to look into it deeper tomorrow night.
A few thoughts (I hope they help):
1) Android apps really are single-threaded, and your main activity controls all the dispatch events (including events to what I assume to be a second thread that you created). If you kill the main activity, I'm pretty sure that your application would terminate execution immediately following your first call to android.os.Process.killProcess(pidofmain), and you'd never make it to your second call because you would have killed your entire application. Again, this is assuming by the variable name pidofmain that you are killing the main UI thread and not just an activity called main.
2) I'm a little curious also about where you got pidofmain? It sounds like you have three activities total, and in the first activity you get it's process id and send it to the second activity in an intent bundle, which also gets passed along to a third activity (which is the place where you're trying to kill this whole thing)? If that is the case, and you're trying to kill the currently running activity, the table in the documentation here leads me to believe that you can't just kill an activity that's in the resumed state using the same method. Official Android Docs for Activity You might want to try calling the finish() method for your currently running activity.
What exactly do you see in logcat? And what happens in the UI? Does the visible activity continue to run, but the other activity has been removed from the backstack?
I have an Android app. I have a main activity, that has a button. When the button is clicked, another activity comes to the foreground. The thing is, I want to run a background thread that polls updates from the server. However, I want it to run only when the app is in foreground (either the main activity or the second one), and to stop polling when the user clicks the Home button or clicks the Back button till it's going back from the main activity.
But how do I know if the app is still in the foreground? I can catch the onPause of the main activity, but it's called also when I'm launching the second activity.
So how do I know when the app is in background?
Thanks
You should make a Service for the work you are doing in the background.
For stopping it when you click the Home or Back button, just make a listener for them and stop the Service when either one is pressed.
Seems easiest to me that each activity polls. Is it super important that it can poll when it is between the two activities? Otherwise you will have problems about knowing who is in front or not.
You can have a singleton with reference counting.
You main activity should add the first reference on it's onResume and from now, upon calling for every new activity (startActivity for example) you should add a reference.
Each activity should decrease the reference counting on its onPause.
Another option is to use services: Services
I have this requirement to send my application background and then bring it to foreground on some key capture intents (not from application launcher offcourse) So How can I send the current tasks to background and bring the same to foreground ?
Use moveTaskToBack() to send the activity in the background and still running if the user presses the back key.
see :Activity for the way on how to do this. its quite simple.
so in order to do this you will also need to override the onBackPressed() method or onKeyPressed() and call this method if the back button was pressed (dont forget to return true on the back pressed methods so android is aware that you consumed the event and doesnt finish the activity).
For returning to this activity that you have moved to the background you can post a notification with a pending intent to launch it back and that will automatically bring the activity to foreground.
Hope this helps.
To send you application to background you should call moveTaskToBack() from your Activity class. When your Activity gets new intent (btw. the onNewIntent() method from your Activity will be called) your Activity gets into foreground by system (you don't have to do anything).
What do you mean by "background?" Activities are stacked one upon another as you create new Activities, then accessed in reverse order using the device's back button. Think of the push() and pop() methods, it's the same paradigm. Applications that need to have code running non-interactively should extend android.app.Service, but beware that you can do some real damage implementing a service. Rogue processes can drain battery life and reduce UI responsiveness.
I solve all the problem pertaining to notification start with fresh activity after moveTaskToBack(true) when back key is pressed by
adding to manifest android:launchMode="SingleTask" android:clearTaskOnLaunch="true"in the activity xml markup section