Come avviareForeground () senza mostrare la notifica?


90

Voglio creare un servizio e farlo funzionare in primo piano.

La maggior parte dei codici di esempio contiene notifiche. Ma non voglio mostrare alcuna notifica. È possibile?

Puoi farmi qualche esempio? Esistono alternative?

Il mio servizio app sta facendo mediaplayer. Come fare in modo che il sistema non interrompa il mio servizio tranne l'app che lo uccide da solo (come mettere in pausa o interrompere la musica con il pulsante).


11
Non è possibile avere un servizio "Primo piano" senza una notifica. Periodo.
Jug6ernaut

1
Che ne dici di dare una notifica "falsa"? questo è un trucco?
Muhammad Resna Rizki Pratama,

Una volta configurata la notifica in primo piano, puoi rimuovere le "notifiche in esecuzione in background" utilizzando l' app Rubber .
Laurent

1
@MuhammadResnaRizkiPratama, valuteresti di cambiare la risposta accettata? Questa domanda è estremamente popolare ma la risposta accettata è completamente obsoleta, troppo pesante e fa supposizioni sul motivo per cui lo sviluppatore ne ha bisogno. Suggerisco questa risposta perché è affidabile, non sfrutta i bug del sistema operativo e funziona sulla maggior parte dei dispositivi Android.
Sam

Risposte:


95

Come caratteristica di sicurezza della piattaforma Android, non puoi , in nessuna circostanza, avere un servizio in primo piano senza avere anche una notifica. Questo perché un servizio in primo piano consuma una quantità maggiore di risorse ed è soggetto a diversi vincoli di pianificazione (cioè, non viene interrotto così rapidamente) rispetto ai servizi in background e l'utente deve sapere cosa potrebbe consumare la batteria. Quindi, non farlo.

Tuttavia, è possibile avere una notifica "falsa", ovvero è possibile creare un'icona di notifica trasparente (iirc). Questo è estremamente falso per i tuoi utenti e non hai motivo di farlo, a parte uccidere la loro batteria e creare così malware.


3
Grazie. Questo mi rende chiaro perché setForeground necessita della notifica.
Muhammad Resna Rizki Pratama

21
Beh, sembra che tu abbia un motivo per farlo, gli utenti lo hanno chiesto. Sebbene possa essere falso come dici, è stato richiesto per un prodotto su cui sto lavorando dai gruppi di test. Forse Android dovrebbe avere un'opzione per nascondere le notifiche da alcuni servizi che sai essere sempre in esecuzione. Altrimenti la barra delle notifiche sembrerà un albero di Natale.
Radu

3
@Radu in realtà non dovresti avere così tante app in primo piano: questo ucciderà la durata della batteria, perché sono programmate in modo diverso (essenzialmente, non muoiono). Questo potrebbe essere accettabile per una mod, ma non vedo quell'opzione che lo farà in Android vanilla in qualsiasi momento presto. Gli "utenti" di solito non sanno cosa è meglio per loro ..
Kristopher Micinski

2
@Radu che non supporta il tuo argomento. Le "app pluripremiate" sono solitamente quelle che effettivamente "risolvono" i buchi di Android attraverso le incongruenze del sistema (come i task killer). Non c'è motivo per cui dovresti dover utilizzare un servizio in primo piano solo perché le "app premiate" lo fanno: se lo fai stai ammettendo di uccidere la batteria dell'utente.
Kristopher Micinski

2
Apparentemente, questo metodo non funzionerà più a partire dalla 4.3. plus.google.com/u/0/105051985738280261832/posts/MTinJWdNL8t
Erbi

79

Aggiornamento: questo è stato "risolto" su Android 7.1. https://code.google.com/p/android/issues/detail?id=213309

Dall'aggiornamento 4.3, è praticamente impossibile avviare un servizio startForeground()senza mostrare una notifica.

Puoi, tuttavia, nascondere l'icona utilizzando le API ufficiali ... non è necessaria un'icona trasparente: (utilizzare NotificationCompatper supportare le versioni precedenti)

NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setPriority(Notification.PRIORITY_MIN);

Ho fatto pace con il fatto che la notifica stessa deve ancora essere lì, ma per chi vuole ancora nasconderla, potrei aver trovato una soluzione anche per questo:

  1. Avvia un servizio falso startForeground()con la notifica e tutto il resto.
  2. Avvia il servizio reale che desideri eseguire, anche con startForeground()(stesso ID notifica)
  3. Arresta il primo servizio (falso) (puoi chiamare stopSelf()e in onDestroy chiamare stopForeground(true)).

