Context.startForegroundService() затем не вызывал Service.startForeground()

Я использую класс Service на ОС Android O.

Я планирую использовать Service в фоновом режиме.

Рекомендация Android гласит, что startService() должен использовать startForegroundService().

Если вы используете startForegroundService(), Service выбрасывает

Context.startForegroundService() тогда не вызывал Service.startForeground()

ошибка.

Что не так с этим?

Ответы

Ответ 1

Из Google docs на Android 8.0 меняется поведение:

Система позволяет приложениям вызывать Context.startForegroundService(), даже когда приложение находится в фоновом режиме. Однако приложение должно вызвать этот метод startForeground() службы в течение пяти секунд после создания службы.

Решение: startForeground() в onCreate() для Service которую вы используете Context.startForegroundService()

Смотрите также: Пределы выполнения фона для Android 8.0 (Oreo)

Ответ 2

Я позвонил ContextCompat.startForegroundService(this, intent), чтобы запустить службу, затем

В сервисе onCreate

 @Override
 public void onCreate() {
        super.onCreate();

        if (Build.VERSION.SDK_INT >= 26) {
            String CHANNEL_ID = "my_channel_01";
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "Channel human readable title",
                    NotificationManager.IMPORTANCE_DEFAULT);

            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);

            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setContentTitle("")
                    .setContentText("").build();

            startForeground(1, notification);
        }
}

Ответ 3

Причина, по которой возникает эта проблема, заключается в том, что платформа Android не может гарантировать запуск службы в течение 5 секунд, но, с другой стороны, среда имеет строгие ограничения на уведомление переднего плана, которое должно быть запущено в течение 5 секунд, без проверки того, пыталась ли среда запустить службу,

Это определенно проблема с фреймворком, но не все разработчики, сталкивающиеся с этой проблемой, делают все возможное:

  1. startForeground уведомление должно быть как в onCreate и в onStartCommand, потому что если ваша служба уже создана и каким-то образом ваша деятельность пытается запустить ее снова, onCreate не будет.

  2. Идентификатор уведомления не должен быть 0, иначе произойдет тот же сбой, даже если это не та же самая причина.

  3. stopSelf не должен вызываться до startForeground.

Со всем вышеперечисленным 3 эта проблема может быть немного уменьшена, но все же не является исправлением, реальное исправление или, скажем так, обходной путь - понизить целевую версию SDK до 25.

И обратите внимание, что, скорее всего, Android P все еще будет нести эту проблему, потому что Google отказывается даже понимать, что происходит, и не верит, что это их вина, прочитайте №36 и №56 для получения дополнительной информации.

Ответ 4

Ваше приложение будет вылетать, если вы звоните Context.startForegroundService(...), а затем вызвать Context.stopService(...) перед Service.startForeground(...) называется.

У меня есть четкое воспроизведение здесь ForegroundServiceAPI26

Я открыл ошибку по этому адресу: Google tracker tracker

Несколько ошибок по этому вопросу были открыты и закрыты не будут исправлены.

Надеюсь, мой с четкими репро шагами сделает разрез.

Информация предоставлена командой Google

Отслеживание проблем Google Комментарий 36

Это не ошибка фреймворка; это намеренно. Если приложение запускает экземпляр службы с помощью startForegroundService(), оно должно startForegroundService() этот экземпляр службы в состояние переднего плана и показать уведомление. Если экземпляр службы останавливается до startForeground(), это обещание не выполняется: это ошибка в приложении.

Что касается публикации № 31, публикация Службы, которую другие приложения могут запускать напрямую, принципиально небезопасна. Вы можете немного смягчить это, рассматривая все начальные действия этого сервиса как требующие startForeground(), хотя, очевидно, это может не соответствовать startForeground().

Google tracker track Комментарий 56

Есть несколько различных сценариев, которые приводят к одному и тому же результату.

Непосредственная семантическая проблема, что просто ошибка startForegroundService() что-то с помощью startForegroundService() но игнорирует фактический переход на передний план с помощью startForeground(), это просто семантическая проблема. Это преднамеренно рассматривается как ошибка приложения. Остановка службы перед ее переносом на передний план - ошибка приложения. В этом суть ОП, и именно поэтому этот вопрос был помечен как "работающий по назначению".

Однако есть и вопросы о ложном обнаружении этой проблемы. Это рассматривается как подлинная проблема, хотя она отслеживается отдельно от этой конкретной проблемы отслеживания ошибок. Мы не глухи к жалобе.

Ответ 5

Я исследовал это на пару дней и получил решение. Теперь в Android O вы можете установить ограничение фона как ниже

Служба, вызывающая класс обслуживания

Intent serviceIntent = new Intent(SettingActivity.this,DetectedService.class);
                    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O){

                        SettingActivity.this.startForegroundService(serviceIntent);
                    }else{
                        startService(serviceIntent);
                    }

