programacion - Android 8.0: java.lang.IllegalStateException: no se permite iniciar la intención del servicio
manual android studio avanzado (16)
Al iniciar la aplicación, la aplicación inicia el servicio que debería realizar alguna tarea de red. Después de apuntar al nivel 26 de API, mi aplicación no puede iniciar el servicio en Android 8.0 en segundo plano.
Causado por: java.lang.IllegalStateException: No se permite iniciar el servicio Intención {cmp = my.app.tt / com.my.service}: la aplicación está en segundo plano uidRecord {90372b1 u0a136 CEM inact procs: 1 seq (0,0 , 0)}
según tengo entendido relacionado con: Límites de ejecución en segundo plano
El método startService () ahora arroja una IllegalStateException si una aplicación dirigida a Android 8.0 intenta usar ese método en una situación en la que no está permitido crear servicios en segundo plano.
" en una situación en la que no está permitido ", ¿qué significa realmente? Y como arreglarlo. No quiero establecer mi servicio como "primer plano"
En Oreo, Android define límites a los servicios en segundo plano .
Para mejorar la experiencia del usuario, Android 8.0 (API nivel 26) impone limitaciones sobre lo que las aplicaciones pueden hacer mientras se ejecutan en segundo plano.
Aún así, si necesita un servicio siempre en ejecución, puede utilizar el servicio en primer plano.
Limitaciones del servicio en segundo plano: mientras una aplicación está inactiva, existen límites para el uso de los servicios en segundo plano. Esto no se aplica a los servicios en primer plano, que son más notorios para el usuario.
Para que pueda hacer un servicio en primer plano . Deberá mostrar una notificación al usuario cuando su servicio se esté ejecutando. Ver esta respuesta (Hay muchas otras)
Una solución si -
no quieres una notificación por tu servicio?
Puede realizar tareas periódicas, 1. inicia su servicio, 2. el servicio hará su trabajo y 3. se detendrá. De este modo, su aplicación no se considerará agotada.
Puede usar tareas periódicas con Alarm Manager , Job Scheduler , Evernote-Jobs o developer.android.com/topic/libraries/architecture/workmanager .
- Work manager es la mejor solución para tareas periódicas. Que se introdujo con el componente de arquitectura de Android .
- A diferencia de Job-Scheduler (solo> 21 API) funcionará para todas las versiones.
- También comienza a funcionar después de un modo Doze-Standby .
- Haga un receptor de arranque de Android para programar el servicio después del arranque del dispositivo.
He probado el servicio en ejecución permanente con Work-Manager.
Como @kosev dijo en su respuesta , puede usar JobIntentService. Pero uso una solución alternativa: capto IllegalStateException e inicio el servicio como primer plano. Por ejemplo, esta función inicia mi servicio:
public class CompressWorker extends Worker {
public CompressWorker(
@NonNull Context context,
@NonNull WorkerParameters params) {
super(context, params);
}
@Override
public Worker.Result doWork() {
// Do the work here--in this case, compress the stored images.
// In this example no parameters are passed; the task is
// assumed to be "compress the whole library."
myCompress();
// Indicate success or failure with your return value:
return Result.SUCCESS;
// (Returning RETRY tells WorkManager to try this task again
// later; FAILURE says not to try again.)
}
}
y cuando proceso Intent, hago tal cosa:
OneTimeWorkRequest compressionWork =
new OneTimeWorkRequest.Builder(CompressWorker.class)
.build();
WorkManager.getInstance().enqueue(compressionWork);
Creo que la mejor manera de evitar esto es verificar la versión de compilación en tiempo de ejecución y, dependiendo de eso, si es inferior al nivel 21 de Api, continúe usando startService (). pero si es más alto, debe usar un programador de trabajos. Supongo que también puedes usar Work Manager, pero todavía está en fase beta
La mejor manera es usar JobIntentService que usa el nuevo JobScheduler para Oreo o los servicios antiguos si no están disponibles.
Declara en tu manifiesto:
<service android:name=".YourService"
android:permission="android.permission.BIND_JOB_SERVICE"/>
Y en su servicio debe reemplazar onHandleIntent por onHandleWork:
public class YourService extends JobIntentService {
public static final int JOB_ID = 1;
public static void enqueueWork(Context context, Intent work) {
enqueueWork(context, YourService.class, JOB_ID, work);
}
@Override
protected void onHandleWork(@NonNull Intent intent) {
// your code
}
}
Entonces comienzas tu servicio con:
YourService.enqueueWork(context, new Intent());
Las otras respuestas son correctas, pero me gustaría señalar que otra forma de evitar esto es pedirle al usuario que desactive las optimizaciones de batería para su aplicación (esto generalmente no es una buena idea a menos que su aplicación esté relacionada con el sistema). Consulte esta respuesta para saber cómo solicitar la exclusión de las optimizaciones de batería sin que su aplicación sea bloqueada en Google Play.
También debe verificar si las optimizaciones de la batería están desactivadas en su receptor para evitar fallas a través de:
if (Build.VERSION.SDK_INT < 26 || getSystemService<PowerManager>()
?.isIgnoringBatteryOptimizations(packageName) != false) {
startService(Intent(context, MyService::class.java))
} // else calling startService will result in crash
Las situaciones permitidas son una lista blanca temporal donde el servicio en segundo plano se comporta igual que antes de Android O.
En determinadas circunstancias, una aplicación en segundo plano se coloca en una lista blanca temporal durante varios minutos. Mientras una aplicación está en la lista blanca, puede iniciar servicios sin limitación, y sus servicios en segundo plano pueden ejecutarse. Una aplicación se coloca en la lista blanca cuando maneja una tarea que es visible para el usuario, como:
- Manejo de un mensaje de alta prioridad de Firebase Cloud Messaging (FCM).
- Recibir una transmisión, como un mensaje SMS / MMS.
- Ejecutar un PendingIntent desde una notificación.
- Iniciar un VpnService antes de que la aplicación VPN pase a primer plano.
Fuente: https://developer.android.com/about/versions/oreo/background.html
En otras palabras, si su servicio en segundo plano no cumple con los requisitos de la lista blanca, debe usar el nuevo JobScheduler . Básicamente es lo mismo que un servicio en segundo plano, pero se llama periódicamente en lugar de ejecutarse en segundo plano continuamente.
Si está utilizando un IntentService, puede cambiar a un JobIntentService. Vea la respuesta de @ kosev a continuación .
Sí, eso es porque ya no puede iniciar servicios en segundo plano en API 26. Por lo tanto, puede iniciar ForegroundService por encima de API 26.
Tendrás que usar
ContextCompat.startForegroundService(...)
y publicar una notificación mientras procesa la fuga.
Según las notas de la versión de Firebase , afirman que el soporte para Android O se lanzó por primera vez en 10.2.1 (aunque recomendaría usar la versión más reciente).
agregue nuevas dependencias de mensajería firebase para Android O
compile ''com.google.firebase:firebase-messaging:11.6.2''
Actualice los servicios de Google Play y los repositorios de Google si es necesario.
Si alguna intención funcionaba bien antes cuando la aplicación está en segundo plano, ya no será el caso de Android 8 y superior. Solo se refiere a la intención que tiene que hacer algún procesamiento cuando la aplicación está en segundo plano.
Deben seguirse los siguientes pasos:
-
La intención mencionada anteriormente debe usar
JobIntentService
lugar deIntentService
. -
La clase que extiende
JobIntentService
debe implementar elonHandleWork(@NonNull Intent intent)
-onHandleWork(@NonNull Intent intent)
y debe tener debajo el método, que invocará el métodoonHandleWork
:public static void enqueueWork(Context context, Intent work) { enqueueWork(context, xyz.class, 123, work); }
-
Llame a
enqueueWork(Context, intent)
desde la clase donde se define su intento.Código de muestra:
Public class A { ... ... Intent intent = new Intent(Context, B.class); //startService(intent); B.enqueueWork(Context, intent); }
La siguiente clase estaba extendiendo previamente la clase de Servicio
Public Class B extends JobIntentService{
...
public static void enqueueWork(Context context, Intent work) {
enqueueWork(context, B.class, JobId, work);
}
protected void onHandleWork(@NonNull Intent intent) {
...
...
}
}
-
com.android.support:support-compat
es necesario paraJobIntentService
: uso26.1.0 V
-
Lo más importante es asegurarse de que la versión de las bibliotecas de Firebase esté en al menos
10.2.1
. ¡Tuve problemas con10.2.0
, si tiene alguno! -
Su manifiesto debe tener el siguiente permiso para la clase de Servicio:
service android:name=".B" android:exported="false" android:permission="android.permission.BIND_JOB_SERVICE"
Espero que esto ayude.
Si el servicio se ejecuta en un subproceso en segundo plano al extender
IntentService
, puede reemplazar
IntentService
con
JobIntentService
que se proporciona como parte de la Biblioteca de soporte de Android
La ventaja de usar
JobIntentService
es que se comporta como un
IntentService
en dispositivos pre-O y en O y superiores, lo envía como un trabajo
JobScheduler
también se puede utilizar para trabajos periódicos / bajo demanda.
Pero asegúrese de manejar la compatibilidad con versiones anteriores, ya que la API
JobScheduler
solo está disponible desde la API 21
Si está ejecutando su código en 8.0, la aplicación se bloqueará. Entonces comience el servicio en primer plano. Si debajo de 8.0 usa esto:
Intent serviceIntent = new Intent(context, RingtonePlayingService.class);
context.startService(serviceIntent);
Si es anterior o 8.0, use esto:
Intent serviceIntent = new Intent(context, RingtonePlayingService.class);
ContextCompat.startForegroundService(context, serviceIntent );
Tengo solución
Para dispositivos anteriores a 8.0, solo debe usar
startService()
, pero para dispositivos posteriores a 7.0, debe usar
startForgroundService()
.
Aquí hay una muestra de código para iniciar el servicio.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(new Intent(context, ServedService.class));
} else {
context.startService(new Intent(context, ServedService.class));
}
Y en la clase de servicio, agregue el siguiente código para la notificación:
@Override
public void onCreate() {
super.onCreate();
startForeground(1,new Notification());
}
Donde O es la versión 26 de Android.
Use
startForegroundService()
lugar de
startService()
y no olvide crear
startForeground(1,new Notification());
en su servicio dentro de los 5 segundos de comenzar el servicio.
Veo muchas respuestas que recomiendan simplemente usar un ForegroundService. Para usar un Servicio Foreground, debe haber una notificación asociada a él. Los usuarios verán esta notificación. Dependiendo de la situación, pueden molestarse con su aplicación y desinstalarla.
La solución más fácil es usar el nuevo Componente de Arquitectura llamado WorkManager. Puede consultar la documentación aquí: https://developer.android.com/topic/libraries/architecture/workmanager/
Simplemente define su clase de trabajador que extiende a Trabajador.
@JvmStatic
protected fun startService(intentAction: String, serviceType: Class<*>, intentExtraSetup: (Intent) -> Unit) {
val context = App.context
val intent = Intent(context, serviceType)
intent.action = intentAction
intentExtraSetup(intent)
intent.putExtra(NEED_FOREGROUND_KEY, false)
try {
context.startService(intent)
}
catch (ex: IllegalStateException) {
intent.putExtra(NEED_FOREGROUND_KEY, true)
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent)
}
else {
context.startService(intent)
}
}
}
Luego, programa cuándo quieres ejecutarlo.
override fun onHandleIntent(intent: Intent?) {
val needToMoveToForeground = intent?.getBooleanExtra(NEED_FOREGROUND_KEY, false) ?: false
if(needToMoveToForeground) {
val notification = notificationService.createSyncServiceNotification()
startForeground(notification.second, notification.first)
isInForeground = true
}
intent?.let {
getTask(it)?.process()
}
}
¡Fácil! Hay muchas formas de configurar trabajadores. Admite trabajos recurrentes e incluso puede hacer cosas complejas como encadenar si lo necesita. Espero que esto ayude.
no use en onStartCommand:
return START_NOT_STICKY
solo cámbielo a:
return START_STICKY
y va a funcionar
si ha integrado la notificación push de mensajería firebase entonces,
Agregue nuevas dependencias de mensajería firebase para Android O (Android 8.0), debido a los https://developer.android.com/about/versions/oreo/background.html .
compile ''com.google.firebase:firebase-messaging:11.4.0''
Actualice los servicios de Google Play y los repositorios de Google si es necesario.
Actualizar:
compile ''com.google.firebase:firebase-messaging:11.4.2''