android-service aplicaciones segundo - ¿Cómo comprobar si un servicio se está ejecutando en Android?





11 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 los servicios en ejecución provistos por el sistema operativo Android a través de ActivityManager#getRunningServices .

Todos los enfoques que utilicen 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 finalizar el proceso o a cuál de las llamadas devueltas se llama o no. Tenga en cuenta la columna "killable" en la tabla de eventos del ciclo de vida en la documentación de Android.

plano app para

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

Quiero una actividad de Android que cambie el estado del servicio; me permite encenderlo si está apagado y apagarlo si está encendido.




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 pueda ser detectado por el servicio no es una buena idea, ya que iniciará 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 claro, sugiero transformar el servicio en un singleton con una búsqueda muy perezosa: es decir, no hay instanciación en absoluto de 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 ni instancia el propio singleton. 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 de singleton para cambiar el nombre del confuso método getInstance a algo como el isInstanceCreated() : boolean .

El código se verá como:

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




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



Solo quiero añadir una nota a la respuesta por @Snicolas. Los siguientes pasos pueden usarse para verificar el servicio de parada 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 -> Seleccione y "Forzar detención" su aplicación en la que se ejecuta su servicio. Sin embargo, como su aplicación se detiene aquí, definitivamente las instancias de 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 está funcionando para mí.




onDestroy no siempre se llama en el servicio, por lo que es inútil!

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




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

Los servicios pueden ejecutarse por su cuenta, vinculados a una actividad o a ambas. La forma de verificar una Actividad si su Servicio se está ejecutando o no es haciendo una interfaz (que extienda el Binder) donde declara los métodos que tanto la Actividad como el Servicio comprenden. Puede hacer esto creando su propia interfaz donde declare, por ejemplo, "isServiceRunning ()". Luego, puede vincular su Actividad a su Servicio, ejecutar el método isServiceRunning (), el Servicio comprobará si está funcionando o no y devuelve un 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.




Para el caso de uso aquí indicado, simplemente podemos utilizar el valor de retorno del método stopService() . Devuelve true si existe el servicio especificado y se cancela. En caso contrario devuelve false . Por lo tanto, puede reiniciar el servicio si el resultado es false contrario se garantiza que el servicio actual se ha detenido. :) Sería mejor si miras this .




A continuación se muestra un hack 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 tarde:

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



La respuesta de geekQ pero en clase Kotlin. Gracias geek

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)



Si el servicio pertenece a otro proceso o APK, use 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 eso, 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 en nulo. Desde la actividad puede verificar si es nulo o la fecha es demasiado antigua, lo que significará que no se está ejecutando.

También puede enviar notificaciones de transmisión desde su servicio indicando que se está ejecutando junto con información adicional como el progreso.




simple uso enlazar con no crear auto - 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 la depuración o la implementación de 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 vinculador ActivityManager a través de los enlazadores de caché de servicio. No doy seguimiento del servicio responsable de la vinculación, pero como podemos ver, el resultado de la vinculación 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 ventana y alarma ..

por lo que 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 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();

Ahora no tengo tiempo para excavar en c, así que hasta que diseccione la llamada de reposo suspendo mi respuesta.

pero la mejor manera de verificar si el servicio se está ejecutando es crear un enlace (si no se crea un enlace, el servicio no existe), y consultar al servicio sobre su estado a través del enlace (utilizando el indicador interno almacenado en su estado).

actualización 23.06.2018

me parecieron 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 :)

"Proporcionar una carpeta a un servicio ya enlazado. Este método es síncrono y no iniciará el servicio de destino si no está presente".

el servicio público peekService de IBinder (servicio intencional, tipo de cadena resuelto, paquete de llamada de cadena) lanza 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;
}

*




Related