Skip to content

Instantly share code, notes, and snippets.

@varunon9
Last active December 15, 2024 06:51
Show Gist options
  • Save varunon9/f2beec0a743c96708eb0ef971a9ff9cd to your computer and use it in GitHub Desktop.
Save varunon9/f2beec0a743c96708eb0ef971a9ff9cd to your computer and use it in GitHub Desktop.
How to create an always running service in Android

Full Source Code: https://github.com/varunon9/DynamicWallpaper/tree/always_running_service

Steps-

  1. Create a Foreground Service (MyService.java)
  2. Create a Manifest registered Broadcast Receiver (MyReceiver.java) which will start your Foreground Service
  3. In onDestroy lifecycle of MyService, send a broadcast intent to MyReceiver
  4. Launch the MyService on app start from MainActivity (see step 8)
  5. With above 4 steps, MyService will always get re-started when killed as long as onDestroy of Service gets called
  6. onDestroy method of Service is not always guaranteed to be called and hence it might not get started again
  7. To overcome this step, register a unique periodic Background Job via WorkManager which will restart MyService if not already started
  8. Register this UniquePeriodicWork (this will run every ~16 minutes) from MainActivity and it will also be responsible for the first launch of MyService
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="varunon9.me.dynamicwallpaper">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="false"></receiver>
<service
android:name=".MyService"
android:enabled="true"
android:exported="true" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
package varunon9.me.dynamicwallpaper;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.work.ExistingPeriodicWorkPolicy;
import androidx.work.PeriodicWorkRequest;
import androidx.work.WorkManager;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import java.util.concurrent.TimeUnit;
public class MainActivity extends AppCompatActivity {
private String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate called");
setContentView(R.layout.activity_main);
startServiceViaWorker();
}
public void onStartServiceClick(View v) {
startService();
}
public void onStopServiceClick(View v) {
stopService();
}
@Override
protected void onDestroy() {
Log.d(TAG, "onDestroy called");
stopService();
super.onDestroy();
}
public void startService() {
Log.d(TAG, "startService called");
if (!MyService.isServiceRunning) {
Intent serviceIntent = new Intent(this, MyService.class);
ContextCompat.startForegroundService(this, serviceIntent);
}
}
public void stopService() {
Log.d(TAG, "stopService called");
if (MyService.isServiceRunning) {
Intent serviceIntent = new Intent(this, MyService.class);
stopService(serviceIntent);
}
}
public void startServiceViaWorker() {
Log.d(TAG, "startServiceViaWorker called");
String UNIQUE_WORK_NAME = "StartMyServiceViaWorker";
WorkManager workManager = WorkManager.getInstance(this);
// As per Documentation: The minimum repeat interval that can be defined is 15 minutes
// (same as the JobScheduler API), but in practice 15 doesn't work. Using 16 here
PeriodicWorkRequest request =
new PeriodicWorkRequest.Builder(
MyWorker.class,
16,
TimeUnit.MINUTES)
.build();
// to schedule a unique work, no matter how many times app is opened i.e. startServiceViaWorker gets called
// do check for AutoStart permission
workManager.enqueueUniquePeriodicWork(UNIQUE_WORK_NAME, ExistingPeriodicWorkPolicy.KEEP, request);
}
}
package varunon9.me.dynamicwallpaper;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;
public class MyReceiver extends BroadcastReceiver {
private String TAG = "MyReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive called");
// We are starting MyService via a worker and not directly because since Android 7
// (but officially since Lollipop!), any process called by a BroadcastReceiver
// (only manifest-declared receiver) is run at low priority and hence eventually
// killed by Android.
WorkManager workManager = WorkManager.getInstance(context);
OneTimeWorkRequest startServiceRequest = new OneTimeWorkRequest.Builder(MyWorker.class)
.build();
workManager.enqueue(startServiceRequest);
}
}
package varunon9.me.dynamicwallpaper;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import androidx.core.app.NotificationCompat;
public class MyService extends Service {
private String TAG = "MyService";
public static boolean isServiceRunning;
private String CHANNEL_ID = "NOTIFICATION_CHANNEL";
public MyService() {
Log.d(TAG, "constructor called");
isServiceRunning = false;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate called");
createNotificationChannel();
isServiceRunning = true;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand called");
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Service is Running")
.setContentText("Listening for Screen Off/On events")
.setSmallIcon(R.drawable.ic_wallpaper_black_24dp)
.setContentIntent(pendingIntent)
.setColor(getResources().getColor(R.color.colorPrimary))
.build();
startForeground(1, notification);
return START_STICKY;
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String appName = getString(R.string.app_name);
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
appName,
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy called");
isServiceRunning = false;
stopForeground(true);
// call MyReceiver which will restart this service via a worker
Intent broadcastIntent = new Intent(this, MyReceiver.class);
sendBroadcast(broadcastIntent);
super.onDestroy();
}
}
package varunon9.me.dynamicwallpaper;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
public class MyWorker extends Worker {
private final Context context;
private String TAG = "MyWorker";
public MyWorker(
@NonNull Context context,
@NonNull WorkerParameters params) {
super(context, params);
this.context = context;
}
@NonNull
@Override
public Result doWork() {
Log.d(TAG, "doWork called for: " + this.getId());
Log.d(TAG, "Service Running: " + MyService.isServiceRunning);
if (!MyService.isServiceRunning) {
Log.d(TAG, "starting service from doWork");
Intent intent = new Intent(this.context, MyService.class);
ContextCompat.startForegroundService(context, intent);
}
return Result.success();
}
@Override
public void onStopped() {
Log.d(TAG, "onStopped called for: " + this.getId());
super.onStopped();
}
}
@busslina
Copy link

