Android: keep Service running when app is killed
All the answers seem correct so I'll go ahead and give a complete answer here.
Firstly, the easiest way to do what you are trying to do is launch a Broadcast in Android when the app is killed manually, and define a custom BroadcastReceiver
to trigger a service restart following that.
Now lets jump into code.
Create your Service in YourService.java
Note the onCreate()
method, where we are starting a foreground service differently for Build versions greater than Android Oreo. This because of the strict notification policies introduced recently where we have to define our own notification channel to display them correctly.
The this.sendBroadcast(broadcastIntent);
in the onDestroy()
method is the statement which asynchronously sends a broadcast with the action name "restartservice"
. We'll be using this later as a trigger to restart our service.
Here we have defined a simple Timer task, which prints a counter value every 1 second in the Log
while incrementing itself every time it prints.
public class YourService extends Service {
public int counter=0;
@Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
startMyOwnForeground();
else
startForeground(1, new Notification());
}
@RequiresApi(Build.VERSION_CODES.O)
private void startMyOwnForeground()
{
String NOTIFICATION_CHANNEL_ID = "example.permanence";
String channelName = "Background Service";
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE);
chan.setLightColor(Color.BLUE);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
assert manager != null;
manager.createNotificationChannel(chan);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
Notification notification = notificationBuilder.setOngoing(true)
.setContentTitle("App is running in background")
.setPriority(NotificationManager.IMPORTANCE_MIN)
.setCategory(Notification.CATEGORY_SERVICE)
.build();
startForeground(2, notification);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
startTimer();
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
stoptimertask();
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("restartservice");
broadcastIntent.setClass(this, Restarter.class);
this.sendBroadcast(broadcastIntent);
}
private Timer timer;
private TimerTask timerTask;
public void startTimer() {
timer = new Timer();
timerTask = new TimerTask() {
public void run() {
Log.i("Count", "========= "+ (counter++));
}
};
timer.schedule(timerTask, 1000, 1000); //
}
public void stoptimertask() {
if (timer != null) {
timer.cancel();
timer = null;
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
Create a Broadcast Receiver to respond to your custom defined broadcasts in Restarter.java
The broadcast with the action name "restartservice"
which you just defined in YourService.java
is now supposed to trigger a method which will restart your service. This is done using BroadcastReceiver
in Android.
We override the built-in onRecieve()
method in BroadcastReceiver
to add the statement which will restart the service. The startService()
will not work as intended in and above Android Oreo 8.1, as strict background policies will soon terminate the service after restart once the app is killed. Therefore we use the startForegroundService()
for higher versions and show a continuous notification to keep the service running.
public class Restarter extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.i("Broadcast Listened", "Service tried to stop");
Toast.makeText(context, "Service restarted", Toast.LENGTH_SHORT).show();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(new Intent(context, YourService.class));
} else {
context.startService(new Intent(context, YourService.class));
}
}
}
Define your MainActivity.java
to call the service on app start.
Here we define a separate isMyServiceRunning()
method to check the current status of the background service. If the service is not running, we start it by using startService()
.
Since the app is already running in foreground, we need not launch the service as a foreground service to prevent itself from being terminated.
Note that in onDestroy()
we are dedicatedly calling stopService()
, so that our overridden method gets invoked. If this was not done, then the service would have ended automatically after app is killed without invoking our modified onDestroy()
method in YourService.java
public class MainActivity extends AppCompatActivity {
Intent mServiceIntent;
private YourService mYourService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mYourService = new YourService();
mServiceIntent = new Intent(this, mYourService.getClass());
if (!isMyServiceRunning(mYourService.getClass())) {
startService(mServiceIntent);
}
}
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
Log.i ("Service status", "Running");
return true;
}
}
Log.i ("Service status", "Not running");
return false;
}
@Override
protected void onDestroy() {
//stopService(mServiceIntent);
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("restartservice");
broadcastIntent.setClass(this, Restarter.class);
this.sendBroadcast(broadcastIntent);
super.onDestroy();
}
}
Finally register them in your AndroidManifest.xml
All of the above three classes need to be separately registered in AndroidManifest.xml
.
Note that we define an intent-filter
with the action name as "restartservice"
where the Restarter.java
is registered as a receiver
.
This ensures that our custom BroadcastReciever
is called whenever the system encounters a broadcast with the given action name.
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<receiver
android:name="Restarter"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="restartservice" />
</intent-filter>
</receiver>
<activity android:name="MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="YourService"
android:enabled="true" >
</service>
</application>
This should now restart your service again if the app was killed from the task-manager. This service will keep on running in background as long as the user doesn't Force Stop
the app from Application Settings.
UPDATE: Kudos to Dr.jacky for pointing it out. The above mentioned way will only work if the onDestroy()
of the service is called, which might not be the case certain times, which I was unaware of. Thanks.
If your Service is started by your app then actually your service is running on main process. so when app is killed service will also be stopped. So what you can do is, send broadcast from onTaskRemoved
method of your service as follows:
Intent intent = new Intent("com.android.ServiceStopped");
sendBroadcast(intent);
and have an broadcast receiver which will again start a service. I have tried it. service restarts from all type of kills.