Android: ¿implementar startForeground para un servicio?


124

Así que no estoy seguro de dónde / cómo implementar este método para que mi servicio se ejecute en primer plano. Actualmente comienzo mi servicio por lo siguiente en otra actividad:

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

Y luego en myServices 'onCreate () pruebo el startForeground () ...?

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

Así que sí, estoy un poco perdido e inseguro de cómo implementar esto.


Bueno, esto no funciona, al menos por lo que puedo decir, mi servicio todavía funciona como un servicio en segundo plano y es asesinado.
JDS

Respuestas:


131

Comenzaría completando completamente el Notification. Aquí hay un proyecto de muestra que demuestra el uso de startForeground().


8
¿Es posible usar startForeground () sin notificación? ¿O podemos actualizar más tarde la misma notificación?
CCI

2
¿Hay alguna razón en particular que usaste 1337?
Cody

33
@DoctorOreo: debe ser único dentro de la aplicación, aunque no necesariamente único en el dispositivo. Elegí 1337 porque, bueno, es 1337 . :-)
CommonsWare

La pregunta de @JRC es buena. ¿Es posible usar startForeground () sin notificación?
Snicolas

2
@Snicolas: Gracias por señalar una falla en Android. Trabajaré para arreglar esto.
CommonsWare

78

Desde su actividad principal, inicie el servicio con el siguiente código:

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

Luego, en su servicio onCreate(), crearía su notificación y la configuraría como primer plano de esta manera:

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 ¿cómo actualizar esta notificación desde MainActivity?
Roon13

1
@ Roon13 usando la ID, en este caso 1337 ... debería poder crear una nueva notificación y llamar a startForeground con la ID
mikebertiean


@mikebertiean ¿Cómo puedo llamar a startForeground desde MainActivity? Además, ¿cómo puedo borrar la notificación de MainActvity cuando finaliza el proceso?
Roon13

@mikebertiean Tengo que llamar a startForeground nuevamente en la clase de servicio, pero ¿cómo? ¿Tengo que llamar a startService () nuevamente?
Roon13

30

Este es mi código para configurar el servicio en primer plano:

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

}

Necesito crear una notificación usando PendingIntent, para poder comenzar mi actividad principal desde la notificación.

Para eliminar la notificación, simplemente llame a stopForeground (true);

Se llama en onStartCommand (). Consulte mi código en: https://github.com/bearstand/greyparrot/blob/master/src/com/xiong/richard/greyparrot/Mp3Recorder.java


Si elimina la notificación que llama a stopForeground (verdadero), cancelará el servicio
startforeground

66
¿De dónde llamas a este método?
Srujan Barai

77
Intent.FLAG_ACTIVITY_NEW_TASKno es válido en el contexto de PendingIntent.
mixel

30

Solución para Oreo 8.1

He encontrado algunos problemas como RemoteServiceException debido a una identificación de canal no válida con las versiones más recientes de Android. Así es como lo resolví:

Actividad :

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

Servicio de fondo:

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 DE 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
Puedes usar ContextCompat.startForegroundService(Context,Intent)en tu Actividad lo que hará lo correcto. ( developer.android.com/reference/android/support/v4/content/… )
Simon Featherstone

3
probablemente querrá usar en .setCategory(NotificationCompat.CATEGORY_SERVICE)lugar de Notification.CATEGORY_SERVICEsi su API mínima es <21
Someone Somewhere

66
Tenga en cuenta que la orientación de aplicaciones Build.VERSION_CODES.P(nivel de API 28) o posterior debe solicitar el permiso Manifest.permission.FOREGROUND_SERVICEpara usarla startForeground(); consulte developer.android.com/reference/android/app/…
Vadim Kotov el

21

Además de la respuesta RAWA , esta paz de código:

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

Puedes cambiar a:

ContextCompat.startForegroundService(context, yourIntent);

Si observa dentro de este método, puede ver que este método hace todo el trabajo de comprobación por usted.


9

Si desea que IntentService sea un servicio en primer plano

entonces deberías anular onHandleIntent()así

Override
protected void onHandleIntent(@Nullable Intent intent) {


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

   // Do something

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

}

¿Cómo hacer una notificación?

sencillo. Aquí está el getNotification()método

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

Comprensión más profunda

¿Qué sucede cuando un servicio se convierte en un servicio en primer plano?

Esto pasa

ingrese la descripción de la imagen aquí

¿Qué es un servicio en primer plano?

Un servicio en primer plano,

  • se asegura de que el usuario sea consciente de que algo está sucediendo en segundo plano al proporcionar la notificación.

  • (lo más importante) no es eliminado por el sistema cuando se queda sin memoria

Un caso de uso del servicio en primer plano

Implementación de la funcionalidad de descarga de canciones en una aplicación de música


5

Agregar el código dado Clase de servicio para "OS> = Build.VERSION_CODES.O" en 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;
}

Agregue este permiso en el archivo de manifiesto:

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

1

Manejar la intención en el inicio Comando de servicio mediante.

 stopForeground(true)

Esta llamada eliminará el servicio del estado de primer plano , lo que permitirá que se elimine si se necesita más memoria. Esto no detiene la ejecución del servicio . Para eso, debe llamar a stopSelf () o métodos relacionados.

El valor de paso verdadero o falso se indica si desea eliminar la notificación o no.

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
}

Manejar su tarea cuando se destruye se llama por stopSelf () .

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

Cree una notificación para mantener el servicio ejecutándose en primer plano.

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

Dé un botón de detención en la notificación para detener el servicio cuando el usuario lo necesite.

/**
 * 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: Si su aplicación se dirige al nivel 26 o superior de la API, el sistema impone restricciones sobre el uso o la creación de servicios en segundo plano a menos que la aplicación esté en primer plano.

Si una aplicación necesita crear un servicio en primer plano, la aplicación debe llamar startForegroundService(). Ese método crea un servicio en segundo plano, pero el método le indica al sistema que el servicio se promoverá a sí mismo en primer plano.

Una vez que el servicio ha sido creado, el servicio debe llamar a su startForeground() method within five seconds.


1
Espero que estés hablando de la pregunta actual. De lo contrario, no existe tal regla en la comunidad de Stackoverflow
Farid, el

@RogerGusmao en código de entorno listo para producción no siempre guardará su proyecto. Además - hay muchos grandes ejemplos con código de abajo y por encima de mi respuesta .. Mi proyecto tuvo problemas durante el lanzamiento exactamente porque yo no sabía nada de startForegroundServicemétodo
Andrii Kovalchuk

0

En mi caso, fue totalmente diferente ya que no estaba teniendo actividad para iniciar el servicio en Oreo.

A continuación se detallan los pasos que utilicé para resolver este problema de servicio en primer plano:

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

Y después de eso para iniciar este servicio, activé debajo de cmd:


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


Esto me ayuda a iniciar el servicio sin actividad en dispositivos Oreo :)


0

La solución @mikebertiean casi hizo el truco, pero tuve este problema con un giro adicional: uso el sistema Gingerbread y no quería agregar un paquete adicional solo para ejecutar la notificación. Finalmente encontré: https://android.googlesource.com/platform/frameworks/support.git+/f9fd97499795cd47473f0344e00db9c9837eea36/v4/gingerbread/android/support/v4/app/NotificationCompatGingerbread.java

luego llego a un problema adicional: la notificación simplemente mata mi aplicación cuando se ejecuta (cómo resolver este problema: Android: cómo evitar que al hacer clic en una notificación se llame a onCreate () ), en total mi código en servicio se ve así (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);
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.