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

936

¿Cómo verifico si se está ejecutando un servicio en segundo plano?

Quiero una actividad de Android que alterne el estado del servicio; me permite activarla si está desactivada y desactivada si está activada.

abeja
fuente
2
Echa un vistazo a esta guía alemana .
Markus Peröbner
17
la respuesta correcta está debajo y no la marcada: stackoverflow.com/a/5921190/2369122
toidiu
1
@toidiu Si eso no se ha puesto nervioso getRunningTasks(), probablemente lo será.
Kevin Krumwiede
utilizando la función getSystemService () puede recuperar todos los servicios en ejecución. recorrerlo y comprobar que su servicio existe en la lista aquí puede ver una pequeña muestra wiki.workassis.com/android-check-the-service-is-running
Bikesh M

Respuestas:

292

Tuve el mismo problema no hace mucho. Como mi servicio era local, terminé simplemente usando un campo estático en la clase de servicio para alternar el estado, 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 del mismo .apk y está vinculando al servicio con una intención concreta (una que especifica la clase de servicio exacta), entonces simplemente puede hacer que su servicio establezca una variable global cuando se está ejecutando ese Su 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 condiciones de carrera en tu código.

miracle2k
fuente
27
@Pacerier, la solución a la que hace referencia requiere iniciar el servicio y creo que la mejor solución flexible debería permitirle verificar si un servicio se está ejecutando sin iniciarlo.
Tom
17
¿Qué pasa si el sistema detiene el servicio, cómo lo detecta y cambia su variable?
jmng
23
Cuando se cierra la aplicación, también se elimina el servicio que había iniciado, pero onDestroy()no se llama al servicio. Por lo tanto, la variable estática no se puede actualizar en tal escenario, lo que resulta en un comportamiento inconsistente.
faizal
55
@faizal ¿No se volvería a inicializar también la variable estática, estableciéndola nuevamente en el valor predeterminado que indica que el servicio ya no se está ejecutando?
PabloC
12
@faizal, el Servicio local no es un proceso separado, por lo que si el servicio se elimina, la aplicación también se eliminará.
Sever
1674

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 onDestroy o onSometing events 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 se llaman o no. Tenga en cuenta la columna "eliminable" en la tabla de eventos del ciclo de vida en la documentación de Android.

geekQ
fuente
85
Gracias por esta solucion. Me gustaría agregar: En cambio, "com.example.MyService" es más elegante de usar MyService.class.getName ()
peter.bartos
10
Personalmente, seguí usando un campo estático. Aunque el uso de getRunningServices () es una solución más robusta, creo que hay en estas dos soluciones una compensación entre robustez y eficiencia / simplicidad. Si necesita verificar con frecuencia si un servicio se está ejecutando, recorrer los más de 30 servicios en ejecución no es lo ideal. El raro caso de que un servicio sea destruido por el sistema puede ser manejado quizás por un bloque try / catch o usando START_STICKY.
robguinness
80
No, no es la respuesta correcta porque también está escrito en los documentos: "Nota: este método solo está destinado a depurar o implementar interfaces de usuario de tipo de gestión de servicios". ¡No es para controlar el flujo!
seb
40
¿A la gente le parece elegante tener que pasar por todo eso para verificar si un servidor está funcionando?
Rui Marques
81
A partir de Android O , getRunningServicesestá en desuso. Esta respuesta necesita una actualización para la versión más nueva.
poring91
75

¡Entendido!

