How to startForeground() without showing notification?

AndroidAndroid ServiceForeground

Android Problem Overview


I want to create a service and make it run in the foreground.

Most example codes have notifications on it. But I don't want to show any notification. Is that possible?

Can you give me some examples? Are there any alternatives?

My app service is doing mediaplayer. How to make system not kill my service except the app kill it itself (like pausing or stopping the music by button).

Android Solutions


Solution 1 - Android

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 - Android

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 - Android

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 - Android

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 - Android

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

Solution 6 - Android

You can hide notification on Android 9+ by using custom layout with layout_height = "0dp"

NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.custom_notif);
builder.setContent(remoteViews);
builder.setPriority(NotificationCompat.PRIORITY_LOW);
builder.setVisibility(Notification.VISIBILITY_SECRET);

custom_notif.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="0dp">
</LinearLayout>

Tested on Pixel 1, android 9. This solution doesn't work on Android 8 or less

Solution 7 - Android

Update: this no longer works in Android 4.3 and above


There is one workaround. Try creating notification without setting icon, and the notification would not show. Don't know how it works, but it does :)

    Notification notification = new NotificationCompat.Builder(this)
            .setContentTitle("Title")
            .setTicker("Title")
            .setContentText("App running")
            //.setSmallIcon(R.drawable.picture)
            .build();
    startForeground(101,  notification);

Solution 8 - Android

Block the foreground service notification

Most answers here either don't work, break the foreground service, or violate Google Play policies.

The only way to reliably and safely hide the notification is to have the user block it.

Android 4.1 - 7.1

The only way is to block all notifications from your app:

  1. Send user to app's details screen:

     Uri uri = Uri.fromParts("package", getPackageName(), null);
     Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(uri);
     startActivity(intent);
    
  2. Have user block app's notifications

Note this also blocks your app's toasts.

Android 8.0 - 8.1

It's not worth blocking the notification on Android O because the OS will just replace it with a "running in the background" or "using battery" notification.

Android 9+

Use a Notification Channel to block the service notification without affecting your other notifications.

  1. Assign service notification to notification channel

  2. Send user to notification channel's settings

     Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
         .putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName())
         .putExtra(Settings.EXTRA_CHANNEL_ID, myNotificationChannel.getId());
     startActivity(intent);
    
  3. Have user block channel's notifications

Solution 9 - Android

Update: this no longer works in Android 4.3 and above


I set the icon parameter to the constructor for Notification to zero, and then passed the resulting notification to startForeground(). No errors in the log and no notification shows up. I don't know, though, whether the service was successfully foregrounded--is there any way to check?

Edited: Checked with dumpsys, and indeed the service is foregrounded on my 2.3 system. Haven't checked with other OS versions yet.

Solution 10 - Android

version 4.3(18) and above hiding service notification is not possible , but you could disable the icon , version 4.3(18) and below is possible to hide the notification

Notification noti = new Notification();
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
    noti.priority = Notification.PRIORITY_MIN;
}
startForeground(R.string.app_name, noti);

Solution 11 - Android

I've found on Android 8.0 it's still possible by not using a notification channel.

public class BootCompletedIntentReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

                Intent notificationIntent = new Intent(context, BluetoothService.class);    
                context.startForegroundService(notificationIntent);

            } else {
                //...
            }

        }
    }
}

And in BluetoothService.class:

 @Override
    public void onCreate(){    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

            Intent notificationIntent = new Intent(this, BluetoothService.class);

            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

            Notification notification = new Notification.Builder(this)
                    .setContentTitle("Title")
                    .setContentText("App is running")
                    .setSmallIcon(R.drawable.notif)
                    .setContentIntent(pendingIntent)
                    .setTicker("Title")
                    .setPriority(Notification.PRIORITY_DEFAULT)
                    .build();

            startForeground(15, notification);

        }

    }

A persistent notification is not shown, however you will see the Android 'x apps are running in the background' notification.

Solution 12 - Android

