Running a foreground service in Android without a notification icon [duplicate] - android

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).

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.

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:
Start a fake service with startForeground() with the notification and everything.
Start the real service you want to run, also with startForeground() (same notification ID)
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.

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.

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)

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

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

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);

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:
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);
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.
Assign service notification to notification channel
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);
Have user block channel's notifications

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.

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);

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.

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;
}

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).

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.

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.

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.

Related

Removing buttons from foreground service notification

I have used foreground service but when the app is in bg, it shows task Completed which I dont want. How can I remove it? If this line (.addAction(R.drawable.ic_cancel, getString(R.string.remove_location_updates),
servicePendingIntent)) is removed, the bg service doesn't work. If this code is used: '.setContentIntent(servicePendingIntent)', when I click in the noti, the app doesn't open, noti closed and service stops. How can I solve it? Thanks in advance
private Notification getNotification() {
Intent intent = new Intent(this, LocationUpdatesService.class);
CharSequence text = Utils.getLocationText(mLocation);
// Extra to help us figure out if we arrived in onStartCommand via the notification or not.
intent.putExtra(EXTRA_STARTED_FROM_NOTIFICATION, true);
// The PendingIntent that leads to a call to onStartCommand() in this service.
PendingIntent servicePendingIntent = PendingIntent.getService(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent activityPendingIntent = PendingIntent.getActivity(this, 0,
new Intent(this, LiveTrack.class), 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.addAction(R.drawable.ic_cancel, getString(R.string.remove_location_updates),
servicePendingIntent)
.setContentIntent(activityPendingIntent)
// .setContentIntent(servicePendingIntent)
.setContentText("App name")
.setContentTitle(Utils.getLocationTitle(this))
.setOngoing(true)
.setPriority(Notification.PRIORITY_HIGH)
.setSmallIcon(R.mipmap.ic_launcher)
.setTicker(text)
.setWhen(System.currentTimeMillis());
// Set the Channel ID for Android O.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId(CHANNEL_ID); // Channel ID
}
return builder.build();
}
private void onNewLocation(Location location) {
mLocation = location;
// Notify anyone listening for broadcasts about the new location.
Intent intent = new Intent(ACTION_BROADCAST);
intent.putExtra(EXTRA_LOCATION, location);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
// Update notification content if running as a foreground service.
if (serviceIsRunningInForeground(this)) {
mNotificationManager.notify(NOTIFICATION_ID, getNotification());
}
}
public boolean serviceIsRunningInForeground(Context context) {
ActivityManager manager = (ActivityManager) context.getSystemService(
Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(
Integer.MAX_VALUE)) {
if (getClass().getName().equals(service.service.getClassName())) {
if (service.foreground) {
return true;
}
}
}
return false;
}
If you will look at official document, it says
For user-initiated work that need to run immediately and must execute
to completion, use a foreground service. Using a foreground service
tells the system that the app is doing something important and it
shouldn’t be killed. Foreground services are visible to users via a
non-dismissible notification in the notification tray.
Even in the services document it says
A foreground service performs some operation that is noticeable to the
user. For example, an audio app would use a foreground service to play
an audio track. Foreground services must display a Notification.
Foreground services continue running even when the user isn't
interacting with the app.
So it seems, there must be non-dismissible notification when you are using foreground services.

Want to hide Notification but getting Context.startForegroundService() did not then call Service.startForeground()

I want to hide notification from appearing when the service is running in the background. I am aware that as per Google Play rules we have to show notification but for my current app i dont want to display any notification.
Here is the code, which works fine and shows notification. But how to make it not show any notification:
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String NOTIFICATION_CHANNEL_ID = "com.app..";
String channelName = "App Background Service";
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE);
channel.setLightColor(Color.BLUE);
channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
assert manager != null;
manager.createNotificationChannel(channel);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
Notification notification = notificationBuilder.setOngoing(true)
.setSmallIcon(R.drawable.icon)
.setContentTitle("App is running in background")
.setPriority(NotificationManager.IMPORTANCE_MIN)
.setCategory(Notification.CATEGORY_SERVICE)
.build();
startForeground(0, notification);
} else
{
startForeground(0, new Notification());
}
}
catch(Exception e)
{
e.printStackTrace();
}
If i dont put the above code it gives error saying no startForeground found. And gets killed after 5 sec automatically.
Reason: Context.startForegroundService() did not then call
Service.startForeground()
I want the service not show any notification to user and finish its task silently without getting killed. Is that even possible in Build Version > 26?
I want the service not show any notification to user and finish its task silently without getting killed
Use JobIntentService and ensure that your work will get done in less than 10 minutes. Note that there may be some delay before the work begins (which will not count against that 10-minute limit).
Or, use IntentService and ensure that your work will get done in less than 1 minute.
Neither of those requires a foreground service.

