¿Cómo verificar si un servicio se está ejecutando en Android?

¿Cómo puedo verificar si se está ejecutando un servicio en segundo plano (en Android)?

Quiero una actividad de Android que conmute el estado del servicio; me permite activarlo si está apagado y apagado si está activado.

Tuve el mismo problema no hace mucho tiempo. Como mi servicio era local, terminé simplemente usando un campo estático en la clase de servicio para alternar el estado, tal como lo describe hackbod aquí.

EDITAR (para el registro):

Aquí está la solución propuesta por hackbod:

Si su código de cliente y servidor es parte de la misma .apk y está vinculando al servicio con un Intento concreto (uno que especifique la clase de servicio exacta), entonces simplemente puede hacer que su servicio establezca una variable global cuando se está ejecutando. tu cliente puede verificar

Deliberadamente no tenemos una API para verificar si un servicio se está ejecutando porque, casi sin falta, cuando quieres hacer algo así, terminas con las condiciones de carrera en tu código.

Uso lo siguiente desde dentro de una actividad:

 private boolean isMyServiceRunning(Class serviceClass) { ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { if (serviceClass.getName().equals(service.service.getClassName())) { return true; } } return false; } 

Y lo llamo usando:

 isMyServiceRunning(MyService.class) 

Esto funciona de manera confiable, ya que se basa en la información sobre la ejecución de servicios proporcionados por el sistema operativo Android a través de ActivityManager # getRunningServices .

Todos los enfoques que usan eventos onDestroy o onSometing o Binders o variables estáticas no funcionarán de manera confiable porque como desarrollador nunca se sabe, cuando Android decide matar su proceso o cuáles de las devoluciones de llamada mencionadas son llamadas o no. Tenga en cuenta la columna “Killable” en la tabla de eventos del ciclo de vida en la documentación de Android.

¡Lo tengo!

DEBE llamar a startService() para que su servicio esté debidamente registrado y pasar BIND_AUTO_CREATE no será suficiente.

 Intent bindIntent = new Intent(this,ServiceTask.class); startService(bindIntent); bindService(bindIntent,mConnection,0); 

Y ahora la clase ServiceTools:

 public class ServiceTools { private static String LOG_TAG = ServiceTools.class.getName(); public static boolean isServiceRunning(String serviceClassName){ final ActivityManager activityManager = (ActivityManager)Application.getContext().getSystemService(Context.ACTIVITY_SERVICE); final List services = activityManager.getRunningServices(Integer.MAX_VALUE); for (RunningServiceInfo runningServiceInfo : services) { if (runningServiceInfo.service.getClassName().equals(serviceClassName)){ return true; } } return false; } } 

Un pequeño complemento es:

Mi objective es saber si un servicio se está ejecutando sin ejecutarlo realmente si no se está ejecutando.

Llamar a bindService o llamar a un bash que el servicio puede capturar no es una buena idea, ya que iniciará el servicio si no se está ejecutando.

Por lo tanto, como miracle2k sugirió, lo mejor es tener un campo estático en la clase de servicio para saber si el servicio se ha iniciado o no.

Para hacerlo aún más limpio, sugiero transformar el servicio en un singleton con una búsqueda muy vaga: es decir, no hay instanciación de toda la instancia de singleton a través de métodos estáticos. El método estático getInstance de su servicio / singleton solo devuelve la instancia del singleton si se ha creado. Pero en realidad no inicia ni instancia el singleton en sí mismo. El servicio solo se inicia a través de los métodos normales de inicio del servicio.

Sería aún más limpio modificar el patrón de diseño singleton para cambiar el nombre del confuso método getInstance en algo así como el isInstanceCreated() : boolean .

