studio programacion para móviles libro edición desarrollo desarrollar curso aprende aplicaciones android location android-8.0-oreo

para - manual de programacion android pdf



Android O: ¿El servicio de inicio de inicio antiguo aún funciona? (7)

Añado una muestra si hay alguna necesidad con Backstack Builder.

val notifyManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager val playIntent = Intent(this, this::class.java).setAction(PAUSE) val cancelIntent = Intent(this, this::class.java).setAction(EXIT) val stop = PendingIntent.getService(this, 1, playIntent, PendingIntent.FLAG_UPDATE_CURRENT) val exit = PendingIntent.getService(this, 2, cancelIntent, PendingIntent.FLAG_UPDATE_CURRENT) val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { notifyManager.createNotificationChannel(NotificationChannel(NOTIFICATION_ID_CHANNEL_ID, getString(R.string.app_name), NotificationManager.IMPORTANCE_HIGH)) NotificationCompat.Builder(this, NOTIFICATION_ID_CHANNEL_ID) } else NotificationCompat.Builder(this) builder.apply { setContentTitle(station.name) setContentText(metaToText(meta) ) setSmallIcon(R.drawable.ic_play_arrow_white_24px) setAutoCancel(false) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) priority = Notification.PRIORITY_MAX addAction(R.drawable.ic_stop_white_24px, getString(R.string.player_notification_stop), stop) addAction(R.drawable.ic_close_white_24px, getString(R.string.player_notification_exit), exit) } val stackBuilder = TaskStackBuilder.create(this) stackBuilder.addParentStack(PlayerActivity::class.java) stackBuilder.addNextIntent(Intent(this, PlayerActivity::class.java)) builder.setContentIntent(stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)) startForeground(NOTIFICATION_ID, builder.build())

Entonces, con Android O, debe tener su servicio funcionando como un servicio de primer plano si desea recibir más de unas pocas actualizaciones de ubicación por hora.

Me di cuenta de que el antiguo método para iniciar un servicio en primer plano parece funcionar en O. es decir,

startForeground(NOTIFICATION_ID, getNotification());

De acuerdo con la guía de cambios de comportamiento aquí: https://developer.android.com/preview/behavior-changes.html

El método NotificationManager.startServiceInForeground () inicia un servicio de primer plano. La forma antigua de iniciar un servicio de primer plano ya no funciona.

Aunque el nuevo método solo funciona cuando se dirige a O, parece que el método antiguo todavía parece funcionar en un dispositivo O ya sea que esté apuntando a O o no.

Editar Incluyendo ejemplo:

El proyecto de ejemplo de Google LocationUpdatesForegroundService en realidad tiene un ejemplo funcional en el que puede ver el problema de primera mano. https://github.com/googlesamples/android-play-location/tree/master/LocationUpdatesForegroundService

El método startForeground parece funcionar sin problemas, ya sea la orientación y compilación contra el nivel 25 de API O la segmentación y compilación contra O (como se indica aquí: https://developer.android.com/preview/migration.html#uya )

Entonces, para reproducir:

  1. Configure la aplicación gradle como se menciona en el enlace anterior
  2. Abre la aplicación
  3. Solicitar actualizaciones de ubicación
  4. Cerrar la aplicación (ya sea mediante el botón Atrás o el botón de inicio)

El servicio se está ejecutando en primer plano (se muestra con un icono en la sombra de notificación). Las actualizaciones de ubicación se están realizando según lo esperado (cada 10 segundos) incluso en un dispositivo que ejecuta O. ¿Qué me estoy perdiendo aquí?


Como también se mencionó @Kislingk en un comentario, NotificationManager.startServiceInForeground se eliminó. Se marcó como obsoleto con el commit 08992ac .

Desde el mensaje de confirmación:

En lugar de requerir que se proporcione una Notificación a priori para iniciar un servicio directamente en el estado de primer plano, adoptamos una operación compuesta de dos etapas para llevar a cabo un trabajo de servicio continuo incluso desde un estado de ejecución en segundo plano. El contexto # startForegroundService () no está sujeto a restricciones de fondo, con el requisito de que el servicio ingrese formalmente al estado de primer plano a través de startForeground () dentro de 5 segundos. Si el servicio no lo hace, es detenido por el sistema operativo y la aplicación es culpada por un servicio ANR.


En la Actividad (o en cualquier contexto que inicie el servicio en primer plano), llame a esto:

ContextCompat.startForegroundService(context, serviceClass);

Cuando se haya iniciado el servicio, cree un canal de notificación con un código similar al que dicen los documentos de Android , y luego cree una compilación y úsela:

final Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID).setSmallIcon(...)// .setPriority(...).setCategory(...).setContentTitle(...).setContentText(...).setTicker(...); // and maybe other preparations to the notification... startForeground(notificationId, builder.build());