Android O - Old start foreground service still working?

So, with Android O, you need to have your service running as a foreground service if you want to receive more than just a few location updates per hour.
I noticed that the old method for starting a foreground service does seem to work on O.
i.e.
startForeground(NOTIFICATION_ID, getNotification());
According to the behaviour changes guide here:
https://developer.android.com/preview/behavior-changes.html
The NotificationManager.startServiceInForeground() method starts a foreground service. The old way to start a foreground service no longer works.
Though the new method only works when targeting O, it seems that the old method still seems to work on an O device whether targeting O or not.
Edit
Including example:
The Google sample project LocationUpdatesForegroundService actually has a working example where you can see the issue first hand.
https://github.com/googlesamples/android-play-location/tree/master/LocationUpdatesForegroundService
The startForeground method seems to work without issue whether targeting and compiling against API level 25 OR targeting and compiling against O (as directed to here: https://developer.android.com/preview/migration.html#uya)
So, to reproduce:
Configure the app gradle as mentioned in the previous link
Open the app
Request location updates
Close app (either via back button or home button)
Service is running in foreground (shown by icon in notification shade). Location updates are coming through as expected (every 10 seconds) even on a device running O. What I am missing here?
This worked for me.
In Activity class, start service using startForegroundService() instead of startService()
Intent myService = new Intent(this, MyService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(myService);
} else {
startService(myService);
}
Now in Service class in onStartCommand() do as following
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
......
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
.setContentTitle(getString(R.string.app_name))
.setContentText(text)
.setAutoCancel(true);
Notification notification = builder.build();
startForeground(1, notification);
} else {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setContentTitle(getString(R.string.app_name))
.setContentText(text)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true);
Notification notification = builder.build();
startForeground(1, notification);
}
return START_NOT_STICKY;
}
Note: Using Notification.Builder instead of NotificationCompat.Builder made it work. Only in Notification.Builder you will need to provide Channel ID which is new feature in Android Oreo.
Hope it works!
EDIT:
If you target API level 28 or higher, you need FOREGROUND_SERVICE permission otherwise, your app will crash.
Just add this to the AndroidManifest.xml file.
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
In the Activity (or any context that starts the foreground service), call this:
Intent intent = new Intent(this, MyService.class)
ContextCompat.startForegroundService(context, intent);
When the service has started, create a notification channel using similar code to what Android docs say, and then create a builder and use it:
final Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID).setSmallIcon(...)//
.setPriority(...).setCategory(...).setContentTitle(...).setContentText(...).setTicker(...);
// and maybe other preparations to the notification...
startForeground(notificationId, builder.build());
Usually you start your service from a broadcast receiver using startService. They are saying that it's no more possible (or reliable) to call startService because there are now background limitations, so you need to call startServiceInForeground instead. However from docs it's not really clear when it happens because the app is whitelisted when it receives a broadcast intent, so it's not clear exactly when startService throws IllegalStateException.
The legacy way of starting a foreground service still works when the app is in the foreground but the recommended way to start a foreground service for Apps targeting API level 26/Android O is to use the newly introduced NotificationManager#startServiceInForeground method to create a foreground service in the first place.
The old way of starting the service in the background and then promoting it to the foreground will not work if the app is in background mode, because of the background execution limitations of Android O.
Migration process and steps are documented here. https://developer.android.com/preview/features/background.html#migration
As also #Kislingk mentioned in a comment NotificationManager.startServiceInForeground was removed. It was marked as deprecated w/ the commit 08992ac.
From the commit message:
Rather than require an a-priori Notification be supplied in order to
start a service directly into the foreground state, we adopt a two-stage
compound operation for undertaking ongoing service work even from a
background execution state. Context#startForegroundService() is not
subject to background restrictions, with the requirement that the
service formally enter the foreground state via startForeground() within
5 seconds. If the service does not do so, it is stopped by the OS and
the app is blamed with a service ANR.
I add sample if some need with backstack builder
val notifyManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
val playIntent = Intent(this, this::class.java).setAction(PAUSE)
val cancelIntent = Intent(this, this::class.java).setAction(EXIT)
val stop = PendingIntent.getService(this, 1, playIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val exit = PendingIntent.getService(this, 2, cancelIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notifyManager.createNotificationChannel(NotificationChannel(NOTIFICATION_ID_CHANNEL_ID, getString(R.string.app_name), NotificationManager.IMPORTANCE_HIGH))
NotificationCompat.Builder(this, NOTIFICATION_ID_CHANNEL_ID)
} else
NotificationCompat.Builder(this)
builder.apply {
setContentTitle(station.name)
setContentText(metaToText(meta) )
setSmallIcon(R.drawable.ic_play_arrow_white_24px)
setAutoCancel(false)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) priority = Notification.PRIORITY_MAX
addAction(R.drawable.ic_stop_white_24px, getString(R.string.player_notification_stop), stop)
addAction(R.drawable.ic_close_white_24px, getString(R.string.player_notification_exit), exit)
}
val stackBuilder = TaskStackBuilder.create(this)
stackBuilder.addParentStack(PlayerActivity::class.java)
stackBuilder.addNextIntent(Intent(this, PlayerActivity::class.java))
builder.setContentIntent(stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT))
startForeground(NOTIFICATION_ID, builder.build())
startForeground(1, notification); will work for on Android O but as per Android O requirement we have to show a persistent notification to the user. At the same time it might confuse the user in some cases (system notification about App running in background and impacting battery) and so user may uninstall the app.
So best would be use newly introduce WorkManager class to schedule the task as foreground.
Create your worker class (say MyWorker) by extending "Worker" class where you can perform the long running task. Override below method in this class:
doWork() [Required]
onStopped() [Optional]
onWorkFinished [Optional]
etc.
Create repeating/periodic [PeriodicWorkRequest] or non-repeating [OneTimeWorkRequest] work as per requirement.
Get the instance of WorkManager and en queue the work.
Code snippet:
OneTimeWorkRequest work =
new OneTimeWorkRequest.Builder(MyWorker.class)
.build();
WorkManager.getInstance().enqueue(work);
In android O , android have background restrictions so we have to manage or call the startForegroundService(service) method instead of startSetvice()
Add permission in manifest
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
// We start the service like
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
var service = Intent(context, AnyService::class.java)
context?.startForegroundService(service)
} else {
var service = Intent(context, AnyService::class.java)
context?.startService(service)
}
in side AnyService class
class AnyService : Service() {
override fun onBind(intent: Intent?): IBinder? {
}
override fun onCreate() {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
startMyOwnForeground()
else
startForeground(1, Notification())
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
}
#RequiresApi(Build.VERSION_CODES.O)
private fun startMyOwnForeground() {
val NOTIFICATION_CHANNEL_ID = "example.permanence"
val channelName = "Background Service"
val chan = NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE)
chan.lightColor = Color.BLUE
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(chan)
val notificationBuilder = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
val notification = notificationBuilder.setOngoing(true)
.setContentTitle("App is running in background")
.setPriority(NotificationManager.IMPORTANCE_MIN)
.setCategory(Notification.CATEGORY_SERVICE)
.build()
startForeground(2, notification)
}
}