El código se verá así:

 public class MyService extends Service { private static MyService instance = null; public static boolean isInstanceCreated() { return instance != null; }//met @Override public void onCreate() { instance = this; .... }//met @Override public void onDestroy() { instance = null; ... }//met }//class 

Esta solución es elegante, pero solo es relevante si tiene acceso a la clase de servicio y solo para las clases en la aplicación / paquete del servicio. Si sus clases están fuera de la aplicación / paquete de servicio, puede consultar el ActivityManager con limitaciones subrayadas por Pieter-Jan Van Robays.

Puede usar esto (no intenté esto todavía, pero espero que esto funcione):

 if(startService(someIntent) != null) { Toast.makeText(getBaseContext(), "Service is already running", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(getBaseContext(), "There is no service running, starting service..", Toast.LENGTH_SHORT).show(); } 

El método startService devuelve un objeto ComponentName si ya hay un servicio en ejecución. Si no, se devolverá nulo.

Ver resumen público ComponentName startService (servicio de intención) .

Esto no es como comprobar, creo, porque está comenzando el servicio, por lo que puede agregar stopService(someIntent); bajo el código

  public boolean checkServiceRunning(){ ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE); for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { if ("com.example.yourpackagename.YourServiceName" .equals(service.service.getClassName())) { return true; } } return false; } 

He modificado ligeramente una de las soluciones presentadas anteriormente, pero aprobé la clase en lugar de un nombre de cadena genérico, para estar seguro de comparar las cadenas que salen del mismo método class.getName()

 public class ServiceTools { private static String LOG_TAG = ServiceTools.class.getName(); public static boolean isServiceRunning(Context context,Class serviceClass){ final ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE); final List services = activityManager.getRunningServices(Integer.MAX_VALUE); for (RunningServiceInfo runningServiceInfo : services) { Log.d(Constants.TAG, String.format("Service:%s", runningServiceInfo.service.getClassName())); if (runningServiceInfo.service.getClassName().equals(serviceClass.getName())){ return true; } } return false; } } 

y entonces

 Boolean isServiceRunning = ServiceTools.isServiceRunning( MainActivity.this.getApplicationContext(), BackgroundIntentService.class); 

Solo quiero agregar una nota a la respuesta de @Snicolas. Los siguientes pasos se pueden usar para verificar el servicio de detención con / sin llamar a onDestroy() .

  1. onDestroy() llamado: vaya a Configuración -> Aplicación -> Servicios en ejecución -> Seleccione y detenga su servicio.

  2. onDestroy() no llamado: vaya a Configuración -> Aplicación -> Administrar aplicaciones -> Seleccionar y “Forzar detención” de su aplicación en la que se está ejecutando su servicio. Sin embargo, como su aplicación se detiene aquí, definitivamente las instancias del servicio también se detendrán.

Finalmente, me gustaría mencionar que el enfoque mencionado allí usando una variable estática en la clase de singleton me funciona.

onDestroy no siempre se llama en el servicio, así que esto es inútil!

Por ejemplo: simplemente ejecute la aplicación nuevamente con un cambio de Eclipse. La aplicación se cierra con fuerza usando SIG: 9.

Este es un extracto de los documentos de Android

Las transmisiones ordenadas (enviadas con Context.sendOrderedBroadcast ) se envían a un receptor a la vez. Como cada receptor se ejecuta sucesivamente, puede propagar un resultado al próximo receptor, o puede abortar por completo la transmisión para que no se transmita a otros receptores.

Piense en este hack como “pinging” del Service ya que podemos transmitir sincrónicamente podemos transmitir y obtener un resultado de forma sincrónica en el hilo de la interfaz de usuario.

Service