Ecco! Nessuna notifica e il tuo secondo servizio continua a funzionare.


23
Grazie per questo. Penso che alcune persone non si rendano conto che esiste uno sviluppo Android che va avanti per uso aziendale interno (cioè, non è destinato a comparire sul mercato o essere venduto a Joe Blow). A volte le persone chiedono cose come "smetti di mostrare la notifica di servizio" ed è bello vedere soluzioni anche se non è kosher per il consumo generale.
JamieB

5
@ JamieB Non potrei essere più d'accordo ... Ho suggerito a Dianne Hackborn di ripensare l'intero concetto di servizi in background e magari aggiungere un'autorizzazione per eseguire un servizio in background senza le notifiche. Non è importante per noi sviluppatori se c'è una notifica o meno: è la richiesta dell'UTENTE di rimuoverla! :) A proposito, puoi verificare che funzioni anche per te?
Lior Iluz

1
@JamieB user875707 dice di impostare il parametro icon (l'ID risorsa dell'icona) su 0, non l'id della notifica (se è così, come dice la documentazione "startForeground" non funzionerà).
wangqi060934

9
Ovviamente dovresti presumere che questo non funzionerà nelle versioni future della piattaforma.
hackbod

1
@ JamieB In realtà l'ho testato adesso su Android 5.0.1 e funziona ancora per me.
Lior Iluz

20

Non funziona più a partire da Android 7.1 e potrebbe violare le norme per gli sviluppatori di Google Play .

Chiedi invece all'utente di bloccare la notifica del servizio .


Ecco la mia implementazione della tecnica nella risposta di Lior Iluz .

Codice

ForegroundService.java

public class ForegroundService extends Service {

    static ForegroundService instance;

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

        instance = this;

        if (startService(new Intent(this, ForegroundEnablingService.class)) == null)
            throw new RuntimeException("Couldn't find " + ForegroundEnablingService.class.getSimpleName());
    }

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

        instance = null;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

}

ForegroundEnablingService.java

public class ForegroundEnablingService extends Service {

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (ForegroundService.instance == null)
            throw new RuntimeException(ForegroundService.class.getSimpleName() + " not running");

        //Set both services to foreground using the same notification id, resulting in just one notification
        startForeground(ForegroundService.instance);
        startForeground(this);

        //Cancel this service's notification, resulting in zero notifications
        stopForeground(true);

        //Stop this service so we don't waste RAM.
        //Must only be called *after* doing the work or the notification won't be hidden.
        stopSelf();

        return START_NOT_STICKY;
    }

    private static final int NOTIFICATION_ID = 10;

    private static void startForeground(Service service) {
        Notification notification = new Notification.Builder(service).getNotification();
        service.startForeground(NOTIFICATION_ID, notification);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

}

AndroidManifest.xml

<service android:name=".ForegroundEnablingService" />
<service android:name=".ForegroundService" />

Compatibilità

Testato e funzionante su:

  • Emulatore ufficiale
    • 4.0.2
    • 4.1.2
    • 4.2.2
    • 4.3.1
    • 4.4.2
    • 5.0.2
    • 5.1.1
    • 6.0
    • 7.0
  • Sony Xperia M
    • 4.1.2
    • 4.3
  • Samsung Galaxy ?
    • 4.4.2
    • 5.X
  • Genmony
    • 5.0
    • 6.0
  • CyanogenMod
    • 5.1.1

Non funziona più a partire da Android 7.1.


2
Non l'ho ancora provato, ma sarebbe bello avere almeno un permesso per quello !!! +1 per la parte "A Google", I miei utenti si lamentano della notifica richiesta per mantenere l'app in esecuzione in background, Mi lamento anche della notifica richiesta per Skype / llama e qualsiasi altra app disponibile
Rami Dabain

scusa come questo esempio può essere utilizzato per ascoltare gli allarmi. Sto sviluppando un'app che imposta gli allarmi con alarmManager e il problema che ho è che quando chiudo l'app (App recenti-> Cancella) anche tutti gli allarmi vengono rimossi e sto cercando un modo per
evitarlo

