Comment démarrerForeground () sans afficher de notification?

87

Je veux créer un service et le faire fonctionner au premier plan.

La plupart des exemples de codes comportent des notifications. Mais je ne veux afficher aucune notification. Est-ce possible?

Pouvez-vous me donner quelques exemples? Existe-t-il des alternatives?

Mon service d'application utilise Mediaplayer. Comment faire en sorte que le système ne tue pas mon service sauf que l'application le tue elle-même (comme mettre en pause ou arrêter la musique par bouton).

Muhammad Resna Rizki Pratama
la source
11
Vous ne pouvez pas avoir de service "Premier plan" sans notification. Période.
Jug6ernaut
1
Que diriez-vous de donner une "fausse" notification? c'est un truc?
Muhammad Resna Rizki Pratama
Une fois la notification de premier plan configurée, vous pouvez supprimer "les notifications en cours d'exécution en arrière-plan" à l'aide de l' application Rubber .
Laurent
1
@MuhammadResnaRizkiPratama, envisageriez-vous de changer la réponse acceptée? Cette question est extrêmement populaire, mais la réponse acceptée est complètement obsolète, trop lourde et suppose pourquoi le développeur en a besoin. Je suggère cette réponse car elle est fiable, n'exploite pas les bogues du système d'exploitation et fonctionne sur la plupart des appareils Android.
Sam

Réponses:

95

En tant que fonction de sécurité de la plate-forme Android, vous ne pouvez en aucun cas avoir un service au premier plan sans avoir également une notification. En effet, un service au premier plan consomme une plus grande quantité de ressources et est soumis à des contraintes de planification différentes (c'est-à-dire qu'il n'est pas tué aussi rapidement) que les services d'arrière-plan, et l'utilisateur a besoin de savoir ce qui peut manger sa batterie. Alors, ne fais pas ça.

Cependant, il est possible d'avoir une "fausse" notification, c'est-à-dire que vous pouvez créer une icône de notification transparente (iirc). C'est extrêmement malhonnête pour vos utilisateurs et vous n'avez aucune raison de le faire, à part tuer leur batterie et créer ainsi des logiciels malveillants.

Kristopher Micinski
la source
3
Merci. Cela me permet de comprendre pourquoi setForeground a besoin de la notification.
Muhammad Resna Rizki Pratama
21
Eh bien, il semble que vous ayez une raison de le faire, les utilisateurs l'ont demandé. Bien que cela puisse être malhonnête comme vous le dites, il a été demandé pour un produit sur lequel je travaille par les groupes de test. Peut-être qu'Android devrait avoir une option pour masquer les notifications de certains services dont vous savez qu'ils sont toujours en cours d'exécution. Sinon, votre barre de notification ressemblera à un arbre de Noël.
Radu
3
@Radu en fait, vous ne devriez pas avoir autant d'applications au premier plan: cela va tuer votre batterie, car elles sont programmées différemment (essentiellement, elles ne meurent pas). Cela pourrait être acceptable pour un mod, mais je ne vois pas cette option en faire un Android vanilla de si tôt. Les "utilisateurs" ne savent généralement pas ce qui est le mieux pour eux.
Kristopher Micinski
2
@Radu qui ne supporte pas votre argument. Les "applications primées" sont généralement celles qui "corrigent" les trous d'Android à travers les incohérences du système (comme les tueurs de tâches). Il n'y a aucune raison que vous deviez utiliser un service de premier plan simplement parce que les "applications primées" le font: si vous le faites, vous admettez avoir tué la batterie de l'utilisateur.
Kristopher Micinski
2
Apparemment, cette méthode ne fonctionnera plus à partir de 4.3. plus.google.com/u/0/105051985738280261832/posts/MTinJWdNL8t
erbi
79

Mise à jour: cela a été «corrigé» sur Android 7.1. https://code.google.com/p/android/issues/detail?id=213309

Depuis la mise à jour 4.3, il est pratiquement impossible de démarrer un service avec startForeground()sans afficher de notification.

Vous pouvez cependant masquer l'icône à l'aide des API officielles ... pas besoin d'icône transparente: (à utiliser NotificationCompatpour prendre en charge les anciennes versions)

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

J'ai fait la paix avec le fait que la notification elle-même doit toujours être là, mais pour quiconque veut toujours la cacher, j'ai peut-être trouvé une solution de contournement pour cela aussi:

  1. Démarrez un faux service startForeground()avec la notification et tout.
  2. Démarrez le service réel que vous souhaitez exécuter, également avec startForeground()(même ID de notification)
  3. Arrêtez le premier (faux) service (vous pouvez appeler stopSelf()et appeler onDestroy stopForeground(true)).

Voilà! Aucune notification du tout et votre deuxième service continue de fonctionner.

Lior Iluz
la source
23
Merci pour cela. Je pense que certaines personnes ne réalisent pas qu'il existe un développement Android destiné à un usage interne (c'est-à-dire qu'il n'est pas destiné à apparaître sur le marché ou à être vendu à Joe Blow). Parfois, les gens demandent des choses comme "arrêter d'afficher la notification de service" et il est bon de voir des solutions de contournement même si ce n'est pas casher pour la consommation générale.
JamieB
5
@JamieB Je ne pourrais pas être plus d'accord ... J'ai suggéré à Dianne Hackborn de repenser l'ensemble du concept des services d'arrière-plan et peut-être d'ajouter une autorisation pour exécuter un service en arrière-plan sans les notifications. Ce n'est pas comme si cela importait pour nous, les développeurs, qu'il y ait une notification ou non - c'est la demande de l'UTILISATEUR de la supprimer! :) Au fait, pouvez-vous vérifier que cela fonctionne aussi pour vous?
Lior Iluz
1
@JamieB user875707 dit de définir le paramètre d'icône (l'ID de ressource de l'icône) sur 0, pas l'ID de notification (si tel est le cas, comme le dit la documentation, "startForeground" ne fonctionnera pas).
wangqi060934
9
Vous devez bien sûr supposer que cela ne fonctionnera pas dans les futures versions de la plate-forme.
hackbod
1
@JamieB J'ai testé cela en ce moment sur Android 5.0.1 et cela fonctionne toujours pour moi.
Lior Iluz
20

Cela ne fonctionne plus à partir d'Android 7.1 et peut enfreindre les politiques des développeurs de Google Play .

Au lieu de cela, demandez à l'utilisateur de bloquer la notification de service .


Voici ma mise en œuvre de la technique dans la réponse de Lior Iluz .

Code

ForegroundService.java

public class ForegroundService extends Service {

    static ForegroundService instance;

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

        instance = this;

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

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

        instance = null;
    }

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

}

ForegroundEnablingService.java

public class ForegroundEnablingService extends Service {

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

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

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

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

        return START_NOT_STICKY;
    }

    private static final int NOTIFICATION_ID = 10;

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

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

}

AndroidManifest.xml

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

Compatibilité

Testé et travaille sur:

  • Émulateur officiel
    • 4.0.2
    • 4.1.2
    • 4.2.2
    • 4.3.1
    • 4.4.2
    • 5.0.2
    • 5.1.1
    • 6,0
    • 7,0
  • Sony Xperia M
    • 4.1.2
    • 4.3
  • Samsung Galaxy ?
    • 4.4.2
    • 5.X
  • Genymotion
    • 5,0
    • 6,0
  • CyanogenMod
    • 5.1.1

Ne fonctionne plus depuis Android 7.1.

Sam
la source
2
Je ne l'ai pas encore testé, mais ce serait bien d'avoir au moins une permission pour ça !!! +1 pour la partie "À Google", mes utilisateurs se plaignent de la notification requise pour maintenir l'application en arrière-plan, je me plains également de la notification requise pour skype / llama et toute autre application
Rami Dabain
désolé comment cet exemple peut être utilisé pour écouter les alarmes. Je développe une application qui définit des alarmes avec alarmManager et le problème que j'ai est que lorsque je ferme l'application (Applications récentes-> Effacer) toutes les alarmes sont également supprimées et je cherche un moyen d'éviter cela
Ares91
@ Ares91, essayez de créer une question StackOverflow distincte pour cela et assurez-vous d'inclure autant d'informations que possible, y compris le code correspondant. Je ne connais pas grand-chose aux alarmes, et cette question concerne uniquement les services.
Sam
@Sam quel service doit être appelé en premier, ForegroundEnablingService ou Forground
Android Man
@AndroidManForegroundService
Sam
14

Vous pouvez utiliser ceci (comme suggéré par @Kristopher Micinski):

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

MISE À JOUR:

Veuillez noter que cela n'est plus autorisé avec les versions Android KitKat +. Et gardez à l'esprit que cela viole plus ou moins le principe de conception d'Android qui rend les opérations en arrière-plan visibles pour les utilisateurs, comme mentionné par @Kristopher Micinski

Snicolas
la source
2
Notez que cela ne semble plus être accepté avec le SDK 17. Le service ne passera pas au premier plan si le dessinable passé à la notification est 0.
Snicolas
C'était un bug qui, espérons-le, a été corrigé. L'idée des services Foreground est qu'ils sont moins susceptibles d'être tués, et c'est un moyen de s'assurer que l'utilisateur est conscient de son existence.
Martin Marconcini le
4
Avec Android 18, la notification n'est plus invisible mais indique "L'application est en cours d'exécution, touchez pour plus d'informations ou pour arrêter l'application"
Emanuel Moecklin
1
Capture d'écran d'un de mes clients: 1gravity.com/k10/Screenshot_2013-07-25-12-35-49.jpg . J'ai aussi un appareil 4.3 et je peux confirmer cette constatation.
Emanuel Moecklin
2
Avertissement: Je reçois des rapports de plantage de Sony Ericsson LT26i (Xperia S) avec Android 4.1.2 (SDK 16): android.app.RemoteServiceException: Mauvaise notification publiée depuis le package ...: Impossible de créer l'icône: StatusBarIcon (pkg = ... id = 0x7f020052 level = 0 visible = true num = 0) J'ai également défini l'ID d'icône sur 0 jusqu'au SDK 17. À partir du SDK 18, je l'ai défini sur une ressource dessinable valide. Peut-être avez-vous besoin d'un identifiant d'icône valide du SDK 16!
almisoft
13

Attention : bien que cette réponse semble fonctionner, elle empêche en fait silencieusement votre service de devenir un service de premier plan .

Réponse originale:


Définissez simplement l'ID de votre notification sur zéro:

// 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 avantage que vous pouvez obtenir est que le système Servicesera capable de fonctionner en haute priorité sans être détruit par le système Android, sauf en cas de forte pression de la mémoire.

ÉDITER

Pour le faire fonctionner avec Pre-Honeycomb et Android 4.4 et versions ultérieures, assurez-vous d'utiliser NotificationCompat.Buildercelui fourni par Support Library v7, au lieu de Notification.Builder.

Anggrayudi H
la source
1
Cela fonctionne parfaitement pour moi sur 4.3 (18) @vlad sol. Pourquoi pas encore d'autres votes positifs pour cette réponse, je me demande?
Rob McFeely
J'ai essayé cela dans Android N Preview 4 et cela n'a pas fonctionné. J'ai essayé Notification.Builder, Support Library v4 NotificationCompat.Builderet Support Library v7 NotificationCompat.Builder. La notification n'apparaissait pas dans le tiroir de notification ou la barre d'état, mais le service ne fonctionnait pas en mode de premier plan lorsque je l'ai vérifié à l'aide de getRunningServices()et dumpsys.
Sam
@Sam, donc ce bogue n'apparaît pas sur la version antérieure, n'est-ce pas?
Anggrayudi H
@AnggrayudiH, par "ce bug", voulez-vous dire que la notification n'apparaît pas? Ou voulez-vous dire que le service ne passe pas en mode premier plan?
Sam
1
Cela ne fonctionne pas pour moi. Le processus est lancé au premier plan lorsque id! = 0 est envoyé, mais PAS au premier plan lorsque id = 0. Utilisez adb shell dumpsys activity servicespour vérifier.
beetree
8

Vous pouvez masquer la notification sur Android 9+ en utilisant une mise en page personnalisée avec 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>

Testé sur Pixel 1, Android 9. Cette solution ne fonctionne pas sur Android 8 ou moins

phnmnn
la source
Belle solution de contournement. Aussi, je pense qu'il nécessite une icône sur Android 10. L'utilisation d'une image d'espace réservé transparente pour l'icône résout le problème.
shyos le
7

Mise à jour: cela ne fonctionne plus sous Android 4.3 et supérieur


Il existe une solution de contournement. Essayez de créer une notification sans icône de réglage et la notification ne s'afficherait pas. Je ne sais pas comment ça marche, mais ça marche :)

    Notification notification = new NotificationCompat.Builder(this)
            .setContentTitle("Title")
            .setTicker("Title")
            .setContentText("App running")
            //.setSmallIcon(R.drawable.picture)
            .build();
    startForeground(101,  notification);
Vladimir Petrovski
la source
3
Dans Android N Preview 4, cela ne fonctionne pas. Android affiche une (YourServiceName) is runningnotification au lieu de la vôtre lorsque vous ne spécifiez pas d'icône. Selon CommonsWare, c'est le cas depuis Android 4.3: commonsware.com/blog/2013/07/30
Sam
1
J'ai fait quelques tests aujourd'hui et j'ai confirmé que cela ne fonctionnait pas sous Android 4.3 et supérieur.
Sam
5

Mise à jour: cela ne fonctionne plus sous Android 4.3 et supérieur


J'ai défini le paramètre icon du constructeur pour Notification sur zéro, puis j'ai passé la notification résultante à startForeground (). Aucune erreur dans le journal et aucune notification n'apparaît. Je ne sais pas, cependant, si le service a été mis en avant avec succès - y a-t-il un moyen de vérifier?

Modifié: vérifié avec dumpsys, et en effet le service est au premier plan sur mon système 2.3. Je n'ai pas encore vérifié avec d'autres versions du système d'exploitation.

Alexandre Pruss
la source
9
Utilisez la commande "adb shell dumpsys activity services" pour vérifier si "isForeground" est défini sur true.
noir le
1
Ou appelez simplement le Notification()constructeur vide .
johsin18
Cela a fonctionné pour moi. La première fois que j'ai fait fonctionner mon service pendant la nuit, il est donc définitivement au premier plan (plus dumpsys le dit) et aucune notification.
JamieB
Notez que cela ne fonctionne plus sous Android 4.3 (API 18). Le système d'exploitation place une notification d'espace réservé dans le tiroir de notification.
Sam
4

Bloquer la notification de service de premier plan

La plupart des réponses ici ne fonctionnent pas, interrompent le service de premier plan ou enfreignent les règles de Google Play .

La seule façon de masquer la notification de manière fiable et sûre est de la bloquer par l'utilisateur.

Android 4.1 - 7.1

Le seul moyen est de bloquer toutes les notifications de votre application:

  1. Envoyer l'utilisateur à l'écran des détails de l'application:

    Uri uri = Uri.fromParts("package", getPackageName(), null);
    Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(uri);
    startActivity(intent);
    
  2. Faire bloquer les notifications de l'application par l'utilisateur

Notez que cela bloque également les toasts de votre application.

Android 8,0 à 8,1

Cela ne vaut pas la peine de bloquer la notification sur Android O car le système d'exploitation la remplacera simplement par une notification " en arrière-plan " ou "en utilisant la batterie ".

Android 9+

Utilisez un canal de notification pour bloquer la notification de service sans affecter vos autres notifications.

  1. Attribuer une notification de service au canal de notification
  2. Envoyer l'utilisateur aux paramètres du canal de notification

    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. Faire bloquer les notifications de la chaîne par l'utilisateur

Sam
la source
2

la version 4.3 (18) et au-dessus de cacher la notification de service n'est pas possible, mais vous pouvez désactiver l'icône, la version 4.3 (18) et ci-dessous est possible pour cacher la notification

Notification noti = new Notification();
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
    noti.priority = Notification.PRIORITY_MIN;
}
startForeground(R.string.app_name, noti);
vlad sol
la source
Bien que cette réponse soit probablement correcte et utile, il est préférable que vous y incluiez des explications pour expliquer comment elle aide à résoudre le problème. Cela devient particulièrement utile à l'avenir, s'il y a un changement (peut-être sans rapport) qui l'empêche de fonctionner et que les utilisateurs doivent comprendre comment cela fonctionnait autrefois.
Kevin Brown
Je pense qu'Android 4.3 est en fait l'API 18. De plus, cette méthode ne masque que l'icône de la barre d'état; l'icône existe toujours dans la notification.
Sam
pour masquer l'icône de la notification, vous pouvez ajouter une image vide mBuilder.setSmallIcon (R.drawable.blank);
vlad sol
1
Je regarde la source kitkat, et la méthode du zéro ID ne semble pas pouvoir fonctionner. si l'ID est zéro, l'enregistrement de service est mis à jour au premier plan.
Jeffrey Blattman
2

J'ai trouvé sur Android 8.0 qu'il est toujours possible de ne pas utiliser de canal de notification.

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 {
                //...
            }

        }
    }
}

Et dans 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);

        }

    }

Une notification persistante n'est pas affichée, mais vous verrez la notification Android 'x applications s'exécutant en arrière-plan'.

Pete
la source
Sous Android 8.1: "Mauvaise notification pour startForeground: java.lang.RuntimeException: canal non valide pour la notification de service"
T-igra
Personnellement, je pense que la apps are running in the backgroundnotification est encore pire qu'une notification spécifique à une application, donc je ne suis pas sûr que cette approche en vaille la peine.
Sam
-2

Mise à jour: cela ne fonctionne plus sous Android 7.1 et supérieur


Voici un moyen de rendre oom_adj de votre application à 1 (Testé dans l'émulateur du SDK ANDROID 6.0).Ajoutez un service temporaire, dans votre appel de service principal startForgroundService(NOTIFICATION_ID, notificion). Et puis redémarrez l'appel de service temporaire startForgroundService(NOTIFICATION_ID, notificion)avec le même identifiant de notification, après un certain temps dans l'appel de service temporaire stopForgroundService (true) pour rejeter l'ontification en cours.

Tshunglee
la source
C'est la seule solution de travail que je connaisse. Cependant, il est déjà couvert par une autre réponse .
Sam
-3

Vous pouvez également déclarer votre application comme persistante.

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

Cela définit essentiellement votre application sur une priorité de mémoire plus élevée, ce qui réduit la probabilité qu'elle soit supprimée.

Habitant
la source
5
C'est faux, seules les applications système peuvent être persistantes: groups.google.com/forum/?fromgroups=#!topic/android-developers/…
Snicolas
-6

J'ai développé un simple lecteur multimédia il y a quelques mois. Donc, ce que je crois, c'est que si vous faites quelque chose comme:

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

startService (i);

Ensuite, le système ne devrait pas être en mesure de tuer votre service.

référence : lisez le paragraphe qui traite du moment où le système arrête le service

redM0nk
la source
J'ai lu dans certains articles. Si vous faites simplement startService (), le système a besoin de plus de mémoire. Donc, le système va tuer ce service. c'est vrai? Je n'ai tout simplement pas le temps de faire des expériences en service.
Muhammad Resna Rizki Pratama
1
Muhammad, c'est vrai - le but de startForeground () est de donner effectivement au service une "priorité mémoire" plus élevée que les services d'arrière-plan afin qu'il puisse survivre plus longtemps. Le startForeground () devrait utiliser une notification afin que l'utilisateur soit plus conscient d'un service plus persistant / plus difficile à tuer qui est en cours d'exécution.
DustinB
Ce n'est tout simplement pas vrai; il est pratiquement impossible de créer un service qui ne peut pas être tué sur Android. Android tue facilement les services non au premier plan pour libérer de la mémoire.
Sam