¿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 alterne el estado del servicio; me permite activarlo si está apagado y apagado si está activado.



Answers


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<RunningServiceInfo> 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 objetivo es saber si un servicio se está ejecutando sin ejecutarlo realmente si no se está ejecutando.

Llamar a bindService o llamar a un intento 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 o 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;
    }



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.




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.




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 .




Xamarin C # verison.

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



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



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<? extends Service> 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 intentos pendientes asociados a su servicio.




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.




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.

Esto es un truco, pero piense en esto como un "ping" del Service ya que podemos transmitir sincrónicamente, podemos transmitir y obtener un resultado sincrónicamente 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;   
        }
    }



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




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



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.




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.




esto funciona para mí

 if(startService(new Intent(this, YourService.class)) != 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();
  }



enlace de uso simple con no crear auto

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<Service>);
    boolean bindResult = context.bindService(bindIntent, ServiceConnection, 0);

¿por qué no usar? getRunningServices ()

List<ActivityManager.RunningServiceInfo> 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.