BroadcastReceiver .

 @Override public void onCreate() { LocalBroadcastManager .getInstance(this) .registerReceiver(new ServiceEchoReceiver(), IntentFilter("echo"); } private class ServiceEchoReceiver{ public void onReceive (Context context, Intent intent) { LocalBroadcastManager .getInstance(this) .sendBroadcastSync(new Intent("echo")); } } 

Activity

  bool serviceRunning = false; protected void onCreate (Bundle savedInstanceState){ LocalBroadcastManager.getInstance(this).registerReceiver(echo); LocalBroadcastManager.getInstance(this).sendBroadcastSync(new Intent("echo")); if(!serviceRunning){ //try and run the service } } private BroadcastReceiver echo = new BroadcastReceiver(){ public void onReceive (Context context, Intent intent) { serviceRunning = true; } } 

En primer lugar, no debes intentar acceder al servicio mediante el ActivityManager. (Discutido aquí )

Los servicios pueden ejecutarse por su cuenta, estar vinculados a una Actividad o ambos. La forma de verificar una actividad si su servicio se está ejecutando o no es mediante la creación de una interfaz (que amplía Binder) donde se declaran los métodos que tanto la actividad como el servicio entienden. Puede hacer esto creando su propia interfaz donde declare, por ejemplo, “isServiceRunning ()”. A continuación, puede vincular su Actividad a su Servicio, ejecutar el método isServiceRunning (), el Servicio comprobará por sí mismo si se está ejecutando o no y devolverá un valor booleano a su Actividad.

También puede usar este método para detener su Servicio o interactuar con él de otra manera.

Utilicé este tutorial para aprender a implementar este escenario en mi aplicación.

La forma correcta de verificar si un servicio se está ejecutando es simplemente preguntarlo. Implemente un BroadcastReceiver en su servicio que responda a los pings de sus actividades. Registre BroadcastReceiver cuando se inicie el servicio y anule el registro cuando se destruya el servicio. Desde su actividad (o cualquier componente), envíe un bash de transmisión local al servicio y, si responde, sabe que se está ejecutando. Tenga en cuenta la sutil diferencia entre ACTION_PING y ACTION_PONG en el siguiente código.

 public class PingableService extends Service { public static final String ACTION_PING = PingableService.class.getName() + ".PING"; public static final String ACTION_PONG = PingableService.class.getName() + ".PONG"; public int onStartCommand (Intent intent, int flags, int startId) { LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, new IntentFilter(ACTION_PING)); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy () { LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver); super.onDestroy(); } private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive (Context context, Intent intent) { if (intent.getAction().equals(ACTION_PING)) { LocalBroadcastManager manager = LocalBroadcastManager.getInstance(getApplicationContext()); manager.sendBroadcast(new Intent(ACTION_PONG)); } } }; } public class MyActivity extends Activity { private boolean isSvcRunning = false; @Override protected void onStart() { LocalBroadcastManager manager = LocalBroadcastManager.getInstance(getApplicationContext()); manager.registerReceiver(mReceiver, new IntentFilter(PingableService.ACTION_PONG)); // the service will respond to this broadcast only if it's running manager.sendBroadcast(new Intent(PingableService.ACTION_PING)); super.onStart(); } @Override protected void onStop() { LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver); super.onStop(); } protected BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive (Context context, Intent intent) { // here you receive the response from the service if (intent.getAction().equals(PingableService.ACTION_PONG)) { isSvcRunning = true; } } }; } 

Versión Xamarin C #:

 private bool isMyServiceRunning(System.Type cls) { ActivityManager manager = (ActivityManager)GetSystemService(Context.ActivityService); foreach (var service in manager.GetRunningServices(int.MaxValue)) { if (service.Service.ClassName.Equals(Java.Lang.Class.FromType(cls).CanonicalName)) { return true; } } return false; } 

Para el caso de uso dado aquí, simplemente podemos hacer uso del valor de retorno del método stopService() . Devuelve true si existe el servicio especificado y se elimina. De lo contrario, devuelve false . Por lo tanto, puede reiniciar el servicio si el resultado es false contrario, se garantiza que se detuvo el servicio actual. 🙂 Sería mejor si miras esto .

De nuevo, otra alternativa que las personas pueden encontrar más limpia si usan intenciones pendientes (por ejemplo, con el AlarmManager :

 public static boolean isRunning(Class serviceClass) { final Intent intent = new Intent(context, serviceClass); return (PendingIntent.getService(context, CODE, intent, PendingIntent.FLAG_NO_CREATE) != null); } 

Donde CODE es una constante que usted define privadamente en su clase para identificar los bashs pendientes asociados a su servicio.

A continuación se muestra un truco elegante que cubre todos los Ifs . Esto es solo para servicios locales.

  public final class AService extends Service { private static AService mInstance = null; public static boolean isServiceCreated() { try { // If instance was not cleared but the service was destroyed an Exception will be thrown return mInstance != null && mInstance.ping(); } catch (NullPointerException e) { // destroyed/not-started return false; } } /** * Simply returns true. If the service is still active, this method will be accessible. * @return */ private boolean ping() { return true; } @Override public void onCreate() { mInstance = this; } @Override public void onDestroy() { mInstance = null; } } 

Y luego más adelante:

  if(AService.isServiceCreated()){ ... }else{ startService(...); } 

Puede haber varios servicios con el mismo nombre de clase.

Acabo de crear dos aplicaciones. El nombre del paquete de la primera aplicación es com.example.mock . Creé un subpaquete llamado lorem en la aplicación y un servicio llamado Mock2Service . Por lo tanto, su nombre completamente calificado es com.example.mock.lorem.Mock2Service .

Luego creé la segunda aplicación y un servicio llamado Mock2Service . El nombre del paquete de la segunda aplicación es com.example.mock.lorem . El nombre completo del servicio es com.example.mock.lorem.Mock2Service , también.

Aquí está mi salida de logcat.

 03-27 12:02:19.985: D/TAG(32155): Mock-01: com.example.mock.lorem.Mock2Service 03-27 12:02:33.755: D/TAG(32277): Mock-02: com.example.mock.lorem.Mock2Service 

Una mejor idea es comparar las instancias de ComponentName porque equals() de ComponentName compara los nombres de los paquetes y de las clases. Y no puede haber dos aplicaciones con el mismo nombre de paquete instalado en un dispositivo.

El método equals () de ComponentName .

 @Override public boolean equals(Object obj) { try { if (obj != null) { ComponentName other = (ComponentName)obj; // Note: no null checks, because mPackage and mClass can // never be null. return mPackage.equals(other.mPackage) && mClass.equals(other.mClass); } } catch (ClassCastException e) { } return false; } 

Nombre del componente

Dentro de TheServiceClass define:

  public static Boolean serviceRunning = false; 

Luego, en onStartCommand (…)

  public int onStartCommand(Intent intent, int flags, int startId) { serviceRunning = true; ... } @Override public void onDestroy() { serviceRunning = false; } 

Luego, llame a if(TheServiceClass.serviceRunning == true) desde cualquier clase.

La respuesta de geekQ pero en la clase Kotlin. Gracias geekQ

 fun isMyServiceRunning(serviceClass : Class<*> ) : Boolean{ var manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager for (service in manager.getRunningServices(Integer.MAX_VALUE)) { if (serviceClass.name.equals(service.service.className)) { return true } } return false } 

La llamada

 isMyServiceRunning(NewService::class.java) 

Esto se aplica más a la depuración del Servicio de Intención, ya que engendran un hilo, pero también puede funcionar para los servicios regulares. Encontré este hilo gracias a Binging

En mi caso, jugué con el depurador y encontré la vista del hilo. De alguna manera se parece al ícono de viñeta en MS Word. De todos modos, no tienes que estar en modo de depuración para usarlo. Haga clic en el proceso y haga clic en ese botón. Cualquier Servicio de Intento se mostrará mientras se están ejecutando, al menos en el emulador.

Si el servicio pertenece a otro proceso o APK, utilice la solución basada en el ActivityManager.

Si tiene acceso a su fuente, solo use la solución basada en un campo estático. Pero en lugar de usar un booleano sugeriría usar un objeto Date. Mientras el servicio se está ejecutando, simplemente actualice su valor a ‘ahora’ y cuando termine, configúrelo como nulo. De la actividad puede verificar si es nula o la fecha es demasiado antigua, lo que significará que no se está ejecutando.

También puede enviar notificaciones de difusión desde su servicio indicando que se está ejecutando a lo largo de más información, como progreso.

 public static boolean isServiceRunning; @Override public int onStartCommand(Intent intent, int flags, int startId) { isServiceRunning = true; return START_NOT_STICKY; } @Override public void onDestroy() { super.onDestroy(); isServiceRunning = false; } 

O

si no quieres usar static, entonces el usuario SharedPreferences

 @Override public int onStartCommand(Intent intent, int flags, int startId) { PrefManager.getPref().saveBoolean(AppConstants.Pref.IS_SERVICE_RUNNING, true); return START_NOT_STICKY; } @Override public void onDestroy() { super.onDestroy(); PrefManager.getPref().saveBoolean(AppConstants.Pref.IS_SERVICE_RUNNING, false); } 

PrefManager es mi clase singleton para gestionar todas las preferencias.

enlace de uso simple con no crear automáticamente – vea ps. y actualizar …

 public abstract class Context { ... /* * @return {true} If you have successfully bound to the service, * {false} is returned if the connection is not made * so you will not receive the service object. */ public abstract boolean bindService(@RequiresPermission Intent service, @NonNull ServiceConnection conn, @BindServiceFlags int flags); 

ejemplo:

  Intent bindIntent = new Intent(context, Class); boolean bindResult = context.bindService(bindIntent, ServiceConnection, 0); 

¿por qué no usar? getRunningServices ()

 List getRunningServices (int maxNum) Return a list of the services that are currently running. 

Nota: este método solo está destinado a depurar o implementar interfaces de usuario de tipo de gestión de servicios.


PD. la documentación de Android es engañosa. He abierto un problema en el rastreador de Google para eliminar cualquier duda:

https://issuetracker.google.com/issues/68908332

como podemos ver, el servicio de vinculación en realidad invoca una transacción a través del archivador de ActivityManager a través de los archivadores de memoria caché de servicio; no puedo rastrear qué servicio es responsable de vincularlo, pero como podemos ver, el resultado para bind es:

 int res = ActivityManagerNative.getDefault().bindService(...); return res != 0; 

la transacción se realiza a través de la carpeta:

 ServiceManager.getService("activity"); 

siguiente:

  public static IBinder getService(String name) { try { IBinder service = sCache.get(name); if (service != null) { return service; } else { return getIServiceManager().getService(name); 

esto se establece en ActivityThread a través de:

  public final void bindApplication(...) { if (services != null) { // Setup the service cache in the ServiceManager ServiceManager.initServiceCache(services); } 

esto se llama en ActivityManagerService en método:

  private final boolean attachApplicationLocked(IApplicationThread thread, int pid) { ... thread.bindApplication(... , getCommonServicesLocked(),...) 

entonces:

  private HashMap getCommonServicesLocked() { 

pero no hay paquete de ventana de “actividad” y alarma.

así que necesitamos volver a llamar:

  return getIServiceManager().getService(name); sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject()); 

esto hace que la llamada a través de:

  mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0); 

lo que lleva a :

 BinderInternal.getContextObject() 

y este es el método nativo …

  /** * Return the global "context object" of the system. This is usually * an implementation of IServiceManager, which you can use to find * other services. */ public static final native IBinder getContextObject(); 

No tengo tiempo ahora para cavar en c, así que hasta que diseccione la llamada de descanso suspenderé mi respuesta.

pero la mejor forma de verificar si el servicio se está ejecutando es crear un enlace (si el enlace no se creó, el servicio no existe) y consultar el servicio acerca de su estado a través del enlace (usando el indicador interno almacenado en ese estado).

actualización 23.06.2018

Encontré esos interesantes:

 /** * Provide a binder to an already-bound service. This method is synchronous * and will not start the target service if it is not present, so it is safe * to call from {@link #onReceive}. * * For peekService() to return a non null {@link android.os.IBinder} interface * the service must have published it before. In other words some component * must have called {@link android.content.Context#bindService(Intent, ServiceConnection, int)} on it. * * @param myContext The Context that had been passed to {@link #onReceive(Context, Intent)} * @param service Identifies the already-bound service you wish to use. See * {@link android.content.Context#bindService(Intent, ServiceConnection, int)} * for more information. */ public IBinder peekService(Context myContext, Intent service) { IActivityManager am = ActivityManager.getService(); IBinder binder = null; try { service.prepareToLeaveProcess(myContext); binder = am.peekService(service, service.resolveTypeIfNeeded( myContext.getContentResolver()), myContext.getOpPackageName()); } catch (RemoteException e) { } return binder; } 

en breve 🙂

“Proporcione una carpeta a un servicio ya encuadernado. Este método es síncrono y no iniciará el servicio objective si no está presente”.

public IBinder peekService (servicio de intenciones, String resolvedType, String callingPackage) arroja RemoteException;

*

 public static IBinder peekService(IBinder remote, Intent service, String resolvedType) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken("android.app.IActivityManager"); service.writeToParcel(data, 0); data.writeString(resolvedType); remote.transact(android.os.IBinder.FIRST_CALL_TRANSACTION+84, data, reply, 0); reply.readException(); IBinder binder = reply.readStrongBinder(); reply.recycle(); data.recycle(); return binder; } 

*

Tómalo fácil chicos … 🙂

Creo que la solución más adecuada es mantener un par clave-valor en SharedPreferences sobre si el servicio se está ejecutando o no.

La lógica es muy recta; en cualquier posición deseada en su clase de servicio; pon un valor booleano que actuará como una bandera para ti sobre si el servicio se está ejecutando o no. Luego lea este valor donde quiera en su aplicación.

A continuación, se muestra un código de muestra que estoy usando en mi aplicación:

En mi clase de servicio (un servicio para Audio Stream), ejecuto el siguiente código cuando el servicio está activo;

 private void updatePlayerStatus(boolean isRadioPlaying) { SharedPreferences sharedPref = this.getSharedPreferences(getString(R.string.str_shared_file_name), Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPref.edit(); editor.putBoolean(getString(R.string.str_shared_file_radio_status_key), isRadioPlaying); editor.commit(); } 

Luego, en cualquier actividad de mi aplicación, estoy verificando el estado del servicio con la ayuda del siguiente código;

 private boolean isRadioRunning() { SharedPreferences sharedPref = this.getSharedPreferences(getString(R.string.str_shared_file_name), Context.MODE_PRIVATE); return sharedPref.getBoolean(getString(R.string.str_shared_file_radio_status_key), false); } 

Sin permisos especiales, sin bucles … De manera fácil, solución limpia 🙂

Si necesita información adicional, consulte el enlace

Espero que esto ayude.