Android - implémenter startForeground pour un service?

124

Je ne sais donc pas où / comment implémenter cette méthode pour que mon service s'exécute au premier plan. Actuellement, je démarre mon service par ce qui suit dans une autre activité:

Intent i = new Intent(context, myService.class); 
context.startService(i);

Et puis dans onCreate () de myServices j'essaye le startForeground () ...?

Notification notification = new Notification();
startForeground(1, notification);

Alors oui, je suis un peu perdu et je ne sais pas comment mettre en œuvre cela.

JDS
la source
Eh bien, cela ne fonctionne pas, du moins pour autant que je sache, mon service fonctionne toujours comme un service d'arrière-plan et se fait tuer.
JDS
La discussion est liée à: stackoverflow.com/questions/10962418/…
Snicolas

Réponses:

131

Je commencerais par remplir complètement le fichier Notification. Voici un exemple de projet démontrant l'utilisation de startForeground().

CommonsWare
la source
8
Est-il possible d'utiliser startForeground () sans notification? Ou pouvons-nous mettre à jour plus tard la même notification?
JRC
2
Y a-t-il une raison particulière que vous avez utilisée 1337?
Cody
33
@DoctorOreo: il doit être unique dans l'application, mais pas nécessairement unique sur l'appareil. J'ai choisi 1337 parce que, eh bien, c'est 1337 . :-)
CommonsWare
La question @JRC est bonne. Est-il possible d'utiliser startForeground () sans notification?
Snicolas
2
@Snicolas: Merci d'avoir signalé une faille dans Android. Je vais travailler pour résoudre ce problème.
CommonsWare
78

Depuis votre activité principale, démarrez le service avec le code suivant:

Intent i = new Intent(context, MyService.class); 
context.startService(i);

Ensuite, dans votre service pour onCreate()vous, vous construiriez votre notification et la définiriez comme premier plan comme ceci:

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

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

Notification notification = new NotificationCompat.Builder(this)
                .setSmallIcon(R.mipmap.app_icon)
                .setContentTitle("My Awesome App")
                .setContentText("Doing some work...")
                .setContentIntent(pendingIntent).build();

startForeground(1337, notification);
mikebertiean
la source
@mike comment mettre à jour cette notification depuis MainActivity?
Roon13
1
@ Roon13 en utilisant l'ID, dans ce cas 1337 ... vous devriez pouvoir créer une nouvelle notification et appeler startForeground avec l'ID
mikebertiean
@ Roon13 consultez cette question stackoverflow.com/questions/5528288/…
mikebertiean
@mikebertiean Comment puis-je appeler le startForeground depuis MainActivity? également comment puis-je effacer la notification de MainActvity lorsque le processus est terminé?
Roon13
@mikebertiean J'ai compris que je dois à nouveau appeler startForeground en classe Service mais comment? Dois-je appeler à nouveau startService ()?
Roon13
30

Voici mon code pour mettre le service au premier plan:

private void runAsForeground(){
    Intent notificationIntent = new Intent(this, RecorderMainActivity.class);
    PendingIntent pendingIntent=PendingIntent.getActivity(this, 0,
            notificationIntent, Intent.FLAG_ACTIVITY_NEW_TASK);

    Notification notification=new NotificationCompat.Builder(this)
                                .setSmallIcon(R.drawable.ic_launcher)
                                .setContentText(getString(R.string.isRecording))
                                .setContentIntent(pendingIntent).build();

    startForeground(NOTIFICATION_ID, notification);

}

J'ai besoin de créer une notification à l'aide de PendingIntent, afin de pouvoir démarrer mon activité principale à partir de la notification.

Pour supprimer la notification, appelez simplement stopForeground (true);

Il est appelé dans onStartCommand (). Veuillez vous référer à mon code à: https://github.com/bearstand/greyparrot/blob/master/src/com/xiong/richard/greyparrot/Mp3Recorder.java

Richard
la source
Si vous supprimez la notification appelant stopForeground (true), vous annulez le service
startforeground
6
D'où appelez-vous cette méthode?
Srujan Barai du
7
Intent.FLAG_ACTIVITY_NEW_TASKn'est pas valide dans le contexte de PendingIntent.
mixel
30

Solution pour Oreo 8.1

