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



Answers

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.

Question

¿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.




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



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.




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



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.




Antes que nada, no intente llegar al servicio mediante el ActivityManager. (Discutido here )

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.




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 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.




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 link

Espero que esto ayude.




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.




De nuevo, otra alternativa que las personas pueden encontrar más limpia si usan intenciones pendientes (por ejemplo, con 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.




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



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

ComponentName






Related