и класс обслуживания должен быть похож

public class DetectedService extends Service { 
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        int NOTIFICATION_ID = (int) (System.currentTimeMillis()%10000);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForeground(NOTIFICATION_ID, new Notification.Builder(this).build());
        }


        // Do whatever you want to do here
    }
}

Ответ 6

У меня есть виджет, который делает относительно частые обновления, когда устройство не работает, и я видел тысячи аварий за несколько дней.

Триггер проблемы

Я даже заметил проблему даже на моем Pixel 3 XL, когда я не думал, что устройство вообще сильно загружено. И все пути кода были покрыты startForeground(). Но потом я понял, что во многих случаях мой сервис выполняет работу очень быстро. Я полагаю, что триггер для моего приложения заключался в том, что служба заканчивала работу до того, как система успела показать уведомление.

Обходной путь/решение

Я смог избавиться от всех сбоев. Что я сделал, чтобы удалить вызов stopSelf(). (Я думал о том, чтобы отложить остановку, пока я не был уверен, что уведомление было показано, но я не хочу, чтобы пользователь видел уведомление, если в этом нет необходимости.) Когда служба простаивала в течение минуты или система уничтожает его как обычно без каких-либо исключений.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    stopForeground(true);
} else {
    stopSelf();
}

Ответ 7

Просто голова, так как я потратил слишком много часов на это. Я продолжал получать это исключение, даже если я вызывал startForeground(..) как первое в onCreate(..). В конце концов я обнаружил, что проблема была вызвана использованием NOTIFICATION_ID = 0. Использование любого другого значения, похоже, исправить это.

Ответ 8

Я знаю, слишком много ответов уже опубликовано, однако правда в том, что startForegroundService нельзя исправить на уровне приложения, и вы должны прекратить его использовать. Рекомендация Google использовать API Service # startForeground() в течение 5 секунд после вызова Context # startForegroundService() - это не то, что приложение всегда может сделать.

Android запускает много процессов одновременно, и нет никакой гарантии, что Looper вызовет вашу целевую службу, которая должна вызывать startForeground() в течение 5 секунд. Если ваша целевая служба не получила вызов в течение 5 секунд, вам не повезло, и ваши пользователи столкнутся с ситуацией ANR. В трассировке стека вы увидите что-то вроде этого:

Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{1946947 u0 ...MessageService}

main" prio=5 tid=1 Native
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x763e01d8 self=0x7d77814c00
  | sysTid=11171 nice=-10 cgrp=default sched=0/0 handle=0x7dfe411560
  | state=S schedstat=( 1337466614 103021380 2047 ) utm=106 stm=27 core=0 HZ=100
  | stack=0x7fd522f000-0x7fd5231000 stackSize=8MB
  | held mutexes=
  #00  pc 00000000000712e0  /system/lib64/libc.so (__epoll_pwait+8)
  #01  pc 00000000000141c0  /system/lib64/libutils.so (android::Looper::pollInner(int)+144)
  #02  pc 000000000001408c  /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+60)
  #03  pc 000000000012c0d4  /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44)
  at android.os.MessageQueue.nativePollOnce (MessageQueue.java)
  at android.os.MessageQueue.next (MessageQueue.java:326)
  at android.os.Looper.loop (Looper.java:181)
  at android.app.ActivityThread.main (ActivityThread.java:6981)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1445)

Как я понимаю, Лупер проанализировал очередь здесь, нашел "обидчика" и просто убил ее. Теперь система счастлива и здорова, а разработчики и пользователи - нет, но, поскольку Google ограничивает свои обязанности системой, почему они должны заботиться о последних двух? Видимо, нет. Могут ли они сделать это лучше? Конечно, например, они могли бы обслуживать диалог "Приложение занято", предлагая пользователю принять решение о том, ждать или убить приложение, но зачем беспокоиться, это не их ответственность. Главное, что система сейчас здорова.

По моим наблюдениям, это происходит относительно редко, в моем случае примерно 1 сбой в месяц для пользователей 1К. Воспроизвести его невозможно, и даже если он воспроизводится, вы ничего не сможете сделать, чтобы исправить это навсегда.

В этом потоке было хорошее предложение использовать "bind" вместо "start", а затем, когда служба готова, обрабатывать onServiceConnected, но, опять же, это означает, что вообще не следует использовать вызовы startForegroundService.

Я думаю, что правильным и честным действием со стороны Google было бы сообщить всем, что startForegourndServcie имеет недостаток и не должен использоваться.

Вопрос все еще остается: что использовать вместо этого? К счастью для нас, есть JobScheduler и JobService, которые являются лучшей альтернативой для приоритетных служб. Это лучший вариант, потому что:

Пока выполняется задание, система удерживает блокировку от имени вашего приложения. По этой причине вам не нужно предпринимать никаких действий, чтобы гарантировать, что устройство остается активным в течение всей работы.

Это означает, что вам больше не нужно заботиться об обработке wakelocks, и поэтому он не отличается от приоритетных сервисов. Начиная с реализации, PoV JobScheduler - это не ваша служба, а системная, предположительно, она будет правильно обрабатывать очередь, и Google никогда не прекратит свой собственный дочерний элемент :)

Samsung перешла от startForegroundService к JobScheduler и JobService в своем дополнительном протоколе Samsung (SAP). Это очень полезно, когда такие устройства, как умные часы, должны общаться с такими хостами, как телефоны, где работа должна взаимодействовать с пользователем через главный поток приложения. Поскольку задания публикуются планировщиком в главном потоке, это становится возможным. Однако вы должны помнить, что задание выполняется в главном потоке, и перенести все тяжелые вещи в другие потоки и асинхронные задачи.

Этот сервис выполняет каждое входящее задание в обработчике, запущенном в главном потоке вашего приложения. Это означает, что вы должны перенести логику выполнения в другой поток/обработчик /AsyncTask по вашему выбору

Единственная ловушка перехода на JobScheduler/JobService заключается в том, что вам потребуется рефакторинг старого кода, и это не весело. Последние два дня я потратил на то, чтобы использовать новую реализацию Samsung SAP. Я посмотрю свои отчеты о сбоях и сообщу, увидит ли снова сбой. Теоретически это не должно происходить, но всегда есть детали, о которых мы могли бы не знать.

ОБНОВЛЕНИЕ Больше не сообщается о сбоях в Play Store. Это означает, что JobScheduler/JobService не имеют такой проблемы, и переключение на эту модель является правильным подходом, чтобы избавиться от проблемы startForegroundService раз и навсегда. Надеюсь, Google/Android его прочитает и в конечном итоге прокомментирует/посоветует/предоставит официальное руководство для всех.

Ответ 9

У меня есть работа по этой проблеме. Я проверил это исправление в своем собственном приложении (300K+ DAU), что может уменьшить как минимум 95% такого рода сбоев, но все же не может избежать 100% этой проблемы.

Эта проблема возникает даже при условии, что вы вызываете startForeground() сразу после запуска службы с документацией Google. Возможно, потому, что процесс создания и инициализации службы уже во многих сценариях стоит более 5 секунд, тогда независимо от того, когда и где вы вызываете метод startForeground(), этот сбой неизбежен.

Мое решение состоит в том, чтобы запустить startForeground() в течение 5 секунд после метода startForegroundService(), независимо от того, как долго ваша служба должна быть создана и инициализирована. Вот подробное решение.

  1. Не используйте startForegroundService в первую очередь, используйте bindService() с флагом auto_create. Он будет ждать инициализации службы. Вот код, мой пример службы - MusicService:

    final Context applicationContext = context.getApplicationContext();
    Intent intent = new Intent(context, MusicService.class);
    applicationContext.bindService(intent, new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            if (binder instanceof MusicBinder) {
                MusicBinder musicBinder = (MusicBinder) binder;
                MusicService service = musicBinder.getService();
                if (service != null) {
                    // start a command such as music play or pause.
                    service.startCommand(command);
                    // force the service to run in foreground here.
                    // the service is already initialized when bind and auto_create.
                    service.forceForeground();
                }
            }
            applicationContext.unbindService(this);
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    }, Context.BIND_AUTO_CREATE);
    
  2. Затем здесь реализована реализация MusicBinder:

    /**
     * Use weak reference to avoid binder service leak.
     */
     public class MusicBinder extends Binder {
    
         private WeakReference<MusicService> weakService;
    
         /**
          * Inject service instance to weak reference.
          */
         public void onBind(MusicService service) {
             this.weakService = new WeakReference<>(service);
         }
    
         public MusicService getService() {
             return weakService == null ? null : weakService.get();
         }
     }
    
  3. Самая важная часть, реализация MusicService, метод forceForeground() гарантирует, что метод startForeground() вызывается сразу после startForegroundService():

    public class MusicService extends MediaBrowserServiceCompat {
    ...
        private final MusicBinder musicBind = new MusicBinder();
    ...
        @Override
        public IBinder onBind(Intent intent) {
            musicBind.onBind(this);
            return musicBind;
        }
    ...
        public void forceForeground() {
            // API lower than 26 do not need this work around.
            if (Build.VERSION.SDK_INT >= 26) {
                Intent intent = new Intent(this, MusicService.class);
                // service has already been initialized.
                // startForeground method should be called within 5 seconds.
                ContextCompat.startForegroundService(this, intent);
                Notification notification = mNotificationHandler.createNotification(this);
                // call startForeground just after startForegroundService.
                startForeground(Constants.NOTIFICATION_ID, notification);
            }
        }
    }
    
  4. Если вы хотите запустить фрагмент кода шага 1 в ожидающем намерения, например, если вы хотите запустить службу переднего плана в виде виджета (щелчок на кнопке виджета), не открывая приложение, вы можете обернуть фрагмент кода в широковещательном приемнике, и запускать широковещательное событие вместо команды запуска службы.

