Android: implementazione di startForeground per un servizio?


124

Quindi non sono sicuro di dove / come implementare questo metodo per far funzionare il mio servizio in primo piano. Attualmente inizio il mio servizio come segue in un'altra attività:

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

E poi in onCreate () di myServices provo startForeground () ...?

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

Quindi sì, sono un po 'perso e incerto su come implementarlo.


Bene, questo non funziona, almeno per quanto posso dire il mio servizio funziona ancora come servizio in background e viene ucciso.
JDS

Risposte:


131

Inizierei compilando completamente il file Notification. Di seguito è riportato un progetto di esempio che dimostra l'uso di startForeground().


8
È possibile utilizzare startForeground () senza notifica? O possiamo aggiornare successivamente la stessa notifica?
JRC

2
C'è un motivo particolare che hai usato 1337?
Cody

33
@DoctorOreo: deve essere univoco all'interno dell'app, anche se non necessariamente univoco sul dispositivo. Ho scelto il 1337 perché, beh, è ​​il 1337 . :-)
CommonsWare

La domanda di @JRC è buona. È possibile utilizzare startForeground () senza notifica?
Snicolas

2
@Snicolas: grazie per aver segnalato un difetto in Android. Lavorerò per risolverlo.
CommonsWare

78

Dalla tua attività principale, avvia il servizio con il seguente codice:

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

Quindi nel tuo servizio per onCreate()te dovresti creare la tua notifica e impostarla come primo piano in questo modo:

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

@mike come aggiornare questa notifica da MainActivity?
Roon13

1
@ Roon13 usando l'ID, in questo caso 1337 ... dovresti essere in grado di creare una nuova notifica e chiamare startForeground con l'ID
mikebertiean

@ Roon13 controllare questa domanda stackoverflow.com/questions/5528288/...
mikebertiean

@mikebertiean Come posso chiamare startForeground da MainActivity? inoltre come posso cancellare la notifica da MainActvity quando il processo è finito?
Roon13

@mikebertiean ho capito che devo chiamare di nuovo startForeground nella classe Service, ma come? Devo chiamare di nuovo startService ()?
Roon13

30

Questo è il mio codice per impostare il servizio in primo piano:

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

}

Devo creare una notifica utilizzando PendingIntent, in modo da poter iniziare la mia attività principale dalla notifica.

Per rimuovere la notifica, chiama semplicemente stopForeground (true);

Viene chiamato in onStartCommand (). Fare riferimento al mio codice su: https://github.com/bearstand/greyparrot/blob/master/src/com/xiong/richard/greyparrot/Mp3Recorder.java


Se rimuovi la notifica chiamando stopForeground (true) stai annullando il servizio
startforeground

6
Da dove chiami questo metodo?
Srujan Barai

7
Intent.FLAG_ACTIVITY_NEW_TASKnon è valido nel contesto di PendingIntent.
mixel

30

Soluzione per Oreo 8.1

Ho riscontrato alcuni problemi come RemoteServiceException a causa di un ID canale non valido con le versioni più recenti di Android. Ecco come l'ho risolto:

Attività :

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

BackgroundService:

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
}

EQUIVALENTE 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;
    }
}

8
Puoi usare ContextCompat.startForegroundService(Context,Intent)nella tua attività che farà la cosa giusta. ( developer.android.com/reference/android/support/v4/content/… )
Simon Featherstone

3
probabilmente vorrai usare al .setCategory(NotificationCompat.CATEGORY_SERVICE)posto di Notification.CATEGORY_SERVICEse la tua API minima è <21
Someone Somewhere,

6
Tieni presente che il targeting per app Build.VERSION_CODES.P(livello API 28) o successivo deve richiedere l'autorizzazione Manifest.permission.FOREGROUND_SERVICEper poter utilizzare startForeground()- vedi developer.android.com/reference/android/app/…
Vadim Kotov

21

Oltre alla risposta RAWA , questa pace di codice:

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

Puoi passare a:

ContextCompat.startForegroundService(context, yourIntent);

