How to stop/pause an Android Service when all activities are hidden - android

I'm working on designing an Android app, and I'm looking for information on how to implement what I thought would be a common pattern, but I can't seem to find any information about it online.
I'm going to have an application with multiple activities, and all of these activities are going to be communicating with a WiFi device over a network. So my first thought is that, since all Activities will need access to this central 'network communicator', I should create a Service to hold all of the network communication code. Great!
But now, I want to be able to save battery life, and in general make the application less obtrusive. If none of the activities from my app are visible, then I don't need to be running the network code, and I can shut down my sockets and wait. Then, when one of the activities becomes visible again, I can reconnect at that point.
Here's the problem - I don't know how to do this! Is there an existing method or pattern used to determine when all activities from my app are hidden? I don't want to disconnect/reconnect every time one of my Activities resumes or is created, because most of the time that will be from the user navigating around the app, switching between activities. I want the network connection open this whole time.
I could put some sort of 'reference counting' mechanism in my Service to keep track of when Activities appear and disappear, but that seems like overkill. I'm surprised there isn't an existing mechanism for this.
Sorry if I'm missing something simple...

You can check it using:
public void onPause(){
super.onPause();
Context context = getApplicationContext();
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> taskInfo = am.getRunningTasks(1);
if (!taskInfo.isEmpty()) {
ComponentName topActivity = taskInfo.get(0).topActivity;
if (!topActivity.getPackageName().equals(context.getPackageName())) {
StopPlayer();
Toast.makeText(xYourClassNamex.this, "YOU LEFT YOUR APP. SERVICE STOP", Toast.LENGTH_SHORT).show();
}
}
}
Put the above code in on Pause() of every Class. It will toast when your app will go on recent application by clicking on home or back button. Now you can set any preference instead of this Toast & stop the services. Now check your preference on Resume() of every Class. If preference has any value then u can start your service.

The pattern is to bind the Service to a base class Activity, and then to inherit your multiple classes from that one bound Activity.

A Bound Service sounds like exactly what you need: http://developer.android.com/guide/components/bound-services.html#Binder
Since you have multiple activities using this same service, you can Bind each one to the service, and Unbind the activity when it no longer needs to use the service. A great place to do this would be in your onStart() and onStop() Activity lifecycle methods. The great thing about this is that when the Service detects that there is no Activity bound to it, it will kill itself.
Further reading: Great explanation by user hackbod on why onStart()/onStop() is a good option: https://stackoverflow.com/a/2304794/1839500

Related

How to force killing application when minimizing in Android

Right now I'm developing some kind of password manager & generator application and it is almost done but I'm facing a problem that seems a bit hard to solve.
In order yo improve the security of my app, I added a timer so if the user is under inactivity the app will close. Now I'd like to close the app every time the user want to minimize it (pressing the "HOME" button and the "RECENT APPS" button).
I've tried onStop() and onPause() but nothing works because every activity on my app when is replaced by another moves through the mentioned states. Since I can't use Keycode == Keycode.HOME how could I do that?
I suggest that all your activities keep track of their state in onResume() and onPause(): they can increment some static application-wide variable counting how many of your activities are visible.
When this count goes to zero, it means none of your activities are visible and you can do your cleanup.
The solution proposed by #Don Chakkappan is not good enough I think, as your activity can be reported as active even though it's no longer visible.
Continuously check the Activity in the for ground from a service
ActivityManager am = (ActivityManager) this .getSystemService(ACTIVITY_SERVICE);
List<RunningTaskInfo> taskInfo = am.getRunningTasks(1);
#SuppressWarnings("unused")
ComponentName componentInfo = taskInfo.get(0).topActivity;
Log.d(TRCU.LOG_TAG, "CURRENT Activity ::" + taskInfo.get(0).topActivity.getClassName());
String currentActivity=taskInfo.get(0).topActivity.getClassName();
Then you can finish() or stop Service according to the context
The Android usage model tells you not to do that. You can disable all password generating/managing when going to background and re-enable it when resuming. This will also be much faster performance-wise than killing the application on pause.
Since your app is a security enabling application, all sensitive information should be inaccessible at onPause anyway, so this shouldn't impact the design too much.