Это все. Надеюсь, поможет. Удачи.

Ответ 10

Эта ошибка также возникает в Android 8+, когда вызывается Service.startForeground(int id, Notification Notification), а для id установлено значение 0.

id int: идентификатор для этого уведомления согласно NotificationManager.notify(int, Notification); не должно быть 0.

Ответ 11

https://developer.android.com/reference/android/content/Context.html#startForegroundService(android.content.Intent)

Аналогично startService (Intent), но с неявным обещанием, что Служба вызовет startForeground (int, android.app.Notification) один раз он начинает работать. Службе предоставляется количество времени, сопоставимое к интервалу ANR, чтобы сделать это, иначе система будет автоматически остановите службу и объявите приложение ANR.

В отличие от обычного startService (Intent), этот метод можно использовать в в любое время, независимо от того, находится ли приложение, обслуживающее службу, в переднего плана.

убедитесь, что вы вызываете Service.startForeground(int, android.app.Notification) в onCreate(), чтобы убедиться, что он будет вызываться. Если у вас есть какие-либо условия, которые могут помешать вам это сделать, вам лучше использовать обычный Context.startService(Intent) и вызовите Service.startForeground(int, android.app.Notification) самостоятельно.

Кажется, что Context.startForegroundService() добавляет сторожевой таймер, чтобы убедиться, что вы вызвали Service.startForeground(int, android.app.Notification), прежде чем он был уничтожен...

Ответ 12

Я столкнулся с такой же проблемой, и, проведя время, нашел солотоны, которые вы можете попробовать под кодом. Если ваша Service затем поместила этот код в onCreate else, вы используете Intent Service затем поместите этот код в onHandleIntent.

if (Build.VERSION.SDK_INT >= 26) {
        String CHANNEL_ID = "my_app";
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                "MyApp", NotificationManager.IMPORTANCE_DEFAULT);
        ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("")
                .setContentText("").build();
        startForeground(1, notification);
    }

Ответ 13

Так много ответов, но ни один из них не сработал в моем случае.

Я начал службу как это.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    startForegroundService(intent);
} else {
    startService(intent);
}

И в моем сервисе в onStartCommand

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
                .setContentTitle(getString(R.string.app_name))
                .setContentText("SmartTracker Running")
                .setAutoCancel(true);
        Notification notification = builder.build();
        startForeground(NOTIFICATION_ID, notification);
    } else {
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                .setContentTitle(getString(R.string.app_name))
                .setContentText("SmartTracker is Running...")
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .setAutoCancel(true);
        Notification notification = builder.build();
        startForeground(NOTIFICATION_ID, notification);
    }

И не забывайте устанавливать NOTIFICATION_ID ненулевым

private static final String ANDROID_CHANNEL_ID = "com.xxxx.Location.Channel";
private static final int NOTIFICATION_ID = 555;

ТАК все было отлично, но все равно зависало на 8.1, причина была как ниже.

     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            stopForeground(true);
        } else {
            stopForeground(true);
        }

Я вызвал остановку переднего плана с удалением уведомлений, но как только служба удаленных уведомлений стала фоновой, фоновая служба не может работать в Android O из фона. началось после получения толчка.

Так что волшебное слово

   stopSelf();

До сих пор, по любой причине, что ваш сервис не работает, следуйте всем вышеперечисленным шагам и наслаждайтесь

Ответ 14

Проблема с Android O API 26

Если вы прекратите услугу сразу (так что ваша служба действительно не работает (формулировка/понимание), и вы находитесь под интервалом ANR, вам все равно нужно вызвать startForeground перед остановкой

https://plus.google.com/116630648530850689477/posts/L2rn4T6SAJ5

Пробовал этот подход Но он все еще создает ошибку: -

if (Util.SDK_INT > 26) {
    mContext.startForegroundService(playIntent);
} else {
    mContext.startService(playIntent);
}

Я использую это до тех пор, пока не будет устранена ошибка

mContext.startService(playIntent);

Ответ 15

Я изучал этот вопрос, и это то, что я обнаружил до сих пор. Этот сбой может произойти, если у нас есть код, похожий на этот:

MyForegroundService.java

public class MyForegroundService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(...);
    }
}

MainActivity.java

Intent serviceIntent = new Intent(this, MyForegroundService.class);
startForegroundService(serviceIntent);
...
stopService(serviceIntent);

Исключение выбрано в следующем блоке кода:

ActiveServices.java

