Bad notification for startForeground for Android 9 [duplicate] - android
After upgrading my phone to 8.1 Developer Preview my background service no longer starts up properly.
In my long-running service I've implemented a startForeground method to start the ongoing notification which is called in on create.
#TargetApi(Build.VERSION_CODES.O)
private fun startForeground() {
// Safe call, handled by compat lib.
val notificationBuilder = NotificationCompat.Builder(this, DEFAULT_CHANNEL_ID)
val notification = notificationBuilder.setOngoing(true)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.build()
startForeground(101, notification)
}
Error message:
11-28 11:47:53.349 24704-24704/$PACKAGE_NAMEE/AndroidRuntime: FATAL EXCEPTION: main
Process: $PACKAGE_NAME, PID: 24704
android.app.RemoteServiceException: Bad notification for startForeground: java.lang.RuntimeException: invalid channel for service notification: Notification(channel=My channel pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x42 color=0x00000000 vis=PRIVATE)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1768)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
invalid channel for service notification, apparently my old channel the DEFAULT_CHANNEL_ID is no longer appropriate for API 27 I assume. What would be the proper channel? I've tried to look through the documentation
After some tinkering for a while with different solutions i found out that one must create a notification channel in Android 8.1 and above.
private fun startForeground() {
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel("my_service", "My Background Service")
} else {
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
""
}
val notificationBuilder = NotificationCompat.Builder(this, channelId )
val notification = notificationBuilder.setOngoing(true)
.setSmallIcon(R.mipmap.ic_launcher)
.setPriority(PRIORITY_MIN)
.setCategory(Notification.CATEGORY_SERVICE)
.build()
startForeground(101, notification)
}
#RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(channelId: String, channelName: String): String{
val chan = NotificationChannel(channelId,
channelName, NotificationManager.IMPORTANCE_NONE)
chan.lightColor = Color.BLUE
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
service.createNotificationChannel(chan)
return channelId
}
From my understanding background services are now displayed as normal notifications that the user then can select to not show by deselecting the notification channel.
Update:
Also don't forget to add the foreground permission as required Android P:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
Java Solution (Android 9.0, API 28)
In your Service class, add this:
#Override
public void onCreate(){
super.onCreate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
startMyOwnForeground();
else
startForeground(1, new Notification());
}
private void startMyOwnForeground(){
String NOTIFICATION_CHANNEL_ID = "com.example.simpleapp";
String channelName = "My Background Service";
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE);
chan.setLightColor(Color.BLUE);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
assert manager != null;
manager.createNotificationChannel(chan);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
Notification notification = notificationBuilder.setOngoing(true)
.setSmallIcon(R.drawable.icon_1)
.setContentTitle("App is running in background")
.setPriority(NotificationManager.IMPORTANCE_MIN)
.setCategory(Notification.CATEGORY_SERVICE)
.build();
startForeground(2, notification);
}
UPDATE: ANDROID 9.0 PIE (API 28)
Add this permission to your AndroidManifest.xml file:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
The first answer is great only for those people who know kotlin, for those who still using java here I translate the first answer
public Notification getNotification() {
String channel;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
channel = createChannel();
else {
channel = "";
}
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, channel).setSmallIcon(android.R.drawable.ic_menu_mylocation).setContentTitle("snap map fake location");
Notification notification = mBuilder
.setPriority(PRIORITY_LOW)
.setCategory(Notification.CATEGORY_SERVICE)
.build();
return notification;
}
#NonNull
#TargetApi(26)
private synchronized String createChannel() {
NotificationManager mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
String name = "snap map fake location ";
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel mChannel = new NotificationChannel("snap map channel", name, importance);
mChannel.enableLights(true);
mChannel.setLightColor(Color.BLUE);
if (mNotificationManager != null) {
mNotificationManager.createNotificationChannel(mChannel);
} else {
stopSelf();
}
return "snap map channel";
}
For android, P don't forget to include this permission
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
Works properly on Andorid 8.1:
Updated sample (without any deprecated code):
public NotificationBattery(Context context) {
this.mCtx = context;
mBuilder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle(context.getString(R.string.notification_title_battery))
.setSmallIcon(R.drawable.ic_launcher)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setChannelId(CHANNEL_ID)
.setOnlyAlertOnce(true)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setWhen(System.currentTimeMillis() + 500)
.setGroup(GROUP)
.setOngoing(true);
mRemoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_view_battery);
initBatteryNotificationIntent();
mBuilder.setContent(mRemoteViews);
mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (AesPrefs.getBooleanRes(R.string.SHOW_BATTERY_NOTIFICATION, true)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, context.getString(R.string.notification_title_battery),
NotificationManager.IMPORTANCE_DEFAULT);
channel.setShowBadge(false);
channel.setSound(null, null);
mNotificationManager.createNotificationChannel(channel);
}
} else {
mNotificationManager.cancel(Const.NOTIFICATION_CLIPBOARD);
}
}
Old snipped (it's a different app - not related to the code above):
#Override
public int onStartCommand(Intent intent, int flags, final int startId) {
Log.d(TAG, "onStartCommand");
String CHANNEL_ONE_ID = "com.kjtech.app.N1";
String CHANNEL_ONE_NAME = "Channel One";
NotificationChannel notificationChannel = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
notificationChannel = new NotificationChannel(CHANNEL_ONE_ID,
CHANNEL_ONE_NAME, IMPORTANCE_HIGH);
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.setShowBadge(true);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.createNotificationChannel(notificationChannel);
}
Bitmap icon = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
Notification notification = new Notification.Builder(getApplicationContext())
.setChannelId(CHANNEL_ONE_ID)
.setContentTitle(getString(R.string.obd_service_notification_title))
.setContentText(getString(R.string.service_notification_content))
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(icon)
.build();
Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
notification.contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0);
startForeground(START_FOREGROUND_ID, notification);
return START_STICKY;
}
In my case, it's because we tried to post a notification without specifying the NotificationChannel:
public static final String NOTIFICATION_CHANNEL_ID_SERVICE = "com.mypackage.service";
public static final String NOTIFICATION_CHANNEL_ID_TASK = "com.mypackage.download_info";
public void initChannel(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nm.createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID_SERVICE, "App Service", NotificationManager.IMPORTANCE_DEFAULT));
nm.createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID_INFO, "Download Info", NotificationManager.IMPORTANCE_DEFAULT));
}
}
The best place to put above code is in onCreate() method in the Application class, so that we just need to declare it once for all:
public class App extends Application {
#Override
public void onCreate() {
super.onCreate();
initChannel();
}
}
After we set this up, we can use notification with the channelId we just specified:
Intent i = new Intent(this, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pi = PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID_INFO);
.setContentIntent(pi)
.setWhen(System.currentTimeMillis())
.setContentTitle("VirtualBox.exe")
.setContentText("Download completed")
.setSmallIcon(R.mipmap.ic_launcher);
Then, we can use it to post a notification:
int notifId = 45;
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nm.notify(notifId, builder.build());
If you want to use it as foreground service's notification:
startForeground(notifId, builder.build());
Thanks to #CopsOnRoad, his solution was a big help but only works for SDK
26 and higher. My app targets 24 and higher.
To keep Android Studio from complaining you need a conditional directly around the notification. It is not smart enough to know the code is in a method conditional to VERSION_CODE.O.
#Override
public void onCreate(){
super.onCreate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
startMyOwnForeground();
else
startForeground(1, new Notification());
}
private void startMyOwnForeground(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
String NOTIFICATION_CHANNEL_ID = "com.example.simpleapp";
String channelName = "My Background Service";
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE);
chan.setLightColor(Color.BLUE);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
assert manager != null;
manager.createNotificationChannel(chan);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
Notification notification = notificationBuilder.setOngoing(true)
.setSmallIcon(AppSpecific.SMALL_ICON)
.setContentTitle("App is running in background")
.setPriority(NotificationManager.IMPORTANCE_MIN)
.setCategory(Notification.CATEGORY_SERVICE)
.build();
startForeground(2, notification);
}
}
This worked for me. In my service class, I created the notification channel for android 8.1 as below:
public class Service extends Service {
public static final String NOTIFICATION_CHANNEL_ID_SERVICE = "com.package.MyService";
public static final String NOTIFICATION_CHANNEL_ID_INFO = "com.package.download_info";
#Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nm.createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID_SERVICE, "App Service", NotificationManager.IMPORTANCE_DEFAULT));
nm.createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID_INFO, "Download Info", NotificationManager.IMPORTANCE_DEFAULT));
} else {
Notification notification = new Notification();
startForeground(1, notification);
}
}
}
Note: Create the channel where you are creating the Notification for Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
Alternative answer: if it's a Huawei device and you have implemented requirements needed for Oreo 8 Android and there are still issues only with Huawei devices than it's only device issue, you can read https://dontkillmyapp.com/huawei
I had the same issue. The problem occurred when I used same channel id and notification id for two apps. So try with a unique notification id and channel id.
this maybe old but, incase someone had the same situation as I did.
for some reason, on Android 11 OnePlus Nord
Notification.Builder().Build()
crashes,
NotificationCompat.Builder().Build()
works fine.
Consider migrating to androidx.core.app.NotificationCompat.
If you're working with VPN library, these code will be help, I placed this inside onCreate(savedInstanceState: Bundle?)
NotificationChannelManager.createNotificationChannelIfNeeded(
activity,
channelName = "Chanel Name",
channelDescription = "Channel description"
)
Here is my solution
private static final int NOTIFICATION_ID = 200;
private static final String CHANNEL_ID = "myChannel";
private static final String CHANNEL_NAME = "myChannelName";
private void startForeground() {
final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
getApplicationContext(), CHANNEL_ID);
Notification notification;
notification = mBuilder.setTicker(getString(R.string.app_name)).setWhen(0)
.setOngoing(true)
.setContentTitle(getString(R.string.app_name))
.setContentText("Send SMS gateway is running background")
.setSmallIcon(R.mipmap.ic_launcher)
.setShowWhen(true)
.build();
NotificationManager notificationManager = (NotificationManager) getApplication().getSystemService(Context.NOTIFICATION_SERVICE);
//All notifications should go through NotificationChannel on Android 26 & above
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
}
notificationManager.notify(NOTIFICATION_ID, notification);
}
Hope it will help :)
Related
Disable badge for Notifications generated by calling startForgeground()
I have been facing issues with the notification badge when starting our foreground service on android 8 and above devices. I even tried disabling the badge using the Notification channel object but still, the issue persists. we are using below code block. #RequiresApi(api = Build.VERSION_CODES.O) private void startMyOwnForeground() { Log.d("ForegroundService", "inside startmyownforeground"); String NOTIFICATION_CHANNEL_ID = "custom_ID"; String channelName = "NOTIFICATION CHANNEL"; NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE); NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); assert manager != null; chan.setShowBadge(false); Log.d("Notification::", "From Foreground Service."); manager.createNotificationChannel(chan); Notification.Builder notificationBuilder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID); Log.d("Foregroundservice","log for cheking notif title"); Notification notification = notificationBuilder.setOngoing(true) .setSmallIcon(getIconResId()) .setContentTitle(NOTIFICATION_TEXT) .setContentText(NOTIFICATION_TEXT) .setTicker(NOTIFICATION_TEXT) .build(); startForeground(NOTIFICATION_ID, notification); Log.d("ForegroundService","end of startMyOwnForeground"); }
Notificaiton progressbar produces sound on update Android
I have been updating Notification Progress-bar like this. This is called multiple times. But Notification sound is produced multiple times. Which is annoying builder.setProgress(100, percentage, false); notificationManager.notify(notifycation.notificationId, notifycation.builder.build()); Here is Notification Code public void initProgressNotificaiton(Context context) { notificationManager = NotificationManagerCompat.from(context); builder = new NotificationCompat.Builder(context, ChannelId); builder.setContentTitle("Video Upload") .setOngoing(true) .setContentText("Upload in progress") .setSound(null) .setSmallIcon(R.drawable.upload) .setPriority(NotificationCompat.PRIORITY_LOW); notificationManager.notify(notificationId, builder.build()); } Channel Code public void createNotificationChannel(Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { CharSequence name = "naem"; String description = "desc"; int importance = NotificationManager.IMPORTANCE_DEFAULT; NotificationChannel channel = new NotificationChannel(ChannelId, name, importance); channel.setSound(null, null); channel.setDescription(description); NotificationManager notificationManager = context.getSystemService(NotificationManager.class); notificationManager.createNotificationChannel(channel); } How do i fix that? Tested on API 30 Emulator.
Add a flag to alert only once. Try this: Notification notification = builder.build(); notification.flags |= Notification.FLAG_ONLY_ALERT_ONCE; notificationManager.notify(notificationId, notification);
FCM push notification not working on some devices
We have created an chat app with notifications using FCM my code is correct my device is getting the push notification data as well but some Chinese manufactured device like vivo, oppo, one plus, xiaomi are not allowing the notification to show unless i add app in protected app list of respective manufacturer. is their any solution for this. https://hackernoon.com/notifications-in-android-are-horribly-broken-b8dbec63f48a https://github.community/t5/Project-Development-Help-and/Firebase-Push-Notification-not-receiving-on-some-android-devices/td-p/5489 private NotificationManagerCompat notificationManager; #Override public void onMessageReceived(RemoteMessage remoteMessage) { super.onMessageReceived(remoteMessage); Log.d("test","call"); notificationManager = NotificationManagerCompat.from(this); sendNotification(remoteMessage.getNotification().getTitle(), remoteMessage.getNotification().getBody()); } private void sendNotification(String title, String msg) { Intent resultIntent = new Intent(this, ActivitySplashScreen.class); String channelId = getString(R.string.chc); String channelName = "Message Notification"; TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); stackBuilder.addNextIntentWithParentStack(resultIntent); PendingIntent pendingIntent = stackBuilder.getPendingIntent(99, PendingIntent.FLAG_UPDATE_CURRENT); Notification notification = new NotificationCompat.Builder(this, channelId) .setSmallIcon(R.mipmap.ic_launcher_round) .setContentTitle(title) .setContentText(msg) .setPriority(NotificationCompat.PRIORITY_HIGH) .setCategory(NotificationCompat.CATEGORY_MESSAGE) .setContentIntent(pendingIntent) .build(); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { NotificationManager manager = getSystemService(NotificationManager.class); NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH); manager.createNotificationChannel(channel); } notificationManager.notify(10, notification); }
I can confirm that this implementation works well with One Plus and Xiaomi devices (we have plenty of users using our app from this devices, and no crashes or issues are created from them regarding FCM Notifications). Cannot confirm anything for Vivo or Oppo (so far, we do know we don't have users using this kind of devices). The most important thing is that the notification feature is well implemented if regarding the app is either in foreground or background. If someone wants to do a zoom on this matter, this article explains it on a easy way. Now, the code I use for FCM implementation in Android: //MyMessagingService public class MyFirebaseMessagingService extends FirebaseMessagingService { public static final String TAG = "Firebase Msg"; #Override public void onMessageReceived(RemoteMessage remoteMessage) { Map<String, String> data = remoteMessage.getData(); final LocalNotification localNotification = new LocalNotification(); //data --> entityId - entityType - pushNotificationType // Check if message contains a data payload. if (data.size() > 0) { localNotification.setEntityId(data.get("entityId")); localNotification.setEntityType(data.get("entityType")); localNotification.setPushNotificationType(data.get("pushNotificationType")); localNotification.setTitle(data.get("title")); localNotification.setBody(data.get("body")); localNotification.setIcon(data.get("icon")); localNotification.setDate(new Date().getTime()); localNotification.setRead(false); } if (localNotification.getEntityId() != null) { LocalNotification notificationRetrieved = FirebaseNotificationsHelper.insertLocalNotification(localNotification); FirebaseNotificationsHelper.createNotificationInStatus(notificationRetrieved); } } } //Create notification status static void createNotificationInStatus(LocalNotification localNotification) { String notificationChannelId = App.getContext().getString(R.string.default_notification_channel_id); String notificationChannelName = App.getContext().getString(R.string.default_notification_channel_name); String notificationChannelDescription = App.getContext().getString(R.string.default_notification_channel_description); NotificationCompat.Builder notificationBuilder; NotificationCompat.Builder notificationBuilderPublicVersion; notificationBuilder = new NotificationCompat.Builder(App.getContext(), notificationChannelId); notificationBuilderPublicVersion = new NotificationCompat.Builder(App.getContext(), notificationChannelId); notificationBuilder.setDefaults(NotificationCompat.DEFAULT_VIBRATE | NotificationCompat.DEFAULT_LIGHTS | NotificationCompat.DEFAULT_SOUND) .setWhen(System.currentTimeMillis()) .setSmallIcon(R.drawable.ic_stat_push_notif) .setTicker(localNotification.getTitle()) .setPriority(NotificationCompat.PRIORITY_HIGH) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setContentTitle(localNotification.getTitle()) .setContentText(localNotification.getBody()) .setStyle(new NotificationCompat.BigTextStyle().bigText(localNotification.getBody())) .setPublicVersion(notificationBuilderPublicVersion.setSmallIcon(R.drawable.ic_stat_push_notif).setContentTitle(localNotification.getTitle()). setWhen(System.currentTimeMillis()).setContentText(localNotification.getBody()).build()) .setGroup(NOTIFICATION_GROUP) .setVibrate(new long[]{1000, 1000, 1000, 1000, 1000}) .setAutoCancel(true); int notificationId = SharedPreferencesUtils.getInstance(App.getContext()).getIntValue(PREF_KEY_NOTIFICATION_ID, 0); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationManager notificationManager = (NotificationManager) App.getContext().getSystemService(Context.NOTIFICATION_SERVICE); NotificationChannel notificationChannel = new NotificationChannel(notificationChannelId, notificationChannelName, NotificationManager.IMPORTANCE_HIGH); // Configure the notification channel. notificationChannel.setDescription(notificationChannelDescription); notificationChannel.enableLights(true); notificationChannel.setLightColor(Color.RED); notificationChannel.setVibrationPattern(new long[]{0, 1000, 500, 1000}); notificationChannel.enableVibration(true); notificationChannel.setLockscreenVisibility(NotificationCompat.VISIBILITY_PUBLIC); notificationChannel.setImportance(NotificationManager.IMPORTANCE_HIGH); notificationManager.createNotificationChannel(notificationChannel); notificationManager.notify(notificationId, notificationBuilder.build()); } else { /* Kitkat and previous versions don't show notifications using NotificationManagerCompat */ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { NotificationManager notificationManager = (NotificationManager) App.getContext().getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.notify(notificationId, notificationBuilder.build()); } else { NotificationManagerCompat notificationManager = NotificationManagerCompat.from(App.getContext()); notificationManager.notify(notificationId, notificationBuilder.build()); } } notificationId++; SharedPreferencesUtils.getInstance(App.getContext()).setValue(PREF_KEY_NOTIFICATION_ID, notificationId); }
I donot think that there is a general solution for this since your app is based on google-FCM that means that the android device regularly comunicates with google-internet-services. As far as i know google-FCM is blocked (= not reachable) in china
Can a foreground service call a not ongoing notification (for api > 26)
Even if I'm using channels for those apis, I have a foreground service which seems not to be able to send not ongoing notifications on api > 26 (oreo). On api below, the notification shows correctly. Below is my code which send notifications : private void notification(String content) { Random random = new Random(); int id = random.nextInt(1000)+1; if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) { NotificationChannel chan = new NotificationChannel(STANDARD_NOTIFICATION_CHANNEL_ID, standardChannelName, NotificationManager.IMPORTANCE_NONE); chan.setLightColor(Color.BLUE); chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); assert manager != null; manager.createNotificationChannel(chan); NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, STANDARD_NOTIFICATION_CHANNEL_ID); Notification notification = notificationBuilder.setOngoing(false) .setSmallIcon(android.R.drawable.btn_dropdown) .setContentTitle(content) .setPriority(NotificationManager.IMPORTANCE_DEFAULT) .setCategory(Notification.CATEGORY_SERVICE) .build(); // TODO we need to see how to send not ongoing notifications in our case with API > 26 manager.notify(id,notification); } else { NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, STANDARD_NOTIFICATION_CHANNEL_ID); Notification notification = notificationBuilder.setOngoing(false) .setSmallIcon(R.drawable.ic_launcher_fury) .setContentTitle(content) .build(); NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); manager.notify(id,notification); } } The only notification which is correctly displayed is the ongoing notification which shows to the user that the service is in foreground. I call it like this : #Override public void onCreate() { super.onCreate(); Random random = new Random(); int id = random.nextInt(1000); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startMyOwnForeground(); } else { NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, FOREGROUND_NOTIFICATION_CHANNEL_ID); Notification notification = notificationBuilder.setOngoing(true) .setSmallIcon(R.drawable.ic_launcher_fury) .setContentTitle(getResources().getString(R.string.app_is_in_background)) .build(); startForeground(id, notification); } } #RequiresApi(Build.VERSION_CODES.O) private void startMyOwnForeground() { Random random = new Random(); int id = random.nextInt(1000); NotificationChannel chan = new NotificationChannel(FOREGROUND_NOTIFICATION_CHANNEL_ID, foregroundChannelName, NotificationManager.IMPORTANCE_NONE); chan.setLightColor(Color.BLUE); chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); assert manager != null; manager.createNotificationChannel(chan); NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, FOREGROUND_NOTIFICATION_CHANNEL_ID); Notification notification = notificationBuilder.setOngoing(true) .setSmallIcon(android.R.drawable.btn_dropdown) .setContentTitle(getResources().getString(R.string.app_is_in_background)) .setPriority(NotificationManager.IMPORTANCE_MIN) .setCategory(Notification.CATEGORY_SERVICE) .build(); startForeground(id, notification); } Do you have some clues ?
I guess your problem is here: NotificationChannel chan = new NotificationChannel(FOREGROUND_NOTIFICATION_CHANNEL_ID, foregroundChannelName, NotificationManager.IMPORTANCE_NONE); According to the JavaDoc of NotificationManager.IMPORTANCE_NONE /** * A notification with no importance: does not show in the shade. */ So, try to use a different importance level such as: NotificationManager.IMPORTANCE_DEFAULT NotificationChannel chan = new NotificationChannel(FOREGROUND_NOTIFICATION_CHANNEL_ID, foregroundChannelName, NotificationManager.IMPORTANCE_DEFAULT);
startForeground fail after upgrade to Android 8.1
After upgrading my phone to 8.1 Developer Preview my background service no longer starts up properly. In my long-running service I've implemented a startForeground method to start the ongoing notification which is called in on create. #TargetApi(Build.VERSION_CODES.O) private fun startForeground() { // Safe call, handled by compat lib. val notificationBuilder = NotificationCompat.Builder(this, DEFAULT_CHANNEL_ID) val notification = notificationBuilder.setOngoing(true) .setSmallIcon(R.drawable.ic_launcher_foreground) .build() startForeground(101, notification) } Error message: 11-28 11:47:53.349 24704-24704/$PACKAGE_NAMEE/AndroidRuntime: FATAL EXCEPTION: main Process: $PACKAGE_NAME, PID: 24704 android.app.RemoteServiceException: Bad notification for startForeground: java.lang.RuntimeException: invalid channel for service notification: Notification(channel=My channel pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x42 color=0x00000000 vis=PRIVATE) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1768) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6494) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) invalid channel for service notification, apparently my old channel the DEFAULT_CHANNEL_ID is no longer appropriate for API 27 I assume. What would be the proper channel? I've tried to look through the documentation
After some tinkering for a while with different solutions i found out that one must create a notification channel in Android 8.1 and above. private fun startForeground() { val channelId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { createNotificationChannel("my_service", "My Background Service") } else { // If earlier version channel ID is not used // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context) "" } val notificationBuilder = NotificationCompat.Builder(this, channelId ) val notification = notificationBuilder.setOngoing(true) .setSmallIcon(R.mipmap.ic_launcher) .setPriority(PRIORITY_MIN) .setCategory(Notification.CATEGORY_SERVICE) .build() startForeground(101, notification) } #RequiresApi(Build.VERSION_CODES.O) private fun createNotificationChannel(channelId: String, channelName: String): String{ val chan = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_NONE) chan.lightColor = Color.BLUE chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager service.createNotificationChannel(chan) return channelId } From my understanding background services are now displayed as normal notifications that the user then can select to not show by deselecting the notification channel. Update: Also don't forget to add the foreground permission as required Android P: <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
Java Solution (Android 9.0, API 28) In your Service class, add this: #Override public void onCreate(){ super.onCreate(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) startMyOwnForeground(); else startForeground(1, new Notification()); } private void startMyOwnForeground(){ String NOTIFICATION_CHANNEL_ID = "com.example.simpleapp"; String channelName = "My Background Service"; NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE); chan.setLightColor(Color.BLUE); chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); assert manager != null; manager.createNotificationChannel(chan); NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID); Notification notification = notificationBuilder.setOngoing(true) .setSmallIcon(R.drawable.icon_1) .setContentTitle("App is running in background") .setPriority(NotificationManager.IMPORTANCE_MIN) .setCategory(Notification.CATEGORY_SERVICE) .build(); startForeground(2, notification); } UPDATE: ANDROID 9.0 PIE (API 28) Add this permission to your AndroidManifest.xml file: <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
The first answer is great only for those people who know kotlin, for those who still using java here I translate the first answer public Notification getNotification() { String channel; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) channel = createChannel(); else { channel = ""; } NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, channel).setSmallIcon(android.R.drawable.ic_menu_mylocation).setContentTitle("snap map fake location"); Notification notification = mBuilder .setPriority(PRIORITY_LOW) .setCategory(Notification.CATEGORY_SERVICE) .build(); return notification; } #NonNull #TargetApi(26) private synchronized String createChannel() { NotificationManager mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE); String name = "snap map fake location "; int importance = NotificationManager.IMPORTANCE_LOW; NotificationChannel mChannel = new NotificationChannel("snap map channel", name, importance); mChannel.enableLights(true); mChannel.setLightColor(Color.BLUE); if (mNotificationManager != null) { mNotificationManager.createNotificationChannel(mChannel); } else { stopSelf(); } return "snap map channel"; } For android, P don't forget to include this permission <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
Works properly on Andorid 8.1: Updated sample (without any deprecated code): public NotificationBattery(Context context) { this.mCtx = context; mBuilder = new NotificationCompat.Builder(context, CHANNEL_ID) .setContentTitle(context.getString(R.string.notification_title_battery)) .setSmallIcon(R.drawable.ic_launcher) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setChannelId(CHANNEL_ID) .setOnlyAlertOnce(true) .setPriority(NotificationCompat.PRIORITY_MAX) .setWhen(System.currentTimeMillis() + 500) .setGroup(GROUP) .setOngoing(true); mRemoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_view_battery); initBatteryNotificationIntent(); mBuilder.setContent(mRemoteViews); mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); if (AesPrefs.getBooleanRes(R.string.SHOW_BATTERY_NOTIFICATION, true)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel(CHANNEL_ID, context.getString(R.string.notification_title_battery), NotificationManager.IMPORTANCE_DEFAULT); channel.setShowBadge(false); channel.setSound(null, null); mNotificationManager.createNotificationChannel(channel); } } else { mNotificationManager.cancel(Const.NOTIFICATION_CLIPBOARD); } } Old snipped (it's a different app - not related to the code above): #Override public int onStartCommand(Intent intent, int flags, final int startId) { Log.d(TAG, "onStartCommand"); String CHANNEL_ONE_ID = "com.kjtech.app.N1"; String CHANNEL_ONE_NAME = "Channel One"; NotificationChannel notificationChannel = null; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { notificationChannel = new NotificationChannel(CHANNEL_ONE_ID, CHANNEL_ONE_NAME, IMPORTANCE_HIGH); notificationChannel.enableLights(true); notificationChannel.setLightColor(Color.RED); notificationChannel.setShowBadge(true); notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); manager.createNotificationChannel(notificationChannel); } Bitmap icon = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); Notification notification = new Notification.Builder(getApplicationContext()) .setChannelId(CHANNEL_ONE_ID) .setContentTitle(getString(R.string.obd_service_notification_title)) .setContentText(getString(R.string.service_notification_content)) .setSmallIcon(R.mipmap.ic_launcher) .setLargeIcon(icon) .build(); Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class); notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); notification.contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0); startForeground(START_FOREGROUND_ID, notification); return START_STICKY; }
In my case, it's because we tried to post a notification without specifying the NotificationChannel: public static final String NOTIFICATION_CHANNEL_ID_SERVICE = "com.mypackage.service"; public static final String NOTIFICATION_CHANNEL_ID_TASK = "com.mypackage.download_info"; public void initChannel(){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); nm.createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID_SERVICE, "App Service", NotificationManager.IMPORTANCE_DEFAULT)); nm.createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID_INFO, "Download Info", NotificationManager.IMPORTANCE_DEFAULT)); } } The best place to put above code is in onCreate() method in the Application class, so that we just need to declare it once for all: public class App extends Application { #Override public void onCreate() { super.onCreate(); initChannel(); } } After we set this up, we can use notification with the channelId we just specified: Intent i = new Intent(this, MainActivity.class); i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); PendingIntent pi = PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT); NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID_INFO); .setContentIntent(pi) .setWhen(System.currentTimeMillis()) .setContentTitle("VirtualBox.exe") .setContentText("Download completed") .setSmallIcon(R.mipmap.ic_launcher); Then, we can use it to post a notification: int notifId = 45; NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); nm.notify(notifId, builder.build()); If you want to use it as foreground service's notification: startForeground(notifId, builder.build());
Thanks to #CopsOnRoad, his solution was a big help but only works for SDK 26 and higher. My app targets 24 and higher. To keep Android Studio from complaining you need a conditional directly around the notification. It is not smart enough to know the code is in a method conditional to VERSION_CODE.O. #Override public void onCreate(){ super.onCreate(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) startMyOwnForeground(); else startForeground(1, new Notification()); } private void startMyOwnForeground(){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ String NOTIFICATION_CHANNEL_ID = "com.example.simpleapp"; String channelName = "My Background Service"; NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE); chan.setLightColor(Color.BLUE); chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); assert manager != null; manager.createNotificationChannel(chan); NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID); Notification notification = notificationBuilder.setOngoing(true) .setSmallIcon(AppSpecific.SMALL_ICON) .setContentTitle("App is running in background") .setPriority(NotificationManager.IMPORTANCE_MIN) .setCategory(Notification.CATEGORY_SERVICE) .build(); startForeground(2, notification); } }
This worked for me. In my service class, I created the notification channel for android 8.1 as below: public class Service extends Service { public static final String NOTIFICATION_CHANNEL_ID_SERVICE = "com.package.MyService"; public static final String NOTIFICATION_CHANNEL_ID_INFO = "com.package.download_info"; #Override public void onCreate() { super.onCreate(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); nm.createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID_SERVICE, "App Service", NotificationManager.IMPORTANCE_DEFAULT)); nm.createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID_INFO, "Download Info", NotificationManager.IMPORTANCE_DEFAULT)); } else { Notification notification = new Notification(); startForeground(1, notification); } } } Note: Create the channel where you are creating the Notification for Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
Alternative answer: if it's a Huawei device and you have implemented requirements needed for Oreo 8 Android and there are still issues only with Huawei devices than it's only device issue, you can read https://dontkillmyapp.com/huawei
I had the same issue. The problem occurred when I used same channel id and notification id for two apps. So try with a unique notification id and channel id.
this maybe old but, incase someone had the same situation as I did. for some reason, on Android 11 OnePlus Nord Notification.Builder().Build() crashes, NotificationCompat.Builder().Build() works fine. Consider migrating to androidx.core.app.NotificationCompat.
If you're working with VPN library, these code will be help, I placed this inside onCreate(savedInstanceState: Bundle?) NotificationChannelManager.createNotificationChannelIfNeeded( activity, channelName = "Chanel Name", channelDescription = "Channel description" )
Here is my solution private static final int NOTIFICATION_ID = 200; private static final String CHANNEL_ID = "myChannel"; private static final String CHANNEL_NAME = "myChannelName"; private void startForeground() { final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder( getApplicationContext(), CHANNEL_ID); Notification notification; notification = mBuilder.setTicker(getString(R.string.app_name)).setWhen(0) .setOngoing(true) .setContentTitle(getString(R.string.app_name)) .setContentText("Send SMS gateway is running background") .setSmallIcon(R.mipmap.ic_launcher) .setShowWhen(true) .build(); NotificationManager notificationManager = (NotificationManager) getApplication().getSystemService(Context.NOTIFICATION_SERVICE); //All notifications should go through NotificationChannel on Android 26 & above if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT); notificationManager.createNotificationChannel(channel); } notificationManager.notify(NOTIFICATION_ID, notification); } Hope it will help :)