Android: How to backup a database when the application gets closed?

I am needing help to determine the right approach. I want to make a backup of an internal database to a location in the external storage every time the whole application gets interrupted or terminated/destroyed. I want to call this method from a central class called Main which extends Application. The reason for that is, that I need to use several activites and I want to call the backup Method only when needed (like described when the whole application gets destroyed or interrupted by another application). I try to avoid calling this backup method in every activity in their onPause() methods.
I thought about starting a service in the onCreate() method of the application, and starting the backup method when the service gets destroyed. But this won't help in the case of an interrupt, as far as I understood the logic behind services. And also the service doesn't seem to start. startService(new Intent(getApplicationContext(), BackupService.class)); Furthermore I don't think it is a good approach to just use the onDestroy() method of a service, this is not what the service class is made for in my opinion.
So summarizing my Question, do you know a better way then using a service, or if not do you know how I should use the service to be able to call a backup only at the point when the whole app (and not only an activity) is interrupted or destroyed.
First of all, if your service "doesn't seem to start", you are probably doing something wrong.
To accomplish your goal make a backup of an internal database to a location in the external storage every time the whole application gets interrupted or terminated/destroyed:
There are three cases in general here.
If you want to do it in the activity layer:
To know when your application is crashed, you need to implement a custom handler to catch the uncaught exceptions.
To know when your activity is "interrupted", the only way is do it in onPause.
To know when your activity is "terminated", the only way is to do it in onDestroy.
This will require you to have a clear navigation and only do it in your "main activity", and all the other activity starts and comes back to it OR use a flag to indicate if the pause was caused by going to another activity.
If you want to do it in the service layer: (Your way of doing it onDestroy won't allow you to detect interrupted case since you will have to start service sticky to keep it running)
You will have to set up a flag on each activity onBind (you will have to bind it and unbind it) to know if it is a crash/interrupt/termination, which will complicate other part of your code.
To avoid running repetitive code, you will have to create a generic base class and extend your other activities from it.
I use this approach to play background music in one of my games, but I guess it works in this scenario as well.
Use a boolean flag to indicate whether or not your app is launching another part of your app.
boolean movingInApp = false;
....
movingInApp = true;
Intent intent...
.....
public void onPause() {
if(!movingInApp) {
//start service
}
}
public void onResume() {
movingInApp = false;
//Stop service
}
By setting the value of movingInApp to true before launching any intent etc, you can prevent your app from starting the service. Remember to set it to false again later in your onResume() method. If the system makes your app go to the background, this will be false, and your service will be started.
Why dont u have all of your activities extend a base activity which in turn extend the android activity class
I the base activity have backupDB method in the onPause
Therefore u dont have to put it in every activity pause method

Application-level onPause

I know that Android doesn't have an Application-level onPause the way an Activity has an onPause, but it looks like I need a similar functionality. I asked a (dumb) question awhile ago (http://stackoverflow.com/questions/9508301/checking-the-manner-in-which-an-activity-has-been-reanimated) and realized that onPause/onResume will give me what I'm looking for: The ability to know if the Activity has been relegated to the background (either via the Home button, opening a different app, the phone sleeping, etc.).
The trouble is, onPause also fires in an Activity as I go from one Activity in my App to another. So I'm heading down the road of finding EVERY SINGLE way a user can go from Activity to Activity in my app (and there are anywhere from 15 to 25 of those) and setting a flag right before each user-initiated Activity switch. Then in onResume I'm checking that flag and if it's X, I know that it was a user-initiated onResume. If the flag is Y, I know that it's a result of the Home button being pressed, the phone sleeping, etc.).
This just seems like a big messy kludge. Is there a better way?
(I add this to help, not to confuse: My goal is to have a (very annoying, I know) passcode screen that pops up any time the app goes away from the foreground for any reason (even screen sleep). I planned to use onPause/onResume to check when to pop the Passcode activity, but onPause fires every time I change Activities and therefore I need to add more.)
Apps like Pinnacle Locker do something similar--I believe they intercept an Intent for specific packages, and thus are fired whenever the system switches to an app, but not when the user changes screens between apps.
I may be misunderstanding you, but another approach would be to turn your approach around: whenever you change from one screen to the other inside an app, you are either launching an Intent or finish()ing (such as when the user presses the back button), right?
Then you could set a flag in your internal Intents, and whenever you directly call finish() or when onBackPressed() is called in your apps you could set a similar flag. If that flag is not set when onResume() runs in your app, then you could show the lockscreen.
This doesn't really answer your question, but perhaps as an alternate approach, could you make an activity base class that does the flag management and have all your activities inherit from that? You could even do something like set a timeout of, say, 5 seconds between one activity's onPause and another activity's onResume to decide that you were doing an out-of-app switch instead of an in-app switch, although if you just have a utility function that you call from your activity base class for initiating an activity change, that still vastly reduces the surface area of where you have to keep track of things.
You should combine the onPause() function with application visibility status and process further if your application is not visible. Following code might help:
private boolean isVisible(String app) {
ActivityManager activityManager = (ActivityManager) getApplicationContext()
.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> appProcesses = activityManager
.getRunningAppProcesses();
for (RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.importance == RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
Log.i("Foreground App", appProcess.processName);
if (app.equalsIgnoreCase(appProcess.processName)) {
return true;
}
}
}
return false;
}
Again this might not fit in your current scenario entirely.

