PLEASE NOTE: THE DESCRIPTION AND CODE OF THIS POST WORKS QUITE WELL FOR ANDROID UP TO VERSION 7. FOR AN UPDATE FOR ANDROID >7, SEE THE NEXT POST.
(make sure to understand this code before going to the next blog post, though, as that one builds on this one)
(make sure to understand this code before going to the next blog post, though, as that one builds on this one)
Creating a never ending background service in Android is simple but not obvious. Android has two ways of working: foreground (the app that you use and manipulate) and background (the services that you do not see but that provide data to the apps). Not every app has a background service but some do.
(please note: confusingly, Android has added two ways of working for services: background proper that is becoming more and more difficult to use and Foreground processing where the user is aware of the background process. See this link for more details). In the remainder I will use the term background to cover both cases.
You use a background service to provide continuous data collection or processing while the app is no longer in the foreground (i.e. it has been minimised and another app is displayed) or even when the app has been closed by the user - or killed by Android (this happens when the operating system is running out of resources, e.g. memory).
A typical example of an app that requires a never ending background service is a pedometer that tracks your steps 24/7. No matter if the app is not in the foreground, you still want to count your steps.
This is how to create the ever ending background service.
There are three parts in the code: (i) an Activity (the foreground app), (ii) a Service and (iii) a BroadcastReceiver which will receive a signal when someone or something kills the service; its role is to restart the service.
First of all you will need an app (an Activity). We will create a very simple app that does not display anything (just the string "Hello World"). This is pretty straightforward. AndroidStudio will do it for you.
Here is the Manifest
(please note: confusingly, Android has added two ways of working for services: background proper that is becoming more and more difficult to use and Foreground processing where the user is aware of the background process. See this link for more details). In the remainder I will use the term background to cover both cases.
You use a background service to provide continuous data collection or processing while the app is no longer in the foreground (i.e. it has been minimised and another app is displayed) or even when the app has been closed by the user - or killed by Android (this happens when the operating system is running out of resources, e.g. memory).
A typical example of an app that requires a never ending background service is a pedometer that tracks your steps 24/7. No matter if the app is not in the foreground, you still want to count your steps.
This is how to create the ever ending background service.
There are three parts in the code: (i) an Activity (the foreground app), (ii) a Service and (iii) a BroadcastReceiver which will receive a signal when someone or something kills the service; its role is to restart the service.
First of all you will need an app (an Activity). We will create a very simple app that does not display anything (just the string "Hello World"). This is pretty straightforward. AndroidStudio will do it for you.
Here is the Manifest
Code Editor
Apart from the usual declaration of an application, there are two parts that are relevant here:
Code Editor
which declares the background service and
Code Editor
which declares the BroadcastReceiver that will be called when the message uk.ac.shef.oak.ActivityRecognition.RestartSensor is sent by the service itself when it is about to die. Note. In a previous version of this post, the receiver was implicitly defined using:
<intent-filter> <action android:name="uk.ac.shef.oak.ActivityRecognition.RestartSensor"/> </intent-filter>
This is no longer possible in Android. Receivers must be declared explicitly in the receiver Java code.
The app is very simple: it launches the service if not already active and will stop the service when killed (so that the service is restarted immediately after the app dies.
<intent-filter> <action android:name="uk.ac.shef.oak.ActivityRecognition.RestartSensor"/> </intent-filter>
This is no longer possible in Android. Receivers must be declared explicitly in the receiver Java code.
The app is very simple: it launches the service if not already active and will stop the service when killed (so that the service is restarted immediately after the app dies.
There are two main parts here:
Which starts the service only when not already running, and
The latter may seem rather peculiar: why do we want to stop exactly the service that we want to keep alive? Because if we do not stop it, the service will die with our app. Instead, by stopping the service, we will force the service to call its own onDestroy which will force it to recreate itself after the app is dead.
Here is the service:
Here is the service:
There are three parts relevant here: (i) the creation of the service and the initialisation of a counter (which is used to display if the service is alive or not) (ii) onStartCommand that will start the timer which will print the value of the counter every second (note: it returns START_STICKY - that is used to tell Android to try not to kill the service when resources are scarce: note Android can ignore this) (iii) onDestroy which will restart the service when killed. Ignore the Timer for now. Let's see the latter:
onDestroy is called when the service is stopped by the app (i.e. the app is killed wither by Android or by the user).
In this case, the service sends a message to the BroadcastReceiver which will restart the service after the service stop (it is an asynchronous call so it will not be affected by the death of the service.
The Broadcast receiver is defined in this way:
In this case, the service sends a message to the BroadcastReceiver which will restart the service after the service stop (it is an asynchronous call so it will not be affected by the death of the service.
The Broadcast receiver is defined in this way:
Very simple. When it gets the signal, it restarts the service in the provided context.
Now if you run the app when the phone is connected to AndroidStudio, you will see the counting printed out in the Log. When the app is killed, the service will be stopped, the broadcast receiver will print out
Service Stops! Oooooooooooooppppssssss!!!!
and the service will start again (by resetting the counter to zero because it is a new service!).
Now if you run the app when the phone is connected to AndroidStudio, you will see the counting printed out in the Log. When the app is killed, the service will be stopped, the broadcast receiver will print out
Service Stops! Oooooooooooooppppssssss!!!!
and the service will start again (by resetting the counter to zero because it is a new service!).
Download the code
The code is provide here; licence: MIT, so you can use it for any purpose.
Some Additional Points and FAQs
Why can't I simply send a message to the broadcast receiver directly from the Activity's onDestroy?
You can but in that case the service will not be restarted if your app is in the background and Android decides to kill your service because it needs resources. I.e. your service would not be guaranteed to work indefinitely
What is I do not want the counter to restart when the process is killed?
Yes this is the usual case. Well you cannot directly. The only way is to save the status of the service and to reload it when the service is started. You will do this by using the following code:
try {
SharedPreferences prefs= getSharedPreferences("uk.ac.shef.oak.ServiceRunning", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt("counter", counter);
editor.apply();
//Long.i("MoveMore", "Saving readings to preferences");
} catch (NullPointerException e) {
Log.e(TAG, "error saving: are you testing?" +e.getMessage());
ServerOperations.database.addError(e.getMessage());
}
in the inDestroy of the service and the following code in the onStartCommand of the service:
SharedPreferences prefs= ctx.getSharedPreferences("uk.ac.shef.oak.ServiceRunning", ctx.MODE_PRIVATE);
boolean found=prefs.getInt("counter", 0);
This should save the value of counter. Note that this is a general method: should you have a very large data structure, you can Gson it and save it as String.
What is the Timer in the SensorService class?
A timer wakes up every n seconds to perform a tasks. In this case it just increments counter and prints out its value.
You can but in that case the service will not be restarted if your app is in the background and Android decides to kill your service because it needs resources. I.e. your service would not be guaranteed to work indefinitely
What is I do not want the counter to restart when the process is killed?
Yes this is the usual case. Well you cannot directly. The only way is to save the status of the service and to reload it when the service is started. You will do this by using the following code:
try {
SharedPreferences prefs= getSharedPreferences("uk.ac.shef.oak.ServiceRunning", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt("counter", counter);
editor.apply();
//Long.i("MoveMore", "Saving readings to preferences");
} catch (NullPointerException e) {
Log.e(TAG, "error saving: are you testing?" +e.getMessage());
ServerOperations.database.addError(e.getMessage());
}
in the inDestroy of the service and the following code in the onStartCommand of the service:
SharedPreferences prefs= ctx.getSharedPreferences("uk.ac.shef.oak.ServiceRunning", ctx.MODE_PRIVATE);
boolean found=prefs.getInt("counter", 0);
This should save the value of counter. Note that this is a general method: should you have a very large data structure, you can Gson it and save it as String.
What is the Timer in the SensorService class?
A timer wakes up every n seconds to perform a tasks. In this case it just increments counter and prints out its value.