Usted DEBE llamar startService()a su servicio para estar debidamente registrado y el paso BIND_AUTO_CREATEno 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;
     }
}
Kevin Parker
fuente
Esto enumerará solo los servicios del sistema, ¿no? Por lo tanto, mi servicio local está excluido de la lista y obtendré información falsa; (
Ewoks
Esto funciona con servicios externos, para los servicios locales es bastante obvio si está ejecutando.
Kevin Parker
11
Lo siento, pero tengo que decir que es una respuesta súper tonta ... ¡¿Por qué es súper obvio ?!
Ewoks
10
No está claro a qué te refieres aquí ... ¿Quién estaba hablando de estrellarse en absoluto? No estoy interesado en estrellarlo. El servicio puede iniciarse, detenerse, tal vez fue un servicio intencionado y se detendrá por sí solo cuando se termine ... La pregunta es cómo saber si aún se está ejecutando o no después de 3 minutos, por ejemplo.
Ewoks el
1
Es incorrecto dar la impresión de que también se debe iniciar un servicio vinculado. NO. La creación automática de Bind hace exactamente lo que dice. Creará (y por lo tanto "iniciará") el servicio si aún no se está ejecutando.
Sreedevi J
57

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 puede ser atrapado por el servicio no es una buena idea, ya que comenzará el servicio si no se está ejecutando.

Entonces, como sugirió miracle2k, 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 recuperación muy perezosa: es decir, no hay instanciación de 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í. El servicio solo se inicia a través de los métodos normales de inicio del servicio.

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

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 clases dentro de la aplicación / paquete del servicio. Si sus clases están fuera de la aplicación / paquete de servicio, puede consultar el ActivityManager con las limitaciones subrayadas por Pieter-Jan Van Robays.

Snicolas
fuente
32
Esto es defectuoso. No se garantiza que onDestroy sea llamado.
Pacerier
8
Cuando el sistema tiene poca memoria, su servicio se cancelará automáticamente sin una llamada a su onDestroy, por lo que digo que esto es defectuoso.
Pacerier
17
@Pacerier, pero si el sistema mata el proceso, el indicador de instancia aún se restablecerá. Supongo que la próxima vez que se cargue el receptor (después de que el sistema elimine el servicio), la 'instancia' de bandera estática se volverá a crear como nula.
Tom
2
Al menos mejor que iterar a través de todos esos servicios en isMyServiceRunning que realmente retrasa las cosas si se hace en cada rotación del dispositivo :)
Gunnar Forsgren - Mobimation
1
Su variable de instancia no debe declararse final, de lo contrario no se puede establecer o anular mediante los métodos onCreate () o onDestroy ().
k2col
27

Puede usar esto (aún no lo intenté, 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.

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

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

Keenan Gebze
fuente
11
No es exactamente lo que dicen los documentos. Según su enlace: "Devoluciones Si el servicio se está iniciando o ya se está ejecutando, se devuelve el ComponentName del servicio real que se inició; de lo contrario, si el servicio no existe, se devuelve un valor nulo".
Gabriel
Buen pensamiento ... pero no encaja en la situación actual.
Code_Life
55
no es la forma correcta, porque cuando se active IDE, if(startService(someIntent) != null)eso lo comprobará, IsserviceRunningpero también jugará un nuevo servicio.
Chintan Khetiya
Como se indicó, si detiene el servicio después de este control, será útil para este problema. Pero, ¿por qué iniciar y detener un servicio por nada?
Taner
66
Esto iniciará el servicio, ¿no? Solo quiero verificar el estado del servicio en lugar de iniciarlo ...
Raptor
26
/**
 * Check if the service is Running 
 * @param serviceClass the class of the Service
 *
 * @return true if the service is running otherwise false
 */
public boolean checkServiceRunning(Class<?> serviceClass){
    ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE))
    {
        if (serviceClass.getName().equals(service.service.getClassName()))
        {
            return true;
        }
    }
    return false;
}
JR
fuente
21

Un extracto de documentos de Android :

Al igual que sendBroadcast (Intent) , pero si hay receptores para el Intent, esta función los bloqueará y los enviará inmediatamente antes de regresar.

Piense en este truco como "ping" alService . Como podemos transmitir sincrónicamente, podemos transmitir y obtener un resultado sincrónicamente , en el hilo de la interfaz de usuario.

Service

@Override
public void onCreate() {
   LocalBroadcastManager
     .getInstance(this)
     .registerReceiver(new ServiceEchoReceiver(), new IntentFilter("ping"));
     //do not forget to deregister the receiver when the service is destroyed to avoid
     //any potential memory leaks 
}

private class ServiceEchoReceiver extends BroadcastReceiver {
    public void onReceive (Context context, Intent intent) {
      LocalBroadcastManager
         .getInstance(this)
         .sendBroadcastSync(new Intent("pong"));
    }
}