Stopping and starting a Service based on application state

I have a Service which tracks the location of the user. Currently, the Service boots when the application starts and stops when the application terminates. Unfortunately, if users keep the application in the background, the Service never stops and drains battery.
I would like the Service to stop when my application is not in the foreground. I was hoping the Application class would let me Override onPause and onResume handlers, but it does not have them. Is there another way I can accomplish this?
I haven't tested this yet, but it looks like if you use Context#bindService() (instead of Context#startService()), the service should stop when no more activities are bound to it. (see Service lifecycle).
Then use onPause()/onResume() in each activity to bind/unbind from the service.
Alternatively, you could add a pair of methods on your service which tell it to start/stop listening for location updates and call it from each activity's onResume()/onPause(). The service would still be running, but the location updates wouldn't be draining the battery.
Reading all the above answers I would suggest Simply add a boolean global flag for each activity & put it in your onResume & onPause & also while launching an Activity Something like this
public void onPause()
{
super.onPause();
activity1IsResumed = true;
}
&same for onResume
& similarly when launching a new Activity
startActivityForResult(myintent ,0);
activity2IsResumed = true;
activity1IsResumed = false;
then in your Service simply check
if(activity1IsResumed || activity2IsResumed || activity3IsResumed)
{
//your logic
}
else
{
//another logic
//or dont run location tracker
}
& you are done!
You should override the onPause and onResume methods on your Activity. If you have multiple activities you may want to have a common base class for them and put the start/stop logic into the base class.
I have not tried this approach but I think you can override the home key of android device by using KeyEvent.KEYCODE_HOME and you can use stopService(Intent) to stop your service and when again application resumes, you can write startService(Intent) in the onResume() method of your Activity.
This way I think your service will only stop when user explicitly presses home button to take application in the background and not when he switches from one activity to another.
What I would suggest is overriding the onPause/onReume methods as others have said. Without knowing more about the flow of your application and interactions between Activities, I can't give much more information beyond guesswork.
If your Activities are persistent, however, my recommendation would be to utilize the Intents better when switching between Activities.
For instance, each Activity should have a boolean "transition" flag. So, when you move from one Activity to the next, you set up an Intent extra:
intent.putExtra("transition",true);
Followed in the receiving Activity by: (in onCreate)
intent.getBooleanExtra("transition",false);
This way, for each Activity that launches, you can know whether it has come from another Activity, or if it has been launched from a home screen launcher. Thus, if it gets a true transition, then onPause should NOT stop the service--that means you will be returning to the previous Activity after it returns. If it receives no "transition" extra, or a false transition, then you can safely assume there is no Activity underneath it waiting to take over for the current one.
On the first Activity, you will simply need to stop the service if you are switching to another Activity, which you should be able to figure out programmatically if one Activity is started from another.
It sounds like the real problem is how to only stop the service when you go to an activity that isn't one of your own? One way would be to in your onPause method to stop the activity. Do this for all your activities. Then override your startActivity method. And in here do a conditional test to confirm that you are purposefully navigating to one of your own. If your are set a flag to true.
Now go back to your on pause overridden method. And only stop your service if the flag is not equal to true. Set the flag to false.
All events that navigate away will close your service. Navigating to your own will leave it intact.
Do the overriding in a base class that all your activities extend.
Writeen in my andolroid. Will post ezaple later.
Try using the Bound Services technique to accomplish this.
Bound Services | Android Developers
You can use bound services in a way such that the service will stop when no activities are bound to it. This way, when the app is not in the foreground, the service will not be running. When the user brings the app back to the foreground, the Activity will bind to the service and the service will resume.
Create methods registerActivity() and unRegisterActivity() in your Application object and implement first method in all you acts onResume() and second in acts onPause().
First method add activity to List<Activity> instance in your app object, unRegisterActivity() checks size of list in every call if==0 stopService();.