Its a quite troublesome for developer's sometime client did not want permanent notification for foreground service. I have created a Fake notification to start the service after that I cancel that by notificationManager.cancel(1);

  final String NOTIFICATION_CHANNEL_ID = "com.exmaple.project";
    final String channelName = "Notification";
   @RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onCreate() {
    super.onCreate();
    stopForeground(true);
    Intent stopSelf = new Intent(this, Notification_Service.class);
    stopSelf.setAction("ACTION_STOP_SERVICE");
    PendingIntent pStopSelf = PendingIntent
            .getService(this, 0, stopSelf
                    , PendingIntent.FLAG_CANCEL_CURRENT);
    Notification notification;
    NotificationCompat.Action action =
            new NotificationCompat.Action.Builder(
                    0, "Close", pStopSelf
            ).build();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationChannel serviceChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "Notification One", NotificationManager.IMPORTANCE_DEFAULT);
        NotificationManager notificationManager = getSystemService(NotificationManager.class);
        notificationManager.createNotificationChannel(serviceChannel);
        notification = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_launcher_background)
                .setContentText("Welcome to App.")
                .setPriority(Notification.PRIORITY_MIN)
                .addAction(action)
                .build();
    } else {
        notification = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_launcher_background)
                .setContentTitle("App")
                .setContentText("Welcome to App.")
                .setPriority(Notification.PRIORITY_MIN)
                .addAction(action)
                .build();
    }
    NotificationManager notificationManager =
            (NotificationManager) getSystemService(Service.NOTIFICATION_SERVICE);
    notificationManager.notify(1, notification);
    startForeground(1, notification);
    notificationManager.cancel(1);
}

Sometime the permanent notification does not remove by notificationManager.cancel(1); for that I have add fake close action button.

Action button result:

 @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            super.onStartCommand(intent, flags, startId);
            if ("ACTION_STOP_SERVICE".equals(intent.getAction())) {
                stopSelf();
            }
     
            return START_STICKY;
        }

Start Service:

 if (!isMyServiceRunning()) {
       Intent serviceIntent = new Intent(this, Notification_Service.class);
                ContextCompat.startForegroundService(this, serviceIntent);
            }

Check if the service is already running.

private boolean isMyServiceRunning() {
        ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (Notification_Service.class.getName().equals(service.service.getClassName())) {
                return true;
            }
        }
        return false;
    }

Solution 13 - Android

The most suitable solution is to work with Notification Channel.

All you need to do is removing notificationManager.createNotificationChannel(channel) from your class.

val notificationManager =
            getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        val channel = NotificationChannel(
            notificationChannelId,
            "Endless Service notifications channel",
            NotificationManager.IMPORTANCE_HIGH
        ).let {
            it.description = "Endless Service channel"
            it.enableLights(true)
            it.lightColor = Color.RED
            it.enableVibration(true)
            it.vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
            it
        }
        notificationManager.createNotificationChannel(channel)

OR

by simply using notificationManager.deleteNotificationChannel("channel_id")

Although, removing a notification that used by a Foreground service is not recommended.

Solution 14 - Android

Even though it's not the direct question, many of you searching for this can easily solve your challenges of creating a persistent, long running task by using a WorkManager

As of 2022 it's Google's recommended API for running background tasks of all types (One-time, Periodic, Expedited, Foreground, etc).

Solution 15 - Android

Update: this no longer works in Android 7.1 and above


Here is a way to make your app 's oom_adj to 1 (Tested in ANDROID 6.0 SDK emulator). Add a temporary service, In your main service call startForgroundService(NOTIFICATION_ID, notificion). And then start the temporary service call startForgroundService(NOTIFICATION_ID, notificion) with same notification id again, after a while in the temporary service call stopForgroundService(true) to dismiss the onging ontification.

Solution 16 - Android

You can also declare your application as persistent.

<application
    android:icon="@drawable/icon"
    android:label="@string/app_name"
    android:theme="@style/Theme"
    *android:persistent="true"* >
</application>

This essentially sets your app at a higher memory priority, decreasing the probability of it being killed.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMuhammad Resna Rizki PratamaView Question on Stackoverflow
Solution 1 - AndroidKristopher MicinskiView Answer on Stackoverflow
Solution 2 - AndroidLior IluzView Answer on Stackoverflow
Solution 3 - AndroidSamView Answer on Stackoverflow
Solution 4 - AndroidAnggrayudi HView Answer on Stackoverflow
Solution 5 - AndroidSnicolasView Answer on Stackoverflow
Solution 6 - AndroidphnmnnView Answer on Stackoverflow
Solution 7 - AndroidVladimir PetrovskiView Answer on Stackoverflow
Solution 8 - AndroidSamView Answer on Stackoverflow
Solution 9 - AndroidAlexander PrussView Answer on Stackoverflow
Solution 10 - Androidvlad solView Answer on Stackoverflow
Solution 11 - AndroidPeteView Answer on Stackoverflow
Solution 12 - Androidahmad bajwaView Answer on Stackoverflow
Solution 13 - AndroidABHIMANGAL MSView Answer on Stackoverflow
Solution 14 - AndroidCodyView Answer on Stackoverflow
Solution 15 - AndroidTshungleeView Answer on Stackoverflow
Solution 16 - AndroidDenizenView Answer on Stackoverflow