J'ai rencontré des problèmes tels que RemoteServiceException en raison d'un identifiant de canal invalide avec les versions les plus récentes d'Android. Voici comment je l'ai résolu:

Activité :

override fun onCreate(savedInstanceState: Bundle?) {
    val intent = Intent(this, BackgroundService::class.java)

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

Service d'arrière-plan:

override fun onCreate() {
    super.onCreate()
    startForeground()
}

private fun startForeground() {

    val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    val channelId =
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                createNotificationChannel()
            } else {
                // If earlier version channel ID is not used
                // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
                ""
            }

    val notificationBuilder = NotificationCompat.Builder(this, channelId )
    val notification = notificationBuilder.setOngoing(true)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setPriority(PRIORITY_MIN)
            .setCategory(Notification.CATEGORY_SERVICE)
            .build()
    startForeground(101, notification)
}


@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(): String{
    val channelId = "my_service"
    val channelName = "My Background Service"
    val chan = NotificationChannel(channelId,
            channelName, NotificationManager.IMPORTANCE_HIGH)
    chan.lightColor = Color.BLUE
    chan.importance = NotificationManager.IMPORTANCE_NONE
    chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
    val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    service.createNotificationChannel(chan)
    return channelId
}

ÉQUIVALENT JAVA

public class YourService extends Service {

    // Constants
    private static final int ID_SERVICE = 101;

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

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

        // do stuff like register for BroadcastReceiver, etc.

        // Create the Foreground Service
        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        String channelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? createNotificationChannel(notificationManager) : "";
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId);
        Notification notification = notificationBuilder.setOngoing(true)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setPriority(PRIORITY_MIN)
                .setCategory(NotificationCompat.CATEGORY_SERVICE)
                .build();

        startForeground(ID_SERVICE, notification);
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private String createNotificationChannel(NotificationManager notificationManager){
        String channelId = "my_service_channelid";
        String channelName = "My Foreground Service";
        NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
        // omitted the LED color
        channel.setImportance(NotificationManager.IMPORTANCE_NONE);
        channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
        notificationManager.createNotificationChannel(channel);
        return channelId;
    }
}
Rawa
la source
8
Vous pouvez utiliser ContextCompat.startForegroundService(Context,Intent)dans votre activité ce qui fera la bonne chose. ( developer.android.com/reference/android/support/v4/content/… )
Simon Featherstone
3
vous voudrez probablement utiliser .setCategory(NotificationCompat.CATEGORY_SERVICE)au lieu de Notification.CATEGORY_SERVICEsi votre API min est <21
Someone Somewhere
6
Veuillez noter que le ciblage d'applications Build.VERSION_CODES.P(niveau d'API 28) ou version ultérieure doit demander l'autorisation Manifest.permission.FOREGROUND_SERVICEpour pouvoir utiliser startForeground()- voir developer.android.com/reference/android/app/...
Vadim Kotov
21

En plus de la réponse RAWA , cette paix de code:

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

Vous pouvez changer pour:

ContextCompat.startForegroundService(context, yourIntent);

Si vous regardez à l'intérieur de cette méthode, vous pouvez voir que cette méthode fait tout le travail de vérification pour vous.

Edgar Khimich
la source
9

Si vous souhaitez faire d'IntentService un service de premier plan

alors tu devrais remplacer onHandleIntent()comme ça

Override
protected void onHandleIntent(@Nullable Intent intent) {


    startForeground(FOREGROUND_ID,getNotification());     //<-- Makes Foreground

   // Do something

    stopForeground(true);                                // <-- Makes it again a normal Service                         

}

Comment faire une notification?

Facile. Voici la getNotification()méthode

public Notification getNotification()
{

    Intent intent = new Intent(this, SecondActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);


    NotificationCompat.Builder foregroundNotification = new NotificationCompat.Builder(this);
    foregroundNotification.setOngoing(true);

    foregroundNotification.setContentTitle("MY Foreground Notification")
            .setContentText("This is the first foreground notification Peace")
            .setSmallIcon(android.R.drawable.ic_btn_speak_now)
            .setContentIntent(pendingIntent);


    return foregroundNotification.build();
}

Compréhension plus approfondie

Que se passe-t-il lorsqu'un service devient un service de premier plan