Activity

    bool serviceRunning = false;

    protected void onCreate (Bundle savedInstanceState){
        LocalBroadcastManager.getInstance(this).registerReceiver(pong, new IntentFilter("pong"));
        LocalBroadcastManager.getInstance(this).sendBroadcastSync(new Intent("ping"));
        if(!serviceRunning){
           //run the service
        }
    }

    private BroadcastReceiver pong = new BroadcastReceiver(){
        public void onReceive (Context context, Intent intent) {
          serviceRunning = true;   
        }
    }

El ganador en muchas aplicaciones es, por supuesto, un campo booleano estático en el servicio que está configurado en truein Service.onCreate()y to falsein Service.onDestroy()porque es mucho más simple.

Peterchaula
fuente
Esta es una solución mucho mejor que la aceptada, que falla si Android mata el servicio, ya que el método de variable global aún indicaría que el servicio se está ejecutando cuando en realidad ya no lo está. Este truco de transmisión de ping-pong síncrono es en realidad el ÚNICO método confiable para verificar si un servicio está activo. Solo le permite SOLICITAR el servicio si está allí. Si responde, el servicio está activo y ejecutándose, de lo contrario, no se ha iniciado o se ha cerrado, ya sea mediante programación o por el sistema para recuperar memoria.
Phoenix Revelado
13

Modifiqué ligeramente una de las soluciones presentadas anteriormente, pero pasé la clase en lugar de un nombre de cadena genérico, para asegurarme 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);
loretoparisi
fuente
para ser más estricto, puedes cambiar el Class<? extends Service>
parámetro de
11

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 el BroadcastReceiver cuando se inicie el servicio y anule el registro cuando se destruya el servicio. Desde su actividad (o cualquier componente), envíe una intención 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;
            }
        }
    };
}
Ben H
fuente
1
De hecho, me gusta este enfoque. Es un poco codificado pero siempre funcionará. No veo las intenciones de transmisión consideran obsoletos cualquier momento pronto :)
ShellDude
8

Solo quiero agregar una nota a la respuesta de @Snicolas. Los siguientes pasos se pueden utilizar para verificar detener el servicio con / sin llamadas 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 -> Seleccione 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 también se detendrán las instancias de servicio.

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

Pablo
fuente
7

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 desde Eclipse. La aplicación se cierra a la fuerza con SIG: 9.

Kevin Parker
fuente
6

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

Los servicios pueden ejecutarse por sí mismos, estar vinculados a una Actividad o ambas. La forma de registrar una Actividad si su Servicio se está ejecutando o no es haciendo una interfaz (que extienda Binder) donde declare métodos que tanto la Actividad como el Servicio entiendan. Puede hacer esto haciendo su propia interfaz donde declara, por ejemplo, "isServiceRunning ()". Luego puede vincular su Actividad a su Servicio, ejecutar el método isServiceRunning (), el Servicio verificará 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 cómo implementar este escenario en mi aplicación.