@ Ares91, prova a fare una domanda StackOverflow separata per questo e assicurati di includere quante più informazioni possibili, incluso il codice pertinente. Non so molto sugli allarmi e questa domanda riguarda solo i servizi.
Sam il

@Sam quale servizio dovrebbe essere chiamato per primo, ForegroundEnablingService o Forground
Kamran Majeed

@AndroidManForegroundService
Sam

14

Puoi usarlo (come suggerito da @Kristopher Micinski):

Notification note = new Notification( 0, null, System.currentTimeMillis() );
note.flags |= Notification.FLAG_NO_CLEAR;
startForeground( 42, note );

AGGIORNARE:

Tieni presente che questo non è più consentito con le versioni di Android KitKat +. E tieni presente che ciò viola più o meno il principio di progettazione in Android che rende le operazioni in background visibili agli utenti come menzionato da @Kristopher Micinski


2
Nota che questo non sembra più essere accettato con SDK 17. Il servizio non andrà in primo piano se il drawable passato alla notifica è 0.
Snicolas

Era un bug e si spera che sia stato risolto. L'idea dei servizi in primo piano è che sono meno inclini a essere uccisi e questo è un modo per garantire che l'utente sia consapevole della sua esistenza.
Martin Marconcini

4
Con Android 18 la notifica non è più invisibile ma mostra che dice "App in esecuzione, tocca per maggiori informazioni o per interrompere l'app"
Emanuel Moecklin

1
Screenshot di uno dei miei clienti: 1gravity.com/k10/Screenshot_2013-07-25-12-35-49.jpg . Anch'io ho un dispositivo 4.3 e posso confermare questa scoperta.
Emanuel Moecklin

2
Avviso: ricevo rapporti sugli arresti anomali da Sony Ericsson LT26i (Xperia S) con Android 4.1.2 (SDK 16): android.app.RemoteServiceException: Notifica errata pubblicata dal pacchetto ...: Impossibile creare l'icona: StatusBarIcon (pkg = ... id = 0x7f020052 level = 0 visible = true num = 0) Ho impostato icon id su 0 fino all'SDK 17. Da SDK 18 l'ho impostato su una risorsa disegnabile valida. Forse hai bisogno di un ID icona valido dall'SDK 16!
almisoft

13

Attenzione : sebbene questa risposta sembri funzionare, di fatto impedisce silenziosamente al tuo servizio di diventare un servizio in primo piano .

Risposta originale:


Basta impostare l'ID della notifica su zero:

// field for notification ID
private static final int NOTIF_ID = 0;

    ...
    startForeground(NOTIF_ID, mBuilder.build());
    NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    mNotificationManager.cancel(NOTIF_ID);
    ...

Un vantaggio che puoi ottenere è Serviceche sarai in grado di funzionare con priorità alta senza essere distrutto dal sistema Android, a meno che non sia sotto pressione della memoria.

MODIFICARE

Per farlo funzionare con Pre-Honeycomb e Android 4.4 e versioni successive, assicurati di utilizzare quello NotificationCompat.Builderfornito da Support Library v7, invece di Notification.Builder.


1
Questo funziona perfettamente per me su 4.3 (18) @vlad sol. Perché nessun altro voto positivo per questa risposta mi chiedo?
Rob McFeely

1
Ho provato questo in Android N Preview 4 e non ha funzionato. Ho provato Notification.Builder, Support Library v4 NotificationCompat.Buildere Support Library v7 NotificationCompat.Builder. La notifica non veniva visualizzata nel cassetto delle notifiche o nella barra di stato, ma il servizio non era in esecuzione in modalità in primo piano quando l'ho controllato utilizzando getRunningServices()e dumpsys.
Sam

@ Sam, quindi questo bug non compare nella versione precedente, corretto?
Anggrayudi H

@AnggrayudiH, con "questo bug", intendi che la notifica non viene visualizzata? O intendi che il servizio non entrerà in modalità primo piano?
Sam il

1
Questo non funziona per me. Il processo viene avviato come primo piano quando id! = 0 viene inviato, ma NON come primo piano quando id = 0. Utilizzare adb shell dumpsys activity servicesper verificare.
albero di barbabietola

10

Puoi nascondere la notifica su Android 9+ utilizzando il layout personalizzato con layout_height = "0dp"

NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.custom_notif);
builder.setContent(remoteViews);
builder.setPriority(NotificationCompat.PRIORITY_LOW);
builder.setVisibility(Notification.VISIBILITY_SECRET);

custom_notif.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="0dp">
</LinearLayout>

Testato su Pixel 1, Android 9. Questa soluzione non funziona su Android 8 o meno


1
Bella soluzione alternativa. Inoltre penso che richieda l'icona su Android 10. L'uso di un'immagine segnaposto trasparente per l'icona risolve il problema.
shyos

7

Aggiornamento: non funziona più in Android 4.3 e versioni successive


C'è una soluzione alternativa. Prova a creare una notifica senza impostare l'icona e la notifica non verrà visualizzata. Non so come funziona, ma funziona :)

    Notification notification = new NotificationCompat.Builder(this)
            .setContentTitle("Title")
            .setTicker("Title")
            .setContentText("App running")
            //.setSmallIcon(R.drawable.picture)
            .build();
    startForeground(101,  notification);

3
In Android N Preview 4, questo non funziona. Android mostra una (YourServiceName) is runningnotifica invece della tua quando non specifichi un'icona. Secondo CommonsWare, questo è stato il caso da Android 4.3: commonsware.com/blog/2013/07/30/…
Sam

1
Ho fatto alcuni test oggi e ho confermato che questo non funziona con Android 4.3 e versioni successive.
Sam

5

Aggiornamento: non funziona più in Android 4.3 e versioni successive


Ho impostato il parametro icon sul costruttore per Notification su zero, quindi ho passato la notifica risultante a startForeground (). Nessun errore nel registro e nessuna notifica viene visualizzata. Non so, però, se il servizio sia stato messo in primo piano con successo: esiste un modo per verificarlo?

Modificato: verificato con dumpsys, e in effetti il ​​servizio è in primo piano sul mio sistema 2.3. Non ho ancora verificato con altre versioni del sistema operativo.


9
Utilizzare il comando "adb shell dumpsys activity services" per verificare se "isForeground" è impostato su true.
nero

1
Oppure chiama semplicemente il Notification()costruttore vuoto .
johsin18

Questo ha funzionato per me. La prima volta che ho eseguito il mio servizio durante la notte, quindi è decisamente in primo piano (più dumpsys dice che lo è) e nessuna notifica.
JamieB

Nota che questo non funziona più in Android 4.3 (API 18). Il sistema operativo inserisce una notifica segnaposto nel cassetto delle notifiche.
Sam

4

Blocca la notifica del servizio in primo piano

La maggior parte delle risposte qui o non funziona, interrompe il servizio in primo piano o viola le norme di Google Play .

L'unico modo per nascondere in modo affidabile e sicuro la notifica è farla bloccare dall'utente.

Android 4.1 - 7.1

L'unico modo è bloccare tutte le notifiche dalla tua app:

  1. Invia l'utente alla schermata dei dettagli dell'app:

    Uri uri = Uri.fromParts("package", getPackageName(), null);
    Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(uri);
    startActivity(intent);
    
  2. Avere notifiche dell'app per il blocco degli utenti

Nota che questo blocca anche i toast della tua app.

Android 8.0 - 8.1

Non vale la pena bloccare la notifica su Android O perché il sistema operativo lo sostituirà semplicemente con un "in esecuzione in background " o " utilizzando la batteria notifica ".

Android 9+

Utilizza un canale di notifica per bloccare la notifica del servizio senza influire sulle altre notifiche.

  1. Assegna la notifica di servizio al canale di notifica
  2. Invia l'utente alle impostazioni del canale di notifica

    Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
        .putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName())
        .putExtra(Settings.EXTRA_CHANNEL_ID, myNotificationChannel.getId());
    startActivity(intent);
    
  3. Fai in modo che l'utente blocchi le notifiche del canale


2

versione 4.3 (18) e successive non è possibile nascondere la notifica del servizio, ma è possibile disabilitare l'icona, versione 4.3 (18) e successive è possibile nascondere la notifica

Notification noti = new Notification();
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
    noti.priority = Notification.PRIORITY_MIN;
}
startForeground(R.string.app_name, noti);

Sebbene questa risposta sia probabilmente corretta e utile, è preferibile includere qualche spiegazione insieme ad essa per spiegare come aiuta a risolvere il problema. Ciò diventa particolarmente utile in futuro, se c'è un cambiamento (forse non correlato) che fa smettere di funzionare e gli utenti devono capire come funzionava una volta.
Kevin Brown

Penso che Android 4.3 sia effettivamente API 18. Inoltre, questo metodo nasconde solo l'icona dalla barra di stato; l'icona è ancora presente nella notifica.
Sam

per nascondere l'icona dalla notifica è possibile aggiungere un'immagine vuota mBuilder.setSmallIcon (R.drawable.blank);
vlad sol

1
Sto guardando la fonte kitkat e il metodo zero ID non sembra funzionare. se l'ID è zero, il record del servizio viene aggiornato in NOT foreground.
Jeffrey Blattman

2

Ho scoperto che su Android 8.0 è ancora possibile non utilizzare un canale di notifica.

public class BootCompletedIntentReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

                Intent notificationIntent = new Intent(context, BluetoothService.class);    
                context.startForegroundService(notificationIntent);

            } else {
                //...
            }

        }
    }
}

E in BluetoothService.class:

 @Override
    public void onCreate(){    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

            Intent notificationIntent = new Intent(this, BluetoothService.class);

            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

            Notification notification = new Notification.Builder(this)
                    .setContentTitle("Title")
                    .setContentText("App is running")
                    .setSmallIcon(R.drawable.notif)
                    .setContentIntent(pendingIntent)
                    .setTicker("Title")
                    .setPriority(Notification.PRIORITY_DEFAULT)
                    .build();

            startForeground(15, notification);

        }

    }

Non viene visualizzata una notifica persistente, tuttavia vedrai la notifica "x app Android in esecuzione in background".


1
In Android 8.1: "Notifica errata per startForeground: java.lang.RuntimeException: canale non valido per notifica di servizio"
T-igra

Personalmente, penso che la apps are running in the backgroundnotifica sia persino peggiore di una notifica specifica per app, quindi non sono sicuro che questo approccio valga la pena.
Sam

-2

Aggiornamento: non funziona più su Android 7.1 e versioni successive


Ecco un modo per impostare oom_adj della tua app su 1 (testato nell'emulatore SDK ANDROID 6.0) Aggiungi un servizio temporaneo, nella chiamata di servizio principale startForgroundService(NOTIFICATION_ID, notificion). Quindi avvia di nuovo la chiamata di servizio temporanea startForgroundService(NOTIFICATION_ID, notificion)con lo stesso ID di notifica, dopo un po 'nella chiamata di servizio temporanea stopForgroundService (true) per ignorare l'ontificazione in corso.


Questa è l'unica soluzione funzionante di cui sono a conoscenza. Tuttavia, è già coperto da un'altra risposta .
Sam

-3

Puoi anche dichiarare la tua applicazione come persistente.

<application
    android:icon="@drawable/icon"
    android:label="@string/app_name"
    android:theme="@style/Theme"
    *android:persistent="true"* >
</application>

Questo essenzialmente imposta la tua app su una priorità di memoria più alta, diminuendo la probabilità che venga uccisa.


5
Questo è sbagliato, solo le app di sistema possono essere persistenti: groups.google.com/forum/?fromgroups=#!topic/android-developers/…
Snicolas

-6

Ho sviluppato un semplice lettore multimediale un paio di mesi fa. Quindi quello che credo è se stai facendo qualcosa del tipo:

Intent i = new Intent(this, someServiceclass.class);

startService (i);

Quindi il sistema non dovrebbe essere in grado di terminare il tuo servizio.

riferimento : leggere il paragrafo che discute quando il sistema interrompe il servizio


Ho letto in alcuni articoli. Se esegui solo startService (), il sistema necessita di più memoria. Quindi il sistema interromperà quel servizio. questo è vero? Non ho tempo per fare esperimenti in servizio.
Muhammad Resna Rizki Pratama,

1
Muhammad, questo è vero - lo scopo di startForeground () è di dare effettivamente al servizio una "priorità di memoria" più alta rispetto ai servizi in background in modo che possa sopravvivere più a lungo. StartForeground () dovrebbe utilizzare una notifica in modo che l'utente sia più consapevole di un servizio più persistente / difficile da uccidere che è in esecuzione.
DustinB

Questo semplicemente non è vero; è praticamente impossibile realizzare un servizio che non può essere ucciso su Android. Android elimina prontamente i servizi non in primo piano per liberare memoria.
Sam
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.