Ça arrive

entrez la description de l'image ici

Qu'est-ce qu'un service de premier plan?

Un service de premier plan,

  • s'assure que l'utilisateur est activement conscient que quelque chose se passe en arrière-plan en fournissant la notification.

  • (le plus important) n'est pas tué par le système lorsqu'il manque de mémoire

Un cas d'utilisation du service de premier plan

Implémentation de la fonctionnalité de téléchargement de chanson dans une application musicale

Rohit Singh
la source
5

Ajouter une classe de service de code donnée pour "OS> = Build.VERSION_CODES.O" dans onCreate ()

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

     .................................
     .................................

    //For creating the Foreground Service
    NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    String channelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? getNotificationChannel(notificationManager) : "";
    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId);
    Notification notification = notificationBuilder.setOngoing(true)
            .setSmallIcon(R.mipmap.ic_launcher)
           // .setPriority(PRIORITY_MIN)
            .setCategory(NotificationCompat.CATEGORY_SERVICE)
            .build();

    startForeground(110, notification);
}



@RequiresApi(Build.VERSION_CODES.O)
private String getNotificationChannel(NotificationManager notificationManager){
    String channelId = "channelid";
    String channelName = getResources().getString(R.string.app_name);
    NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
    channel.setImportance(NotificationManager.IMPORTANCE_NONE);
    channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
    notificationManager.createNotificationChannel(channel);
    return channelId;
}

Ajoutez cette autorisation dans le fichier manifeste:

 <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
Kush
la source
1

Gérez l' intention sur startCommand du service à l'aide de.

 stopForeground(true)

Cet appel supprimera le service de l'état de premier plan , ce qui lui permettra d'être tué si plus de mémoire est nécessaire. Cela n'empêche pas le service de s'exécuter. Pour cela, vous devez appeler stopSelf () ou des méthodes associées.

La transmission de la valeur true ou false indique si vous souhaitez supprimer la notification ou non.

val ACTION_STOP_SERVICE = "stop_service"
val NOTIFICATION_ID_SERVICE = 1
...  
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
    super.onStartCommand(intent, flags, startId)
    if (ACTION_STOP_SERVICE == intent.action) {
        stopForeground(true)
        stopSelf()
    } else {
        //Start your task

        //Send forground notification that a service will run in background.
        sendServiceNotification(this)
    }
    return Service.START_NOT_STICKY
}

Gérez votre tâche lorsque la destruction est appelée par stopSelf () .

override fun onDestroy() {
    super.onDestroy()
    //Stop whatever you started
}

Créez une notification pour maintenir le service en cours d'exécution au premier plan.

//This is from Util class so as not to cloud your service
fun sendServiceNotification(myService: Service) {
    val notificationTitle = "Service running"
    val notificationContent = "<My app> is using <service name> "
    val actionButtonText = "Stop"
    //Check android version and create channel for Android O and above
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        //You can do this on your own
        //createNotificationChannel(CHANNEL_ID_SERVICE)
    }
    //Build notification
    val notificationBuilder = NotificationCompat.Builder(applicationContext, CHANNEL_ID_SERVICE)
    notificationBuilder.setAutoCancel(true)
            .setDefaults(NotificationCompat.DEFAULT_ALL)
            .setWhen(System.currentTimeMillis())
            .setSmallIcon(R.drawable.ic_location)
            .setContentTitle(notificationTitle)
            .setContentText(notificationContent)
            .setVibrate(null)
    //Add stop button on notification
    val pStopSelf = createStopButtonIntent(myService)
    notificationBuilder.addAction(R.drawable.ic_location, actionButtonText, pStopSelf)
    //Build notification
    val notificationManagerCompact = NotificationManagerCompat.from(applicationContext)
    notificationManagerCompact.notify(NOTIFICATION_ID_SERVICE, notificationBuilder.build())
    val notification = notificationBuilder.build()
    //Start notification in foreground to let user know which service is running.
    myService.startForeground(NOTIFICATION_ID_SERVICE, notification)
    //Send notification
    notificationManagerCompact.notify(NOTIFICATION_ID_SERVICE, notification)
}

Donnez un bouton d'arrêt sur la notification pour arrêter le service lorsque l'utilisateur en a besoin.