Se guarderai all'interno di questo metodo, puoi vedere che questo metodo fa tutto il lavoro di controllo per te.


9

Se vuoi rendere IntentService un servizio in primo piano

allora dovresti sovrascrivere in onHandleIntent()questo modo

Override
protected void onHandleIntent(@Nullable Intent intent) {


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

   // Do something

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

}

Come fare la notifica?

semplice. Ecco il getNotification()metodo

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

Comprensione più profonda

Cosa succede quando un servizio diventa un servizio in primo piano

Questo succede

inserisci qui la descrizione dell'immagine

Cos'è un servizio in primo piano?

Un servizio in primo piano,

  • si assicura che l'utente sia attivamente consapevole che qualcosa sta accadendo in background fornendo la notifica.

  • (cosa più importante) non viene ucciso dal sistema quando la memoria è insufficiente

Un caso d'uso del servizio in primo piano

Implementazione della funzionalità di download di brani in un'app musicale


5

Aggiungi la classe di servizio del codice fornita per "OS> = Build.VERSION_CODES.O" in 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;
}

Aggiungi questa autorizzazione nel file manifest:

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

1

Gestire l'intento su startCommand del servizio utilizzando.

 stopForeground(true)

Questa chiamata rimuoverà il servizio dallo stato in primo piano , consentendo di eliminarlo se è necessaria più memoria. Ciò non interrompe l' esecuzione del servizio . Per questo, devi chiamare stopSelf () o metodi correlati.

Passaggio di valore vero o falso indicato se si desidera rimuovere o meno la notifica.

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
}

Gestisci il tuo compito quando on destroy viene chiamato da stopSelf () .

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

Crea una notifica per mantenere il servizio in esecuzione in primo piano.

//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)
}

Fornire un pulsante di interruzione alla notifica per interrompere il servizio quando l'utente ha bisogno.

/**
 * 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)
}

1

Nota: se la tua app è destinata al livello API 26 o superiore, il sistema impone restrizioni sull'utilizzo o sulla creazione di servizi in background a meno che l'app stessa non sia in primo piano.

Se un'app deve creare un servizio in primo piano, l'app deve chiamare startForegroundService(). Questo metodo crea un servizio in background, ma il metodo segnala al sistema che il servizio si promuoverà in primo piano.

Una volta che il servizio è stato creato, il servizio deve chiamare il suo startForeground() method within five seconds.


1
Spero tu stia parlando della domanda attuale. Altrimenti, non esiste una regola del genere nella comunità Stackoverflow
Farid

@RogerGusmao nel codice dell'ambiente pronto per la produzione non salverà sempre il tuo progetto. Inoltre - ci sono molti ottimi esempi con il codice sotto e sopra la mia risposta .. Il mio progetto ha avuto problemi durante il rilascio proprio perché non conoscevo il startForegroundServicemetodo
Andrii Kovalchuk

0

Nel mio caso era completamente diverso poiché non avevo attività per lanciare il servizio in Oreo.

Di seguito sono riportati i passaggi che ho utilizzato per risolvere questo problema di servizio in primo piano:

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

E dopo di che per avviare questo servizio ho attivato sotto cmd -


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


Quindi questo mi aiuta ad avviare il servizio senza attività sui dispositivi Oreo :)


0

La soluzione @mikebertiean ha quasi funzionato, ma ho avuto questo problema con una svolta aggiuntiva: utilizzo il sistema Gingerbread e non volevo aggiungere alcun pacchetto extra solo per eseguire la notifica. Finalmente ho trovato: https://android.googlesource.com/platform/frameworks/support.git+/f9fd97499795cd47473f0344e00db9c9837eea36/v4/gingerbread/android/support/v4/app/NotificationCompatGingerbread.java

quindi ho riscontrato un ulteriore problema: la notifica uccide semplicemente la mia app quando viene eseguita (come risolvere questo problema: Android: come evitare che facendo clic su una notifica chiami onCreate () ), quindi in totale il mio codice in servizio è simile a questo (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);
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.