Notification from IntentService - android

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?

Related

Service started by AlarmManager gets restarted with null intent after application removed from apps tray

I'm beginner android programmer. While experimenting with background Services and background data download I came across this particular problem:
I'm using an AlarmManager to schedule repeating background data download.
public void setBackgroundDataService(Context context, long time_in_millis) {
Intent background_service_intent = new Intent(context, BackgroundDataService.class);
background_service_intent.putExtra("value", val);
PendingIntent pending_intent = PendingIntent.getService(context, BACKGROUND_DATA_SERVICE_ID , background_service_intent, PendingIntent.FLAG_UPDATE_CURRENT);
cancelBackgroundServiceAlarmIfExists(context, background_service_intent);
AlarmManager alarm_manager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
alarm_manager.setRepeating(AlarmManager.RTC, (System.currentTimeMillis() + time_in_millis), time_in_millis, pending_intent);
}
public static void cancelBackgroundServiceAlarmIfExists(Context context, Intent intent) {
// try to cancel Pending Intent if exists
try {
PendingIntent pending_intent = PendingIntent.getService(context, BACKGROUND_DATA_SERVICE_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarm_manager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
alarm_manager.cancel(pending_intent);
Log.d("DEBUG", "Background service terminated");
} catch (Exception e) {
e.printStackTrace();
}
}
Here is onStartCommand() from Service:
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("DEBUG", "Background service invoked");
if (intent == null)
return super.onStartCommand(intent, flags, startId);
Bundle extras = intent.getExtras();
some_value = extras.getString("value");
startDataDownload();
return super.onStartCommand(intent, flags, startId);
}
Data download is performed in AsyncTask
So the problem is:
Why after swiping the app from applications tray Service gets restarted and intent passed in arguments is null. Proper working of the Service relies on extras passed in the intent. At first my application would just crash (usually 2 times, I assume that something was trying to restart it, but crashed while trying to get extras from null intent). After some time Service would start properly and do its job without crashing (probably AlarmManager restarted it with proper intent extras after specified time). Then sporadically it would crash again. I managed to avoid crashes by checking whether intent is null or not. It seems to work. But the question remains.
Why is my Service getting restarted and null intent gets passed to it right after I swipe my app from application tray. Is there anything thet can be done about it? Is there a better way to download data periodically?
In onStartCommand() you are returning START_STICKY because of this:
return super.onStartCommand(intent, flags, startId);
START_STICKY tells Android that it should restart your Service if the Service is killed (for whatever reason). When you swipe your app from the list of recent tasks, Android kills the OS process hosting your app and then, because your Service returned START_STICKY, Android restarts your Service.
After restarting your Service, Android calls onStartCommand() with a null Intent parameter.
It looks like you are also not stopping your Service after the data download completes.
If you only want your Service to be restarted if it is killed while it is working on something, return START_REDELIVER_INTENT from onStartCommand().
The documentation for Service explains this:
For started services, there are two additional major modes of
operation they can decide to run in, depending on the value they
return from onStartCommand(): START_STICKY is used for services that
are explicitly started and stopped as needed, while START_NOT_STICKY
or START_REDELIVER_INTENT are used for services that should only
remain running while processing any commands sent to them.
I've faced this kind of problem recently. So what I did is make my service looks like a foreground task and that'll solve your problem. And to do that the best approach I can think of is using a notification. Here is the required documentation. And here is a sample code for the notification -
private void showNotification() {
String notificationTitle = "Notification Title";
String notificationContent = "Notification Content";
Intent notificationIntent = new Intent(getApplicationContext(), DashboardActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0,
notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(context, channelId)
.setTicker(AppConstants.App_Name)
.setSmallIcon(R.drawable.logo)
.setContentTitle(notificationTitle)
.setColor(getApplicationContext().getResources().getColor(R.color.colorWhite))
.setStyle(new NotificationCompat.BigTextStyle().bigText(notificationContent))
.setAutoCancel(false)
.setOngoing(true)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
}
If you like to stop this process just call void stopForeground (int flags) where ever you like. It won't kill your service but your process will no longer be a foreground task.

Can not dismiss notification of service created

I've created a service , And I also created a notification so when my service runs there is a notification for it.
Now , I want users to be able to swipe/dismiss the notification but when trying to do so I've encountered two problems:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
password = intent.getStringExtra("password");
number = intent.getStringExtra("number");
Intent activityIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0,
activityIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new Notification.Builder(this).
setContentTitle(getText(R.string.app_name)).
setContentText("Subject").
setContentInfo("Doing stuff in the background...").
setSmallIcon(R.drawable.ic).
setAutoCancel(true).
setContentIntent(pendingIntent).build();
startForeground(1, notification);
return super.onStartCommand(intent, flags, startId);
}
This code worked perfectly , the only problem with this code is that users cannot swipe dismiss the notification , So from searching around I found that I can fix it by replacing 'startForeground' function with
NotificationManager notificationManager =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0,notification);
And then it worked, I can swipe dismiss, But now I get a different problem , Once I close my application (using long press on middle button then close all applications)
My app thrrows a nullpointerexception few moments later pointing to the line :
password = intent.getStringExtra("password");
as if the intent is null. This does not happen when I use the startForeground function
What might be the problem ?
Return START_NOT_STICKY in onStartCommand().
There are three options to what should happen once the proccess in which the service is running on crushes:
START_STICKY , START_NOT_STICKY , START_REDELIVER_INTENT.
I wanted to reload the service and keep the intent once it crushes.
START_REDELIVER_INTENT did the trick, it restarts the service and redelivers the intent.
START_STICKY , Almost did the trick , the problem with START_STICKY for me was that it restarts the service but with a null intent.
AutoCancel does not work when service is still on foreground. Try remove service from foreground first:
startForeground(1, notification);
stopForeground(false); //false - do not remove generated notification