private final void bringDownServiceLocked(ServiceRecord r) {
    ...
    if (r.fgRequired) {
        Slog.w(TAG_SERVICE, "Bringing down service while still waiting for start foreground: "
                  + r);
        r.fgRequired = false;
        r.fgWaiting = false;
        mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
                    AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
        mAm.mHandler.removeMessages(
                    ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
        if (r.app != null) {
            Message msg = mAm.mHandler.obtainMessage(
                ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG);
            msg.obj = r.app;
            msg.getData().putCharSequence(
                ActivityManagerService.SERVICE_RECORD_KEY, r.toString());
            mAm.mHandler.sendMessage(msg);
         }
    }
    ...
}

Этот метод выполняется до onCreate() MyForegroundService потому что Android планирует создание службы в обработчике основного потока, но bringDownServiceLocked вызывается на BinderThread, который является условием гонки. Это означает, что у MyForegroundService не было возможности вызвать startForeground что приведет к сбою.

Чтобы исправить это, мы должны убедиться, что bringDownServiceLocked не onCreate() перед onCreate() MyForegroundService.

public class MyForegroundService extends Service {

    private static final String ACTION_STOP = "com.example.MyForegroundService.ACTION_STOP";

    private final BroadcastReceiver stopReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            context.removeStickyBroadcast(intent);
            stopForeground(true);
            stopSelf();
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(...);
        registerReceiver(
            stopReceiver, new IntentFilter(ACTION_STOP));
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(stopReceiver);
    }

    public static void stop(Context context) {
        context.sendStickyBroadcast(new Intent(ACTION_STOP));
    }
}

Используя липкие трансляции, мы stopReceiver за тем, чтобы трансляция не терялась и stopReceiver получает намерение остановки, как только он был зарегистрирован в onCreate() MyForegroundService. К этому времени мы уже назвали startForeground(...). Мы также должны удалить эту липкую трансляцию, чтобы в следующий раз не уведомлять stopReceiver.

Обратите внимание, что метод sendStickyBroadcast устарел, и я использую его только как временный обходной путь, чтобы исправить эту проблему.

Ответ 16

Пожалуйста, не вызывайте никаких StartForgroundServices внутри метода onCreate(), вы должны вызывать службы StartForground в onStartCommand() после создания рабочего потока, в противном случае вы всегда получите ANR, поэтому, пожалуйста, не пишите сложное имя входа в основной поток onStartCommand();

public class Services extends Service {

    private static final String ANDROID_CHANNEL_ID = "com.xxxx.Location.Channel";
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText("SmartTracker Running")
                    .setAutoCancel(true);
            Notification notification = builder.build();
            startForeground(0, notification);
            Log.e("home_button","home button");
        } else {
            NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText("SmartTracker is Running...")
                    .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                    .setAutoCancel(true);
            Notification notification = builder.build();
            startForeground(0, notification);
            Log.e("home_button_value","home_button_value");

        }
        return super.onStartCommand(intent, flags, startId);

    }
}

РЕДАКТИРОВАТЬ: Осторожно! Функция startForeground не может принимать 0 в качестве первого аргумента, она вызовет исключение! этот пример содержит неверный вызов функции, измените 0 на свой собственный const, который не может быть 0 или больше, чем Max (Int32)

Ответ 17

Я добавляю код в @humazed ответ. Так что нет в первоначальном уведомлении. Это может быть обходной путь, но он работает для меня.

@Override
 public void onCreate() {
        super.onCreate();

        if (Build.VERSION.SDK_INT >= 26) {
            String CHANNEL_ID = "my_channel_01";
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "Channel human readable title",
                    NotificationManager.IMPORTANCE_DEFAULT);

            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);

            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setContentTitle("")
                    .setContentText("")
                    .setColor(ContextCompat.getColor(this, R.color.transparentColor))
                    .setSmallIcon(ContextCompat.getColor(this, R.color.transparentColor)).build();

            startForeground(1, notification);
        }
}

Я добавляю прозрачный цвет маленьким значком и цветом в уведомлении. Это будет работать.

Ответ 18

Поскольку все, кто посещает здесь, страдают от одного и того же, я хочу поделиться своим решением, которое никто раньше не пробовал (в любом случае, в этом вопросе). Я могу заверить вас, что он работает, даже на остановленной точке останова, которая подтверждает этот метод.

Проблема в том, чтобы позвонить Service.startForeground(id, notification) из самой службы, верно? К сожалению, Android Framework не гарантирует вызов Service.startForeground(id, notification) в течение Service.onCreate() за 5 секунд, но в любом случае выдает исключение, поэтому я придумала этот способ.

  1. Привязать службу к контексту с помощью привязки из службы перед вызовом Context.startForegroundService()
  2. Если привязка прошла успешно, позвоните в Context.startForegroundService() из сервисного соединения и немедленно позвоните в Service.startForeground() внутри сервисного соединения.
  3. ВАЖНОЕ ПРИМЕЧАНИЕ: Вызовите метод Context.bindService() внутри try-catch, потому что в некоторых случаях вызов может вызвать исключение, и в этом случае вам нужно полагаться на непосредственный вызов Context.startForegroundService() и надеяться на него не подведет. Примером может быть контекст получателя широковещания, однако получение контекста приложения в этом случае не вызывает исключения, а использование контекста напрямую.

