How to startForeground() without showing notification?

Solution 1:

As a security feature of the Android platform, you cannot, under any circumstance, have a foregrounded service without also having a notification. This is because a foregrounded service consumes a heavier amount of resources and is subject to different scheduling constraints (i.e., it doesn't get killed as quickly) than background services, and the user needs to know what's possibly eating their battery. So, don't do this.

However, it is possible to have a "fake" notification, i.e., you can make a transparent notification icon (iirc). This is extremely disingenuous to your users, and you have no reason to do it, other than killing their battery and thus creating malware.

Solution 2:

Update: This was "fixed" on Android 7.1. https://code.google.com/p/android/issues/detail?id=213309

Since the 4.3 update, it's basically impossible to start a service with startForeground() without showing a notification.

You can, however, hide the icon using official APIs... no need for a transparent icon: (Use NotificationCompat to support older versions)

NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setPriority(Notification.PRIORITY_MIN);

I've made peace with the fact the notification itself still needs to be there but for who ever who still wants to hide it, I may have found a workaround for that as well:

  1. Start a fake service with startForeground() with the notification and everything.
  2. Start the real service you want to run, also with startForeground() (same notification ID)
  3. Stop the first (fake) service (you can call stopSelf() and in onDestroy call stopForeground(true)).

Voilà! No notification at all and your second service keeps running.

Solution 3:

This no longer works as of Android 7.1 and it may violate Google Play's developer policies.

Instead, have the user block the service notification.


Here's my implementation of the technique in the answer by Lior Iluz.

Code

ForegroundService.java

public class ForegroundService extends Service {

    static ForegroundService instance;

    @Override
    public void onCreate() {
        super.onCreate();

        instance = this;

        if (startService(new Intent(this, ForegroundEnablingService.class)) == null)
            throw new RuntimeException("Couldn't find " + ForegroundEnablingService.class.getSimpleName());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        instance = null;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

}

ForegroundEnablingService.java

public class ForegroundEnablingService extends Service {

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (ForegroundService.instance == null)
            throw new RuntimeException(ForegroundService.class.getSimpleName() + " not running");

        //Set both services to foreground using the same notification id, resulting in just one notification
        startForeground(ForegroundService.instance);
        startForeground(this);

        //Cancel this service's notification, resulting in zero notifications
        stopForeground(true);

        //Stop this service so we don't waste RAM.
        //Must only be called *after* doing the work or the notification won't be hidden.
        stopSelf();

        return START_NOT_STICKY;
    }

    private static final int NOTIFICATION_ID = 10;

    private static void startForeground(Service service) {
        Notification notification = new Notification.Builder(service).getNotification();
        service.startForeground(NOTIFICATION_ID, notification);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

}

AndroidManifest.xml

<service android:name=".ForegroundEnablingService" />
<service android:name=".ForegroundService" />

Compatibility

Tested and working on:

  • Official Emulator
    • 4.0.2
    • 4.1.2
    • 4.2.2
    • 4.3.1
    • 4.4.2
    • 5.0.2
    • 5.1.1
    • 6.0
    • 7.0
  • Sony Xperia M
    • 4.1.2
    • 4.3
  • Samsung Galaxy ?
    • 4.4.2
    • 5.X
  • Genymotion
    • 5.0
    • 6.0
  • CyanogenMod
    • 5.1.1

No longer working as of Android 7.1.

Solution 4:

Warning: although this answer appears to work, it in fact silently prevents your service from becoming a foreground service.

Original answer:


Just set your notification's ID to zero:

// field for notification ID
private static final int NOTIF_ID = 0;

    ...
    startForeground(NOTIF_ID, mBuilder.build());
    NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    mNotificationManager.cancel(NOTIF_ID);
    ...

A benefit you can get is, a Service will be able to runs on high priority without destroyed by Android system, unless on high memory pressure.

To make it work with Pre-Honeycomb and Android 4.4 and higher, make sure that you use NotificationCompat.Builder which provided by Support Library v7, instead of Notification.Builder.

EDIT

This code will not work anymore due to security reasons in newer api level

NotificationId cannot be set to "0" (which will cause the app to crash)

startForeground(1, notification)

This is the perfect way to show notification (recommended method)

But if you need it reagrdless of the recommended method then try removing the "notificationManager.createNotificationChannel("channel_id")" from your code.

OR USE notificationManager.removeNotificationChannel(channel)

Solution 5:

You can use this (as suggested by @Kristopher Micinski):

Notification note = new Notification( 0, null, System.currentTimeMillis() );
note.flags |= Notification.FLAG_NO_CLEAR;
startForeground( 42, note );

UPDATE:

Please note that this is not allowed anymore with Android KitKat+ releases. And keep in mind that this is more or less violating the design principle in Android that makes background operations visible to users as mentioned by @Kristopher Micinski