Android daily alarm firing too often or only once

I'm trying to implement a "daily reminder" function in my Android app, that should fire once per day at a set time. The first implementation I tried worked for most people, but some subset of users (including at least one person running on Android 4.3 on a Samsung) were reporting that the alarm was firing WAY more often than it should, e.g. every 10 minutes, and every time they opened the app, and just generally being very annoying.
Here's how the alarm is enabled:
Intent myIntent = new Intent(ctx, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(ctx, 0, myIntent,0);
AlarmManager alarmManager = (AlarmManager)ctx.getSystemService(Service.ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, sched,
AlarmManager.INTERVAL_DAY,
pendingIntent);
Then there's this AlarmReceiver class:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Intent service1 = new Intent(context, AlarmService.class);
context.startService(service1);
}
}
This is registered as a receiver in the AndroidManifest: <receiver android:name=".AlarmReceiver"/>
Finally there's the AlarmService, which used to look like this:
public class AlarmService extends Service {
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate()
{
// TODO Auto-generated method stub
super.onCreate();
}
#SuppressWarnings("static-access")
#Override
public void onStart(Intent intent, int startId)
{
super.onStart(intent, startId);
Log.v("pm", "about to notify");
Intent intent1 = new Intent(this.getApplicationContext(), MainActivity.class);
intent1.setAction(Intent.ACTION_MAIN);
intent1.addCategory(Intent.CATEGORY_LAUNCHER);
//intent1.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP| Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingNotificationIntent = PendingIntent.getActivity( this.getApplicationContext(),0, intent1,PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new Notification.Builder(this.getApplicationContext())
.setContentTitle("My App")
.setContentText("Don't forget that thing!")
.setSmallIcon(R.drawable.ic_launcher)
.setWhen(System.currentTimeMillis())
.setContentIntent(pendingNotificationIntent)
.getNotification();
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.defaults |= Notification.DEFAULT_SOUND;
notification.defaults |= Notification.DEFAULT_VIBRATE;
NotificationManager nManager =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nManager.notify(0, notification);
}
#Override
public void onDestroy()
{
// TODO Auto-generated method stub
super.onDestroy();
}
}
However, as I say, people were reporting that this fired every ten minutes or so! So I tried changing the AlarmService to a less deprecated implementation, but in the process now people are saying it only fires once, and then never again!
I replaced onStart with this:
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
Log.v("pm", "about to notify");
if (intent != null) {
Intent intent1 = new Intent(this.getApplicationContext(), MainActivity.class);
intent1.setAction(Intent.ACTION_MAIN);
intent1.addCategory(Intent.CATEGORY_LAUNCHER);
//intent1.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP| Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingNotificationIntent = PendingIntent.getActivity( this.getApplicationContext(),0, intent1,PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new Notification.Builder(this.getApplicationContext())
.setContentTitle("My App")
.setContentText("Don't forget that thing!")
.setSmallIcon(R.drawable.ic_launcher)
.setWhen(System.currentTimeMillis())
.setContentIntent(pendingNotificationIntent)
.getNotification();
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.defaults |= Notification.DEFAULT_SOUND;
notification.defaults |= Notification.DEFAULT_VIBRATE;
NotificationManager nManager =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nManager.notify(0, notification);
} else {
Log.v("pm", "Null Intent");
}
return START_STICKY;
}
Since I can't reproduce the original issue on my devices, it's a bit hard to test! My two theories are:
The problem lies in AlarmReceiver, like it ought to not be starting up a brand new service but doing something with the existing service
I shouldn't bother excluding null intent values in my onStartCommand function
I'm just a little nervous to try number 2 in case it causes people's devices to annoy them again!
Here's how the alarm is enabled
Note that this will be inexact on Android 4.4+, even though you are using setRepeating(), once you raise your android:targetSdkVersion to 19 or higher.
Then there's this AlarmReceiver class
That will not be reliable with a _WAKEUP-style alarm. It is eminently possible for the device to fall asleep between the startService() call and when your service actually gets a chance to do something. Please use WakefulBroadcastReceiver or my WakefulIntentService for _WAKEUP-style alarms if you are going to use the delegate-to-a-service pattern.
but in the process now people are saying it only fires once per day!
Since that is what you want, I would think that this is a good thing.
I replaced onStart with this:
I do not know why you are using a Service instead IntentService. Regardless, please call stopSelf() at the bottom of your onStartCommand() method, so the service goes away. There is no reason for this service to stay running once this work is completed. Also, replace START_STICKY with START_NOT_STICKY.
And, if this is all the work you intend to do in the service, you could dump the service entirely and move your onStartCommand() guts into onReceive() of the BroadcastReceiver.
The pattern of delegating work to a service from a receiver is used when the work will take too long to risk tying up the main application thread (e.g., >1ms)... but then your service needs a background thread, which yours lacks. Since I would expect your code to be less than 1ms in execution time, you could just do that in onReceive() and simplify your app, you would no longer need the separate Service, nor any of the Wakeful* stuff I mentioned earlier.
The problem lies in AlarmReceiver, like it ought to not be starting up a brand new service but doing something with the existing service
If this only runs once per day, there better not be an "existing service". There is no need for you to have a running process, tying up system RAM, just waiting for the clock to tick.
I shouldn't bother excluding null intent values in my onStartCommand function
You will get a null Intent if:
Your process was terminated before the service completed onStartCommand() for a startService() call, and
Your service had successfully run onStartCommand() before and returned START_STICKY
I'm more and more wondering why my AlarmReceiver creates a service, rather than just showing the notification directly.
Agreed. If you plan on lots more work, involving disk or network I/O, then use an IntentService (background thread, service stops itself automatically). Otherwise, I'd just put this in onReceive() and call it good.
I just wanted to add for all people who got problems with the AlarmManager and Android 4.4+ that it is truly important that you add the stopSelf();, like #CommonsWave already said, to the bottom of your onStartCommand() within Service which is called from the BroadcastReceiver

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

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.

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