Это даже работает, когда я жду на точке останова после привязки службы и до запуска вызова startForeground. Ожидание в течение 3-4 секунд не вызывает исключение, в то время как через 5 секунд оно выдает исключение. (Если устройство не может выполнить две строки кода за 5 секунд, тогда пора выбросить это в корзину.)

Итак, начнем с создания сервисного соединения.

// Create the service connection.
ServiceConnection connection = new ServiceConnection()
{
    @Override
    public void onServiceConnected(ComponentName name, IBinder service)
    {
        // The binder of the service that returns the instance that is created.
        MyService.LocalBinder binder = (MyService.LocalBinder) service;

        // The getter method to acquire the service.
        MyService myService = binder.getService();

        // getServiceIntent(context) returns the relative service intent 
        context.startForegroundService(getServiceIntent(context));

        // This is the key: Without waiting Android Framework to call this method
        // inside Service.onCreate(), immediately call here to post the notification.
        myService.startForeground(myNotificationId, MyService.getNotification());

        // Release the connection to prevent leaks.
        context.unbindService(this);
    }

    @Override
    public void onBindingDied(ComponentName name)
    {
        Log.w(TAG, "Binding has dead.");
    }

    @Override
    public void onNullBinding(ComponentName name)
    {
        Log.w(TAG, "Bind was null.");
    }

    @Override
    public void onServiceDisconnected(ComponentName name)
    {
        Log.w(TAG, "Service is disconnected..");
    }
};

Внутри вашего сервиса создайте подшивку, которая возвращает экземпляр вашего сервиса.

public class MyService extends Service
{
    public class LocalBinder extends Binder
    {
        public MyService getService()
        {
            return MyService.this;
        }
    }

    // Create the instance on the service.
    private final LocalBinder binder = new LocalBinder();

    // Return this instance from onBind method.
    // You may also return new LocalBinder() which is
    // basically the same thing.
    @Nullable
    @Override
    public IBinder onBind(Intent intent)
    {
        return binder;
    }
}

Затем попытайтесь связать службу с этим контекстом. В случае успеха он вызовет метод ServiceConnection.onServiceConnected() из используемого вами сервисного соединения. Затем обработайте логику в коде, который показан выше. Пример кода будет выглядеть следующим образом:

// Try to bind the service
try
{
     context.bindService(getServiceIntent(context), connection,
                    Context.BIND_AUTO_CREATE);
}
catch (RuntimeException ignored)
{
     // This is probably a broadcast receiver context even though we are calling getApplicationContext().
     // Just call startForegroundService instead since we cannot bind a service to a
     // broadcast receiver context. The service also have to call startForeground in
     // this case.
     context.startForegroundService(getServiceIntent(context));
}

Похоже, он работает над приложениями, которые я разрабатываю, поэтому он должен работать, когда вы пытаетесь.

Ответ 19

Даже после вызова startForeground в Service, он падает на некоторых устройствах, если мы вызываем stopService непосредственно перед onCreate. Итак, я исправил эту проблему, запустив службу с дополнительным флагом:

Intent intent = new Intent(context,
                YourService.class);
intent.putExtra("request_stop", true);
context.startService(intent);

и добавила проверку onStartCommand, чтобы проверить, действительно ли она была остановлена:

@Override
public int onStartCommand(Intent intent,
    int flags, int startId) {

    //call startForeground first
    boolean stopService = false;
    if (intent != null) {
        stopService = intent.getBooleanExtra("request_stop", false);
    }
    if (stopService) {
        stopSelf();
        return START_STICKY;
    }
    //Continue with the background task
    return START_STICKY;
}

PS Если служба фактически не запускалась, она сначала запускает сервис, который является накладными расходами.

Ответ 20

Я просто проверяю нулевое значение PendingIntent или не до вызова функции context.startForegroundService(service_intent).

это работает для меня

PendingIntent pendingIntent=PendingIntent.getBroadcast(context,0,intent,PendingIntent.FLAG_NO_CREATE);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && pendingIntent==null){
            context.startForegroundService(service_intent);
        }
        else
        {
            context.startService(service_intent);
        }
}

Ответ 21

просто вызовите метод startForeground сразу после создания Service или IntentService. как это:

import android.app.Notification;
public class AuthenticationService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(1,new Notification());
    }
}

Ответ 22