/**
 * Function to create stop button intent to stop the service.
 */
private fun createStopButtonIntent(myService: Service): PendingIntent? {
    val stopSelf = Intent(applicationContext, MyService::class.java)
    stopSelf.action = ACTION_STOP_SERVICE
    return PendingIntent.getService(myService, 0,
            stopSelf, PendingIntent.FLAG_CANCEL_CURRENT)
}
Rana Ranvijay Singh
la source
1

Remarque: si votre application cible le niveau d'API 26 ou supérieur, le système impose des restrictions sur l'utilisation ou la création de services d'arrière-plan, sauf si l'application elle-même est au premier plan.

Si une application doit créer un service de premier plan, l'application doit appeler startForegroundService(). Cette méthode crée un service d'arrière-plan, mais la méthode signale au système que le service se promouvra au premier plan.

Une fois le service créé, le service doit appeler son startForeground() method within five seconds.

Andrii Kovalchuk
la source
1
J'espère que vous parlez de la question actuelle. Sinon, il n'y a pas de telle règle dans la communauté Stackoverflow
Farid
@RogerGusmao dans le code d'environnement prêt pour la production ne sauvegardera pas toujours votre projet. En outre - il y a beaucoup de bons exemples avec du code ci-dessous et au-dessus de ma réponse .. Mon projet a eu des problèmes lors de la publication, précisément parce que je ne connaissais pas la startForegroundServiceméthode
Andrii Kovalchuk
0

Dans mon cas, c'était totalement différent puisque je n'avais pas d'activité pour lancer le service à Oreo.

Voici les étapes que j'ai utilisées pour résoudre ce problème de service de premier plan -

public class SocketService extends Service {
    private String TAG = this.getClass().getSimpleName();

    @Override
    public void onCreate() {
        Log.d(TAG, "Inside onCreate() API");
        if (Build.VERSION.SDK_INT >= 26) {
            NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
            mBuilder.setSmallIcon(R.drawable.ic_launcher);
            mBuilder.setContentTitle("Notification Alert, Click Me!");
            mBuilder.setContentText("Hi, This is Android Notification Detail!");
            NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

            // notificationID allows you to update the notification later on.
            mNotificationManager.notify(100, mBuilder.build());
            startForeground(100, mBuilder.mNotification);
        }
        Toast.makeText(getApplicationContext(), "inside onCreate()", Toast.LENGTH_LONG).show();
    }


    @Override
    public int onStartCommand(Intent resultIntent, int resultCode, int startId) {
        Log.d(TAG, "inside onStartCommand() API");

        return startId;
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "inside onDestroy() API");

    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }
}

Et après cela, pour lancer ce service, j'ai déclenché ci-dessous cmd -


adb -s "+ serial_id +" shell am startforegroundservice -n com.test.socket.sample / .SocketService


Cela m'aide donc à démarrer le service sans activité sur les appareils Oreo :)

Arpana
la source
0

La solution @mikebertiean a presque fait l'affaire, mais j'ai eu ce problème avec une torsion supplémentaire - j'utilise le système Gingerbread et je ne voulais pas ajouter de package supplémentaire juste pour exécuter la notification. Enfin, j'ai trouvé: https://android.googlesource.com/platform/frameworks/support.git+/f9fd97499795cd47473f0344e00db9c9837eea36/v4/gingerbread/android/support/v4/app/NotificationCompatGingerbread.java

puis je rencontre un problème supplémentaire - la notification tue simplement mon application lorsqu'elle s'exécute (comment résoudre ce problème: Android: Comment éviter que cliquer sur une notification appelle onCreate () ), donc au total, mon code en service ressemble à ceci (C # / Xamarin):

Intent notificationIntent = new Intent(this, typeof(MainActivity));
// make the changes to manifest as well
notificationIntent.SetFlags(ActivityFlags.ClearTop | ActivityFlags.SingleTop);
PendingIntent pendingIntent = PendingIntent.GetActivity(this, 0, notificationIntent, 0);
Notification notification = new Notification(Resource.Drawable.Icon, "Starting service");
notification.SetLatestEventInfo(this, "MyApp", "Monitoring...", pendingIntent);
StartForeground(1337, notification);
homme vert
la source