Pieter-Jan Van Robays
fuente
3
Esa discusión tuvo lugar el 26/12/07. O eso es julio de este año (es decir, en el futuro), o eso es antes de que Android fuera incluso público. De cualquier manera, eso me hace no confiar en él.
Tom
Esa discusión es del 26 de diciembre de 2007. Creo que están discutiendo una versión preliminar ( developer.android.com/sdk/OLD_RELEASENOTES.html#m3-rc37a ) que se lanzó el 14 de diciembre de 2007.
ingh.am
6

Una vez más, otra alternativa que las personas podrían 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 CODEes una constante que usted define de forma privada en su clase para identificar los intentos pendientes asociados a su servicio.

Snicolas
fuente
1
Combina o actualiza tu respuesta anterior. Abstenerse de publicar más de una respuesta por publicación.
ChuongPham 01 de
¿Se puede ampliar esta respuesta, es decir, cómo se asocia el valor de CODE con el servicio?
Dave Nottage
¿Dónde obtener contexto?
albahaca
6

A continuación se muestra un elegante truco que cubre todo 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 tarde:

    if(AService.isServiceCreated()){
        ...
    }else{
        startService(...);
    }
TheRealChx101
fuente
El único problema con esto es si el servicio es Sticky y se reinicia solo. Llamar a isServiceCreated () devolverá falso después de que el servicio se inicie nuevamente porque mInstance será nulo.
Mira_Cole
1
¿No se llamaría a onCreate cuando el servicio se reinicie?
TheRealChx101
6

Versión de 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;
}
Nima
fuente
Necesita el ´Context` para GetSystemService.
prueba el
5

Para el caso de uso dado aquí, simplemente podemos hacer uso del stopService()valor de retorno del método. Regresa truesi existe el servicio especificado y se elimina. De lo contrario, vuelve false. Por lo tanto, puede reiniciar el servicio si, de lo falsecontrario, se garantiza que el servicio actual se ha detenido. :) Sería mejor si echas un vistazo a esto .

Rahul Raveendran
fuente
5

Otro enfoque usando kotlin. Inspirado en las respuestas de otros usuarios

fun isMyServiceRunning(serviceClass: Class<*>): Boolean {
    val manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    return manager.getRunningServices(Integer.MAX_VALUE)
            .any { it.service.className == serviceClass.name }
}

Como extensión de kotlin

fun Context.isMyServiceRunning(serviceClass: Class<*>): Boolean {
    val manager = this.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    return manager.getRunningServices(Integer.MAX_VALUE)
            .any { it.service.className == serviceClass.name }
}

Uso

context.isMyServiceRunning(MyService::class.java)
Oscar Emilio Perez Martinez
fuente
4

En kotlin puede agregar una variable booleana en un objeto complementario y verificar su valor de cualquier clase que desee:

companion object{
     var isRuning = false

}

Cambie su valor cuando se crea y destruye el servicio

 override fun onCreate() {
        super.onCreate()
        isRuning = true
    }

override fun onDestroy() {
    super.onDestroy()
    isRuning = false
    }
Mohamed Saber
fuente
3

En su subclase de servicio, utilice un booleano estático para obtener el estado del servicio como se muestra a continuación.

MyService.kt

class MyService : Service() {
    override fun onCreate() {
        super.onCreate()
        isServiceStarted = true
    }
    override fun onDestroy() {
        super.onDestroy()
        isServiceStarted = false
    }
    companion object {
        var isServiceStarted = false
    }
}

MainActivity.kt

class MainActivity : AppCompatActivity(){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val serviceStarted = FileObserverService.isServiceStarted
        if (!serviceStarted) {
            val startFileObserverService = Intent(this, FileObserverService::class.java)
            ContextCompat.startForegroundService(this, startFileObserverService)
        }
    }
}
EdgeDev
fuente
3

Para kotlin, puede usar el siguiente código.

fun isMyServiceRunning(calssObj: Class<SERVICE_CALL_NAME>): Boolean {
    val manager = requireActivity().getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    for (service in manager.getRunningServices(Integer.MAX_VALUE)) {
        if (calssObj.getName().equals(service.service.getClassName())) {
            return true
        }
    }
    return false
}
PA Gosai
fuente
Esta es una gran respuesta para escribir pruebas, ya que puede usarla sin cambiar su código de trabajo.
Robert Liberatore
2

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)
Luis Eduardo Moreno
fuente
66
ActivityManager.getRunningServicesestá en desuso desde Android O
Daniel Shatz
1

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 loremen la aplicación y un servicio llamado Mock2Service. Entonces su nombre completo 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 escom.example.mock.lorem.Mock2Service .

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 ComponentNameinstancias debido equals()aComponentName compara ambos nombres de paquetes y nombres de clase. 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

Maksim Dmitriev
fuente
1

Por favor use este código.

if (isMyServiceRunning(MainActivity.this, xyzService.class)) { // Service class name
    // Service running
} else {
    // Service Stop
}


public static boolean isMyServiceRunning(Activity activity, Class<?> serviceClass) {
        ActivityManager manager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (serviceClass.getName().equals(service.service.getClassName())) {
                return true;
            }
        }
        return false;
    }
Nikunj Sorathiya
fuente
0

Esto se aplica más a la depuración de Intent Service ya que generan un hilo, pero también puede funcionar para servicios regulares. Encontré este hilo gracias a Binging

En mi caso, jugué con el depurador y encontré la vista de hilo. Se parece al icono de viñeta en MS Word. De todos modos, no tiene que estar en modo depurador para usarlo. Haga clic en el proceso y haga clic en ese botón. Cualquier Intent Services aparecerá mientras se está ejecutando, al menos en el emulador.

Joe Plante
fuente
0

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

Si tiene acceso a su fuente, simplemente 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. Desde la actividad puede verificar si es nula o si la fecha es demasiado antigua, lo que significa que no se está ejecutando.

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

FranMowinckel
fuente
0

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 if(TheServiceClass.serviceRunning == true)desde cualquier clase.

Badr
fuente
44
Esto no funciona si su servicio es asesinado por Android.
Heisenberg
@Heisenberg Acabo de experimentar eso yo mismo. ¿Sabes por qué no?
Tim
@Heisenberg cuando el sistema operativo mata mi aplicación, el servicio se reinicia y establece el bool estático en verdadero, pero al obtenerlo informa falso
Tim
esto no funcionará si llamas stopService. Al menos para servicios de intención. onDestroy()será llamado de inmediato, pero onHandleIntent()seguirá ejecutándose
serggl
1
@Heisenberg ¿No matará el servicio debido a la poca memoria también significa matar el proceso?
desarrollador de Android el
0

enlace de uso simple con no crear automáticamente - ver 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<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.


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 enlace en realidad invoca una transacción a través del enlazador ActivityManager a través de los enlazadores de caché del servicio: no sé qué servicio es responsable del enlace, pero como podemos ver, el resultado para el enlace 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 el método:

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

entonces:

 private HashMap<String, IBinder> getCommonServicesLocked() {

pero no hay "actividad" solo paquete de ventanas y alarma.

entonces necesitamos volver a llamar:

 return getIServiceManager().getService(name);

    sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());

esto hace llamar a través de:

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

lo que lleva a :

BinderInternal.getContextObject()

y este es un 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 manera de verificar si el servicio se está ejecutando es crear un enlace (si el enlace no se crea, el servicio no existe) y consultar al servicio sobre su estado a través del enlace (usando el indicador interno almacenado en este estado).

actualizar 23.06.2018

los encontré 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 vinculado. Este método es sincrónico y no iniciará el servicio de destino si no está presente".

public pederService de IBinder (servicio de intención, String resolveType, String callingPackage) produce 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;
}

* *

ceph3us
fuente
bindResult (valor de retorno del método bindService) no es falso si el servicio no se está ejecutando.
Shangeeth Sivan
0

Mi conversión de Kotlin de las ActivityManager::getRunningServicesrespuestas basadas. Pon esta función en una actividad

private fun isMyServiceRunning(serviceClass: Class<out Service>) =
    (getSystemService(ACTIVITY_SERVICE) as ActivityManager)
        .getRunningServices(Int.MAX_VALUE)
        ?.map { it.service.className }
        ?.contains(serviceClass.name) ?: false
Gulshan
fuente
-2

Es posible que use estas opciones de las opciones de Desarrollador de Android para ver si su servicio aún se está ejecutando en segundo plano.

1. Open Settings in your Android device.
2. Find Developer Options.
3. Find Running Services option.
4. Find your app icon.
5. You will then see all the service that belongs to your app running in the background.
Haomin
fuente
-5

Tómelo con calma chicos ... :)

Creo que la solución más adecuada es mantener un par clave-valor SharedPreferencessobre 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 un indicador para ti sobre si el servicio se está ejecutando o no. Luego lea este valor cuando lo desee 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, verifico 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 ... Manera fácil, solución limpia :)

Si necesita información adicional, consulte el enlace

Espero que esto ayude.

Taner
fuente
19
Solo que nadie actualizará el valor cuando maten el servicio
Gunnar Forsgren - Mobimation
cuando se cancela el servicio onDestroy () se activará y eso es posible actualizar su estado
Jongz Puangput
55
@JongzPuangput, onDestroyno siempre se llama cuando se cancela el servicio. Por ejemplo, he visto mis servicios eliminados en situaciones de poca memoria sin onDestroyser llamado.
Sam
@Sam Entonces, ¿cómo se llamará?
Ruchir Baronia
2
@RuchirBaronia Por lo que recuerdo, simplemente no se te notifica cuando se matan tus cosas. Creo que Android está diseñado para eliminar aplicaciones según sea necesario, y las aplicaciones deben diseñarse para esperar que se eliminen en cualquier momento sin notificación.
Sam