Notification from IntentService

I developed an app which should remind me at a certain time. Therefore I implemented an IntentService which starts a notification. The problem is that the notification will be created while the app is in foreground or at least in background open. If I close the app using the task manager the notification is no longer running.
Am I using the wrong service? Or do I have to create something else?
in the intentservice class:
private static final String serviceName = "sebspr.de.deadlines.DeadLineService";
public DeadLineService() {
super(serviceName);
}
#Override
protected void onHandleIntent(Intent intent) {
Context ctx = getApplicationContext();
Intent intent = new Intent(ctx, DeadLineService.class);
PendingIntent pIntent = PendingIntent.getActivity(ctx, 0, intent, 0);
String title = getTitle(list.size());
String text = getText(list);
Notification noti = new Notification.Builder(ctx)
.setContentTitle(title)
.setContentText(text).setSmallIcon(R.mipmap.ic_notfi)
.setContentIntent(pIntent)
.build();
NotificationManager notificationManager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
// hide the notification after its selected
noti.flags |= Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(0, noti);
}
int the MainActivity:
Intent msgIntent = new Intent(this, DeadLineService.class);
startService(msgIntent);
UPDATE
Considering my actual problem the solution is, to take a look at the AlarmManager. The IntentService is here the wrong way to go. I found a good tutorial here: AlarmManger Example
You shouldn't worry about a user killing the app via the task manager. If that happens, you really shouldn't start back up automatically. It isn't a user-friendly way to do things.
What you should worry about is a system restart in which case you can register your app to be notified when the device turns on.
Check out this answer for more details on starting your service when the phone turns on.
Also start your service sticky
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
Actually u can try to keep your application alive after killed but it may annoy your users a bit... So, Look out...
How to save Alarm after app killing?

How to startForeground() without showing notification?

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).
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.
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:
Start a fake service with startForeground() with the notification and everything.
Start the real service you want to run, also with startForeground() (same notification ID)
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.
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.
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)
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
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
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);
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:
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);
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.
Assign service notification to notification channel
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);
Have user block channel's notifications
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.
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);
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.
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;
}
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).
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.
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.
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.

Categories

Resources