busslina commented May 30, 2023

Well, I will make some points:

  • Still in 2023, as developers we can't make a non-stop service on mobile phones. This is not serious. This is a joke. Yes, there is battery issues, but if the user allows an app to run forever, it must be done like this. A special permission "App can Run forever". There is "REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" but this is still the same joke.
  • I know an app (it is not in the Google Play store) that runs forever in the foreground notification mode. I am not an Android expert, now I'm focused on Flutter, and if someone want to help every body and study this app it would be great. It remains active for 4 days easily... Sending location to remote server every 5 seconds....
    https://gpslogger.app/
    https://github.com/mendhak/gpslogger/
    mendhak/gpslogger#1080
  • I think the same people that don't want us to eat meat, to drive cars, to own a house... are the same that are fucking us with this shit. (1984-2030)
  • In Flutter, there is a package that I think is near to do the trick. I will update in few days.
    https://pub.dev/packages/flutter_foreground_task

urbandroid-team/dont-kill-my-app#639

@moradi-morteza
Copy link

I read telegram source code, and test it, they had a non-stop service, how they can do it?

@busslina
Copy link

I read telegram source code, and test it, they had a non-stop service, how they can do it?

Maybe having their app on manufacturers white list

@varunon9
Copy link
Author

I read telegram source code, and test it, they had a non-stop service, how they can do it?

Maybe having their app on manufacturers white list

This can be one possibility. But I don't think this is the only solution.
https://github.com/varunon9/AarogyaSetu_Android This app has an unstoppable service (even if you kill it, it'll appear again in a few seconds). I don't think they are whitelisted by manufacturers. I had talked to early developers of this app and they didn't mention anything about the manufacturer's white list. They pointed me toward standard documentation and I went through code as well to figure it out, but couldn't make any breakthrough due to my limited understanding of native Android.

@busslina
Copy link

busslina commented Jun 20, 2023

I read telegram source code, and test it, they had a non-stop service, how they can do it?

Maybe having their app on manufacturers white list

This can be one possibility. But I don't think this is the only solution. https://github.com/varunon9/AarogyaSetu_Android This app has an unstoppable service (even if you kill it, it'll appear again in a few seconds). I don't think they are whitelisted by manufacturers. I had talked to early developers of this app and they didn't mention anything about the manufacturer's white list. They pointed me toward standard documentation and I went through code as well to figure it out, but couldn't make any breakthrough due to my limited understanding of native Android.

The fact that it will appear again in a few seconds after being killed can indicate that is using Android Alarm Manager that executes periodically and checks if it needs to be relaunched. I do something similar but I'm not sure that the application that you mentioned is doing that after seeing his Android Manifest file.

You can see here the second service has an intent filter related with Firebase, so maybe it is restarted via FCM.

<service android:name="nic.goi.aarogyasetu.background.BluetoothScanningService" />
<service
    android:name="nic.goi.aarogyasetu.FcmMessagingService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

@perfeshnal
Copy link

Thank you Varun kumar to sharing source code
your code problem is we can't stop service manually either
I have some ideas, but I wanted to ask your opinion as well

@ally9335
Copy link

ally9335 commented Mar 3, 2024

I have a service and no Activity. When I need to run it, I do it via adb as "start-foreground-service".
My phone is rooted.

How would I make a service run indefinitely and restart itself when it crashes or gets killed? With no activity.

This solution utilises an Activiy. But is there a way to do it without one?

@ronenfe
Copy link

ronenfe commented Apr 20, 2024

16 minutes is too long. I'm monitoring the call log and I don't want to miss calls in the middle. Also foreground service will take space in the notification area which is annoying. This code used to work 12 years ago and on blackberry devices. This google system is so badly designed.

@busslina
Copy link

I resolved the issue by using LineageOS, a non-fvuked Android ROM/OS. You can have you service for years without the OS killing it. You have to have a compatible device (there are a lot, I bought one specifically to test it).

This is for you Google, and all the manufacturers:
torvaldsnvidia-640x424

@DjuroRad
Copy link

DjuroRad commented Oct 16, 2024

I am wondering why no one mentioned the following issue:

startForegroundService failed android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service com.example.sdk/com.example.sdk.proxy.service.MyForegroundService

This happens for both the MyWorkManager and in onDestroy() lifecycle callback of the MyForegroundService
Do you have any ideas if this could somehow be resolved?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment