web-dev-qa-db-fra.com

Comment démarrer en arrière-plan () sans afficher de notification?

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 montrer aucune notification. Est-ce possible?

Pouvez-vous me donner des exemples? Y a-t-il des alternatives?

Mon service d'application est en train de faire mediaplayer. Comment faire en sorte que le système ne tue pas mon service sauf que l'application le tue lui-même (comme mettre en pause ou arrêter la musique avec un bouton).

En tant que fonction de sécurité de la plate-forme Android, vous ne pouvez pas , sous aucune , disposez d’un service de premier plan sans notification préalable, car un service de premier plan consomme une quantité de ressources plus importante et est soumis à différentes contraintes de planification (c’est-à-dire qu’il n’est pas tué rapidement) que les services d'arrière-plan et que l'utilisateur a besoin de savoir ce qui lui est imposé par sa batterie, donc ne le faites pas .

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 hypocrite pour vos utilisateurs, et vous n'avez aucune raison de le faire, si ce n'est de tuer leur batterie et de créer ainsi un malware.

82

Mise à jour: Ceci a été "corrigé" le Android 7.1. https://code.google. com/p/Android/issues/detail? id = 213309

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

Vous pouvez toutefois masquer l'icône à l'aide des API officielles ... inutile d'utiliser une icône transparente: (utilisez NotificationCompat pour prendre en charge les versions antérieures)

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 qui que ce soit qui veuille encore la cacher, j'ai peut-être trouvé une solution de contournement à cela:

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

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

75
Lior Iluz

Cela ne fonctionne plus à partir de Android 7.1 et cela pourrait enfreindre règles de développement de Google Play .

Au lieu de cela, je suggère ayant l'utilisateur bloque la notification de service .


Voici ma mise en œuvre de la technique dans la réponse par 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 travaillant sur:

  • Emulateur 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 à partir de Android 7.1.

19
Sam

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

PDATE:

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

15
Snicolas

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 des avantages que vous pouvez obtenir est qu'un Service pourra fonctionner en priorité élevée sans être détruit par le système Android, sauf en cas de forte pression de la mémoire.

EDIT

Pour que cela fonctionne avec Pre-Honeycomb et Android 4.4 et versions supérieures, assurez-vous d’utiliser NotificationCompat.Builder fourni par Support Library v7 au lieu de Notification.Builder.

9
Anggrayudi H

Il existe une solution de contournement. Essayez de créer une notification sans configurer l'icône, 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);
6

J'ai mis le paramètre icon au constructeur de Notification pour Notification à zéro, puis j'ai transmis la notification résultante à startForeground (). Aucune erreur dans le journal et aucune notification ne s'affiche. Je ne sais toutefois pas si le service a été mis en avant avec succès. Existe-t-il un moyen de vérifier?

Édité: Vérifié avec dumpsys, et le service est mis en avant sur mon système 2.3. Je n'ai pas encore vérifié avec les autres versions du système d'exploitation.

6
Alexander Pruss

Vous pouvez masquer la notification sur Android 9 en utilisant une disposition 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

2
phnmnn

J'ai trouvé sur Android 8.0, il est toujours possible de ne pas utiliser un 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 ne s'affiche pas, mais vous verrez la notification Android "Les applications sont exécutées en arrière-plan".

2
Pete

Bloquer la notification de service de premier plan

Android 7.1+ ne peut pas être exploité pour masquer la notification. Au lieu de cela, demandez à l'utilisateur de le bloquer.

Android 4.1 - 7.1

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

  1. Envoyer l'utilisateur à l'écran de 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. Avoir les notifications de l'application utilisateur de blocage

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

Android 8

Il ne vaut pas la peine de bloquer la notification sur Android O car le système d'exploitation ne la remplace que par un " s'exécute en arrière-plan" ou " avec batterie "notification.

Android 9

Utilisez un Notification Channel pour bloquer uniquement la notification de service.

  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. Avoir les notifications des canaux bloqués par l'utilisateur

2
Sam

il est impossible de masquer la notification de service dans les versions 4.3 (18) et supérieures, mais vous pouvez désactiver l'icône. La version 4.3 (18) et les versions ultérieures permettent de masquer 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);
2
vlad sol