Я знаю, это поздний ответ, но, я думаю, что это может помочь в будущем, я просто использовал JobIntentService вместо IntentService который включает его JobScheduler и обрабатывает все для меня. посмотрите на этот пример

Ответ 23

Я исправил проблему с запуском сервиса с помощью startService(intent) вместо Context.startForeground() и вызова startForegound() сразу после super.OnCreate(). Кроме того, если вы запускаете службу при загрузке, вы можете запустить Activity, которая запускает службу при загрузке. Хотя это не постоянное решение, оно работает.

Ответ 24

Это проблема самой платформы Android. Многие разработчики подали ошибку для того же, но Google не склонен исправлять это поведение. В моем случае ошибка, помеченная мной, была помечена как "не будет устранена", и люди из Google несколько раз пытались ответить на вопрос, существует ли эта проблема.

Единственный Context.startForegroundService() обойти это сейчас - вообще не использовать Context.startForegroundService(). Вы можете использовать Context.startService() когда у вас есть приоритет переднего плана. Но помните, что это нельзя использовать, если ваше приложение работает в фоновом режиме.

Использование Context.startService() а затем продвижение службы на передний план работает, как и ожидалось.

Чтобы узнать, как я развернул этот обходной путь, вот блог, который я написал со всеми деталями. Ссылка на блог здесь с обещанием, что ссылка не будет мертвой до бесконечности :)

https://www.theandroiddeveloper.com/blog/context-startforegroundservice-did-not-then-call-service-startforeground

Ответ 25

Обновление данных в onStartCommand(...)

onBind (...)

onBind(...) является лучшим событием жизненного цикла для инициирования startForeground против onCreate(...) потому что onBind(...) передает Intent который может содержать важные данные в Bundle необходимые для инициализации Service. Однако это необязательно, так как onStartCommand(...) вызывается, когда Service создается в первый раз или вызывается в последующие разы после.

onStartCommand (...)

startForeground в onStartCommand(...) важна для обновления Service после ее создания.

Когда ContextCompat.startForegroundService(...) вызывается после того, как Service была создана, onBind(...) и onCreate(...) не вызываются. Поэтому обновленные данные могут быть переданы в onStartCommand(...) через Intent Bundle для обновления данных в Service.

Образец

Я использую этот шаблон для реализации PlayerNotificationManager в приложении новостей о криптовалюте Coinverse.

Деятельность /Fragment.kt

context?.bindService(
        Intent(context, AudioService::class.java),
        serviceConnection, Context.BIND_AUTO_CREATE)
ContextCompat.startForegroundService(
        context!!,
        Intent(context, AudioService::class.java).apply {
            action = CONTENT_SELECTED_ACTION
            putExtra(CONTENT_SELECTED_KEY, contentToPlay.content.apply {
                audioUrl = uri.toString()
            })
        })

AudioService.kt

private var uri: Uri = Uri.parse("")

override fun onBind(intent: Intent?) =
        AudioServiceBinder().apply {
            player = ExoPlayerFactory.newSimpleInstance(
                    applicationContext,
                    AudioOnlyRenderersFactory(applicationContext),
                    DefaultTrackSelector())
        }

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    intent?.let {
        when (intent.action) {
            CONTENT_SELECTED_ACTION -> it.getParcelableExtra<Content>(CONTENT_SELECTED_KEY).also { content ->
                val intentUri = Uri.parse(content.audioUrl)
                // Checks whether to update Uri passed in Intent Bundle.
                if (!intentUri.equals(uri)) {
                    uri = intentUri
                    player?.prepare(ProgressiveMediaSource.Factory(
                            DefaultDataSourceFactory(
                                    this,
                                    Util.getUserAgent(this, getString(app_name))))
                            .createMediaSource(uri))
                    player?.playWhenReady = true
                    // Calling 'startForeground' in 'buildNotification(...)'.          
                    buildNotification(intent.getParcelableExtra(CONTENT_SELECTED_KEY))
                }
            }
        }
    }
    return super.onStartCommand(intent, flags, startId)
}

// Calling 'startForeground' in 'onNotificationStarted(...)'.
private fun buildNotification(content: Content): Unit? {
    playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(
            this,
            content.title,
            app_name,
            if (!content.audioUrl.isNullOrEmpty()) 1 else -1,
            object : PlayerNotificationManager.MediaDescriptionAdapter {
                override fun createCurrentContentIntent(player: Player?) = ...
                override fun getCurrentContentText(player: Player?) = ...
                override fun getCurrentContentTitle(player: Player?) = ...
                override fun getCurrentLargeIcon(player: Player?,
                                                 callback: PlayerNotificationManager.BitmapCallback?) = ...
            },
            object : PlayerNotificationManager.NotificationListener {
                override fun onNotificationStarted(notificationId: Int, notification: Notification) {
                    startForeground(notificationId, notification)
                }
                override fun onNotificationCancelled(notificationId: Int) {
                    stopForeground(true)
                    stopSelf()
                }
            })
    return playerNotificationManager.setPlayer(player)
}