Esto funcionó para mí.

  1. En la clase de actividad, inicie el servicio utilizando startForegroundService () en lugar de startService ()

Intent myService = new Intent(this, MyService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(myService); } else { startService(myService); }

  1. Ahora en la clase de servicio en onStartCommand () haga lo siguiente

@Override public int onStartCommand(Intent intent, int flags, int startId) { ...... if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID) .setContentTitle(getString(R.string.app_name)) .setContentText(text) .setAutoCancel(true); Notification notification = builder.build(); startForeground(1, notification); } else { NotificationCompat.Builder builder = new NotificationCompat.Builder(this) .setContentTitle(getString(R.string.app_name)) .setContentText(text) .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setAutoCancel(true); Notification notification = builder.build(); startForeground(1, notification); } return START_NOT_STICKY; }

Nota: el uso de Notification.Builder en lugar de NotificationCompat.Builder lo hizo funcionar. Solo en Notification.Builder deberá proporcionar el ID de canal, que es una característica nueva en Android Oreo.

¡Espero que funcione!


La forma heredada de iniciar un servicio de primer plano aún funciona cuando la aplicación está en primer plano, pero la forma recomendada de iniciar un servicio de primer plano para las aplicaciones dirigidas a la API de nivel 26 / Android O es utilizar el nuevo método NotificationManager # startServiceInForeground para crear un servicio de primer plano en primer lugar.

La antigua forma de iniciar el servicio en segundo plano y luego promocionarlo a primer plano no funcionará si la aplicación está en modo de fondo, debido a las limitaciones de ejecución en segundo plano de Android O.

El proceso de migración y los pasos se documentan aquí. https://developer.android.com/preview/features/background.html#migration


Por lo general, usted comienza su servicio desde un receptor de difusión utilizando startService . Están diciendo que ya no es posible (ni confiable) llamar a startService porque ahora hay limitaciones de fondo, por lo que debe llamar a startServiceInForeground lugar. Sin embargo, de los documentos no está muy claro cuándo sucede porque la aplicación está en la lista blanca cuando recibe un intento de transmisión, por lo que no está claro exactamente cuándo startService lanza la IllegalStateException .


startForeground (1, notificación); funcionará para Android O, pero según el requisito de Android O, tenemos que mostrar una notificación persistente al usuario. Al mismo tiempo, puede confundir al usuario en algunos casos (notificación del sistema sobre la aplicación que se ejecuta en segundo plano y la batería que afecta) y, por lo tanto, el usuario puede desinstalarla. Por lo tanto, lo mejor sería utilizar la clase WorkManager recién introducida para programar la tarea como primer plano.

  1. Cree su clase de trabajador (por ejemplo, MyWorker) extendiendo la clase "Trabajador" donde puede realizar la tarea de larga duración. Reemplace el método de abajo en esta clase:
    • DoWork () [Requerido]
    • onStopped () [Opcional]
    • onWorkFinished [Opcional] etc.
  2. Cree trabajos repetitivos / periódicos [PeriodicWorkRequest] o no repetitivos [OneTimeWorkRequest] según el requisito.
  3. Obtenga la instancia de WorkManager y ponga en cola el trabajo.

Fragmento de código:

OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(MyWorker.class) .build(); WorkManager.getInstance().enqueue(work);