Determining if app is running in background

How can I tell that my application has been put in the background (i.e. none of my app's activities are visible anymore)? I need to determine this so that I can release resources shared among several of my activities (graphics, sound clips and a socket connection).
I've thought about keeping a global counter that's incremented in the activities' onStart() method, and decremented in onStop(). If the counter reaches zero, then all activities have been stopped and my app is running in the background. However I'm not sure if this is going to be 100% reliable. Also, I can't help but think that there must be a better way of doing this.
You shouldn't need to know this, but to answer you:
in your main activity:
public boolean inBackground;
#Override
public void onPause()
{
inBackground=true;
super.onPause();
}
#Override
public void onResume()
{
inBackground=false;
super.onResume();
}
Aren't you after the OnPause()/OnResume() events?
http://developer.android.com/reference/android/app/Activity.html#onPause()
http://developer.android.com/reference/android/app/Activity.html#onResume()
See http://developer.android.com/guide/topics/fundamentals.html#lcycles for an overview.
You could use a global counter assuming it is kept in persistent storage. Always keep in mind the system is free to unload and reload activities from device RAM based on pressure from other apps so instance variables of activities are probably not a good choice to house that data.
I think the Android way of handling a scenario like yours would be to manage your connection state in a service and use persistent storage to monitor application state.
If you need this functionality maybe your architecture is not well designed. Each activity must be in some way "standalone" so when it's stopped release any data associate with it. If you need to store some persistant data between activities use sql or some other data storage and if you need some shared resources between activities put them in service. Try to isolate any coupling between activities.
I haven't tried this myself yet, but I think the following would work.
Create your own custom Activity class that extends Activity (as suggested in this SO question).
Ensure all your activites extend your custom Activity class.
#Override the onStop() method (as per the Activity life cycle docs).
Have onStop() call the utility method below (code based on Droid-Fu project) to figure out if your app is now in the background.
My worry is there could be some timing windows when your last activity closes before its new activity (also i your app) launches, but hopefully that is avoidable.
public static boolean isApplicationInBackground(Context context)
{
ActivityManager am =
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> tasks = am.getRunningTasks(1);
if (!tasks.isEmpty())
{
ComponentName topActivity = tasks.get(0).topActivity;
if (!topActivity.getPackageName().equals(context.getPackageName()))
{
return true;
}
}
return false;
}
Let us know how you get on as I might need to implement this feature too!
Update: I've done some quick testing of this code.
It seems to work fine when pressing the home key to exit the application, as the running task changes to ComponentInfo{com.motorola.blur.home/com.motorola.blur.home.HomeActivity} on my device. However, the code doesn't detect the app as in the background when using the back button to exit the application (it still thinks my app is running).
This scenario can be detected by overriding onDestroy() in your top level activities, and checking isFinished() to confirm that the app is getting destroyed (rather than the activity being recreated, e.g. for an orientation change).

Categories

Resources