startForeground fail after upgrade to Android 8.1
Asked Answered
E

13

269

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

Engine answered 28/11, 2017 at 12:32 Comment(1)
This answer was my solution.Semiaquatic
E
340

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" />
Engine answered 28/11, 2017 at 13:57 Comment(19)
Do we need to make these changes in case of JobIntentService? Or it is handling it internally?Sadfaced
why not IMPORTANCE_DEFAULT instead of IMPORTANCE_NONE?Breeks
@Sub6Resources well it's not that popular as swift (which is really replaces objective-c fast), kotlin is much older than swift (and so? did it replace java as swift did with objective-c?), but still isn't that popular and its rating on Tiobe index site is very low (swift rating is much higher)Breeks
@Breeks Kotlin is actually a newer language than Swift. Kotlin doesn't replace Java, it is just an alternative to Java for Android development. If you try it out, you'll see it is actually quite similar in syntax to Swift. I personally believe it is better than Java, despite what the Tiobe index says (the index is subject to a little bit of non-response bias). It fixes many of the issues that Java has, including the dreaded NullPointerException, verbosity, and several other things. According to the latest Google I/O, 95% of developers who use Kotlin for Android are happy with it.Pooi
@Sub6Resources one thing disappoints me, Kotlin can replace Java in Android at some point (Google does great job), but it's bad that we still have to deal with Oracle Java for JVM developing in other areas (not mobile, but desktop/server/embedded apps). But now we have two languages... Yeah I know that Kotlin/Swift are very similar, I don't know who plagiarises the most but Kotlin was released (First appeared 2011) much sooner than Swift (First appeared 2014)Breeks
For me, the line NotificationCompat.Builder(this, channelId ) produces error, since my min sdk is 14 while this constructor overload does not exist yet.Marlie
This should be called from onCreate() of your serviceAdolescent
This is generating a notification in my status bar " App name is running Tap for more info" which is not cancelable. When I am giving channel in my notification. Any fix for that?Medawar
Unfortunately, this code fails in Android 9.0 (API 28).Frisket
@Frisket In what way? I'm currently using it my project targeting API 28.Engine
I am probably your well wisher because this will cause crashes (caused in my app too by many Pixel users who updated it to Android 9.0 and gave 1 star ratings). I found the solution of why. Please see my updated answer.Frisket
@Frisket I'm not sure what you are doing in you app, but I've a Google Pixel XL with 9.0 (And Google Pixel 2 XL) and it is working fine. Also tried with a Oneplus 6 with 9.0. Do you still have a requiresApi annotation?Engine
@Engine Well even I am not sure what you are doing with your Foreground service in the app because the Documentation doesn't lie. It clearly states, you will get SecurityException if you try to create foreground service without the permission in the manifest.Frisket
That is correct, this issue was a answer for Android 8.1 though. At that point the API for 9.0 wasn't set and the permission not required. Furthermore, you shouldn't get the same "java.lang.RuntimeException: invalid channel for service notification" runtime error for a permission error. I'll gladly update the response to help others though.Engine
@Engine How to do not show notification like App is running in the background?Aegyptus
@135 With a foreground service you can't, it needs to have a notification consistently not to be killed. These are new a restrictions from Oreo and newer, to ensure apps are not draining your battery.Engine
@Rawa: sir i got above error in ionic3 can you tell me how to implement above code in ionic3?Divulgence
it won't help for many Huawei devices anywayBreeks
Invalid channel id also gives this exception (if no channel with that Id is started)Carin
F
186

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" />
Frisket answered 11/7, 2018 at 8:59 Comment(14)
Is there a reason for using a unique IDs in the two startForeground( ) calls? Can't they be the same here since its the same notification?Curbstone
@Frisket so there's no need of notification channel for O?Chagall
@Chagall You need to add permission along with the code for Android 9.0. Both are needed.Frisket
@Frisket Thanks, added that! But if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) should be if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) cause acc to doc: Starting in Android 8.0 (API level 26), all notifications must be assigned to a channel.Chagall
@Frisket I am getting this exception for API level 26 and above(Oreo and Pie)Chagall
@Frisket This is the exception 'Fatal Exception: android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()'Chagall
Could you please post the question separately, I will definitely help you.Frisket
Let us continue this discussion in chat.Chagall
@SujithManjavana it just saying that manager should not be nullFrisket
Is it possible to avoid displaying the notification while the service is running ?Pickup
sometimes it works without <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />Tippler
@AmitVaghela It can only work if you have targetSdkVersion less than 28.Frisket
It's works, but i have a notification when the app start. How remove it ?Lyons
I have a simple question, let's say I want to do a specific code when my service is running. Where should I put this code? Like getting the location or something like that. After creating the Notification ?Monoacid
I
39

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" />
Impair answered 15/7, 2018 at 16:4 Comment(2)
Thanks for translating the code to Java. It's a big help for Java projects!Lingulate
Thank you so much. This code working in android 8.1. But not working in android 11. Is there any updates? Please.....Conan
S
19

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;
}
Sharie answered 4/12, 2017 at 13:20 Comment(3)
Part of the above code is now deprecated, which you can overcome by changing Notification.Builder(getApplicationContext()).setChannelId(CHANNEL_ONE_ID)... to Notification.Builder(getApplicationContext(), CHANNEL_ONE_ID)...Armageddon
@Armageddon you're absolutely right... I added new sample code. Thanks.Sharie
why PRIORITY_MAX what is better to use?Breeks
L
9

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());
Livelong answered 12/1, 2018 at 12:29 Comment(2)
Should the constant NOTIFICATION_CHANNEL_ID_TASK (2nd line) be NOTIFICATION_CHANNEL_ID_INFO?Catamaran
@Timores, no. You can replace it with your own constant.Livelong
Z
8

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);
    }
}
Ziegfeld answered 2/1, 2019 at 20:14 Comment(3)
Can you please clarify what changes you have made in this code, I didn't get this.Frisket
Versions 8.0 and Android Pie works perfectly. But why do we need Notification channel for version 8.1 only?Cal
explain why we have to create notificationMannish
A
3

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

Armbruster answered 25/6, 2018 at 18:46 Comment(0)
B
2

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

Breeks answered 28/1, 2021 at 18:53 Comment(0)
O
0

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.

Ossieossietzky answered 5/5, 2021 at 16:29 Comment(0)
M
0

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.

Mousebird answered 14/5, 2022 at 11:54 Comment(0)
P
0

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"
        )
Petes answered 11/10, 2022 at 10:51 Comment(0)
S
0

I didn't see this mentioned in the answers. I had forgotten to add my application class (which extends Application) to the manifest. The notification wasn't working because the channels are created there.

<application
        android:name=".MyApplication" <-- here 
        android:allowBackup="true"
        ...
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name=".ForegroundService"/>
    </application>
Shear answered 10/8, 2023 at 13:48 Comment(0)
G
-1

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

Gingili answered 4/9, 2019 at 19:18 Comment(1)
Please take some time to explain the rationale for your solution.Congratulate

© 2022 - 2024 — McMap. All rights reserved.