Ответ 26

Хорошо, кое-что, что я заметил по этому поводу, может помочь и другим. Это строго от тестирования, чтобы увидеть, могу ли я выяснить, как исправить вхождения, которые я вижу. Для простоты, скажем, у меня есть метод, который вызывает это от докладчика.

    context.startForegroundService(new Intent(context, TaskQueueExecutorService.class));

    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }       

Это вылетит с той же ошибкой. Служба НЕ будет запущена до тех пор, пока метод не будет завершен, поэтому в службе нет onCreate().

Таким образом, даже если вы обновите пользовательский интерфейс из основного потока, если у вас есть что-то, что может задержать этот метод после него, он не запустится вовремя и выдаст вам страшную ошибку Foreground. В моем случае мы загружали некоторые вещи в очередь, каждая из которых называлась startForegroundService, но некоторая логика была связана с каждой в фоновом режиме. Так что если логика заняла слишком много времени, чтобы завершить этот метод, так как они были вызваны спина к спине, время сбоя. Старая служба startService просто проигнорировала это и пошла по этому пути, и, поскольку мы вызывали его каждый раз, следующий раунд заканчивался.

Это заставило меня задуматься: если бы я вызывал службу из потока в фоновом режиме, разве он не мог быть полностью связан с запуском и запускался немедленно, поэтому я начал экспериментировать. Несмотря на то, что это НЕ запускает это немедленно, это не терпит крах.

new Handler(Looper.getMainLooper()).post(new Runnable() {
        public void run() {
               context.startForegroundService(new Intent(context, 
           TaskQueueExecutorService.class));
               try {
                   Thread.sleep(10000);
               } catch (InterruptedException e) {
                  e.printStackTrace();
              }       
        }
});

Я не буду притворяться, что знаю, почему он не падает, хотя я подозреваю, что это заставляет его ждать, пока основной поток не сможет обработать его своевременно. Я знаю, что не идеально привязывать его к основному потоку, но поскольку мое использование вызывает его в фоновом режиме, я не очень обеспокоен, ждет ли он, пока он не завершится, а не потерпит крах.

Ответ 27

для меня я добавил передний план службы в манифесте в

 <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

если это работает, комментарий ниже, дайте мне знать

Ответ 28

обслуживание

class TestService : Service() {

    override fun onCreate() {
        super.onCreate()
        Log.d(TAG, "onCreate")

        val nBuilder = NotificationCompat.Builder(this, "all")
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle("TestService")
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        startForeground(1337, nBuilder.build())
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val rtn = super.onStartCommand(intent, flags, startId)

        if (intent?.action == STOP_ACTION) {
            Log.d(TAG, "onStartCommand -> STOP")
            stopForeground(true)
            stopSelf()
        } else {
            Log.d(TAG, "onStartCommand -> START")
        }

        return rtn
    }

    override fun onDestroy() {
        Log.d(TAG, "onDestroy")
        super.onDestroy()
    }

    override fun onBind(intent: Intent?): IBinder? = null

    companion object {

        private val TAG = "TestService"
        private val STOP_ACTION = "ly.zen.test.TestService.ACTION_STOP"

        fun start(context: Context) {
            ContextCompat.startForegroundService(context, Intent(context, TestService::class.java))
        }

        fun stop(context: Context) {
            val intent = Intent(context, TestService::class.java)
            intent.action = STOP_ACTION
            ContextCompat.startForegroundService(context, intent)
        }

    }

}

тестер

val nChannel = NotificationChannel("all", "All", NotificationManager.IMPORTANCE_NONE)
val nManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
nManager.createNotificationChannel(nChannel)

start_test_service.setOnClickListener {
    TestService.start([email protected])
    TestService.stop([email protected])
}

Результат

D/TestService: onCreate
D/TestService: onStartCommand -> START
D/TestService: onStartCommand -> STOP
D/TestService: onDestroy

Ответ 29

Это все исчезнет, если вы переписываете свой сервис для запуска с bindService.

Пример того, как это сделать, можно увидеть по адресу: https://github.com/paulpv/ForegroundServiceAPI26/tree/bound

Разницу в моей "репрезентативной" ветки можно увидеть по адресу: https://github.com/paulpv/ForegroundServiceAPI26/compare/repro...bound?expand=1

Ответ 30

Убедитесь, что все пути кода вызывает метод startForeground, например, код может генерировать исключение в методе onCreate вашего сервиса, который предотвращает вызов функции startForeground

   @Override
    public void onCreate() {
try{
}
catch(Exception e)
{
}
finally{
    startForeground(1, notificationbuilder.build());

}
    }