Come verificare se un servizio è in esecuzione su Android?


936

Come posso verificare se è in esecuzione un servizio in background?

Voglio un'attività Android che commuti lo stato del servizio: mi consente di attivarlo se è spento e spento se è acceso.


2
Dai un'occhiata a questa guida tedesca .
Markus Peröbner,

17
la risposta corretta è al di sotto e non quello segnato: stackoverflow.com/a/5921190/2369122
toidiu

1
@toidiu Se non è già stato nerfato getRunningTasks(), probabilmente lo sarà.
Kevin Krumwiede,

utilizzando la funzione getSystemService () è possibile recuperare tutti i servizi in esecuzione. scorrilo
Bikesh M

Risposte:


292

Ho avuto lo stesso problema non molto tempo fa. Dato che il mio servizio era locale, ho finito semplicemente usando un campo statico nella classe di servizio per attivare lo stato, come descritto da hackbod qui

EDIT (per la cronaca):

Ecco la soluzione proposta da hackbod:

Se il tuo codice client e server fa parte dello stesso .apk e stai vincolando il servizio con un intento concreto (uno che specifica l'esatta classe di servizio), allora puoi semplicemente impostare il tuo servizio impostando una variabile globale quando lo sta eseguendo il tuo cliente può controllare.

Non abbiamo deliberatamente un'API per verificare se un servizio è in esecuzione perché, quasi a colpo sicuro, quando vuoi fare qualcosa del genere finisci con le condizioni di competizione nel tuo codice.


27
@Pacerier, la soluzione a cui fai riferimento richiede l'avvio del servizio e penso che la migliore soluzione flessibile dovrebbe permetterti di verificare se un servizio è in esecuzione senza avviarlo.
Tom,

17
Che dire se il servizio viene arrestato dal sistema, come viene rilevato e attivato la variabile?
jmng

23
Quando l'app viene uccisa, anche il servizio che è stato avviato viene ucciso, ma il servizio onDestroy()non viene chiamato. Pertanto la variabile statica non può essere aggiornata in uno scenario di questo tipo con conseguente comportamento incoerente.
faizal

5
@faizal Anche la variabile statica non verrebbe re-inizializzata, ripristinandola al valore predefinito che indica che il servizio non è più in esecuzione?
PabloC,

12
@faizal, il servizio locale non è un processo separato, quindi se il servizio viene interrotto anche l'app ucciderà.
Sever

1674

Uso all'interno di un'attività quanto segue:

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

E lo chiamo usando:

isMyServiceRunning(MyService.class)

Funziona in modo affidabile, perché si basa sulle informazioni relative ai servizi in esecuzione fornite dal sistema operativo Android tramite ActivityManager # getRunningServices .

Tutti gli approcci che utilizzano eventi onDestroy o onSometing o leganti o variabili statiche non funzioneranno in modo affidabile perché come sviluppatore non si sa mai, quando Android decide di interrompere il processo o quale dei callback menzionati viene chiamato o meno. Nota la colonna "killable" nella tabella degli eventi del ciclo di vita nella documentazione di Android.


85
Grazie per questa soluzione Vorrei aggiungere: Invece "com.example.MyService" è più elegante usare MyService.class.getName ()
peter.bartos il

10
Personalmente, sono andato con l'utilizzo di un campo statico. Sebbene l'utilizzo di getRunningServices () sia una soluzione più solida, credo che in queste due soluzioni vi sia un compromesso tra robustezza ed efficienza / semplicità. Se è necessario verificare frequentemente se un servizio è in esecuzione, non è l'ideale eseguire un ciclo attraverso potenzialmente più di 30 servizi in esecuzione. Il raro caso in cui un servizio venga distrutto dal sistema può essere gestito forse da un blocco try / catch o usando START_STICKY.
robguinness,

80
No, non è la risposta giusta perché è anche scritto nei documenti: "Nota: questo metodo è destinato esclusivamente al debug o all'implementazione delle interfacce utente del tipo di gestione del servizio". Non è pensato per il flusso di controllo!
settembre

40
Le persone trovano elegante dover passare attraverso tutto ciò per verificare se un server è in esecuzione?
Rui Marques,

81
A partire Android O , getRunningServicesè deprecato. Questa risposta richiede un aggiornamento per la versione più recente.
poring91,

75

Fatto!

È NECESSARIO chiamare startService()per la corretta registrazione del servizio e il passaggio BIND_AUTO_CREATEnon sarà sufficiente.

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

E ora la classe 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;
     }
}

Questo elencherà solo i servizi di sistema, no ?! Quindi il mio servizio locale è escluso dalla lista e diventerò falso; (
Ewoks

Funziona con servizi esterni, per i servizi locali è abbastanza ovvio se sei in esecuzione.
Kevin Parker,

11
Scusa ma devo dire che è una risposta super sciocca ... Perché è super ovvia ?!
Ewoks,

10
Non è chiaro cosa intendi qui ... Chi stava parlando di crash ?! Non mi interessa farlo a pezzi. Il servizio può essere avviato, arrestato, forse è stato un servizio intenzionale e si interromperà da solo al termine ... La domanda è come sapere se è ancora in esecuzione o meno dopo 3 minuti, ad esempio.
Ewoks,

1
Non è corretto dare l'impressione che debba essere avviato anche un servizio associato. NO. Bind auto create fa esattamente quello che dice. Creerà (e quindi "avvierà") il servizio se il servizio non è ancora in esecuzione.
Sreedevi J,

57

Un piccolo complemento è:

Il mio obiettivo è sapere se un servizio è in esecuzione senza realmente eseguirlo se non è in esecuzione.

Chiamare bindService o chiamare un intento che può essere colto dal servizio non è una buona idea, in quanto avvierà il servizio se non è in esecuzione.

Quindi, come suggerito da miracle2k, la cosa migliore è avere un campo statico nella classe di servizio per sapere se il servizio è stato avviato o meno.

Per renderlo ancora più pulito, suggerisco di trasformare il servizio in un singleton con un recupero molto pigro: cioè, non vi è alcuna istanza di tutta l' istanza singleton attraverso metodi statici. Il metodo statico getInstance del tuo servizio / singleton restituisce l'istanza del singleton solo se è stata creata. Ma in realtà non avvia o istanzia il singleton stesso. Il servizio viene avviato solo tramite i normali metodi di avvio del servizio.

Sarebbe quindi ancora più pulito modificare il modello di progettazione singleton per rinominare il metodo confuso getInstance in qualcosa di simile al isInstanceCreated() : booleanmetodo.

Il codice sarà simile a:

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

Questa soluzione è elegante, ma è rilevante solo se si ha accesso alla classe di servizio e solo per le classi è l'app / pacchetto del servizio. Se le tue classi sono al di fuori dell'app / pacchetto di servizi, puoi interrogare ActivityManager con le limitazioni sottolineate da Pieter-Jan Van Robays.


32
Questo è difettoso. onDestroy non è garantito per essere chiamato.
Pacerier

8
Quando il sistema ha poca memoria, il tuo servizio verrà automaticamente interrotto senza una chiamata a onDestroy, motivo per cui dico che questo è difettoso.
Pacerier

17
@Pacerier, ma se il sistema interrompe il processo, il flag dell'istanza verrà comunque ripristinato. Immagino che quando il prossimo ricevitore verrà caricato (dopo che il sistema ha ucciso il servizio) il flag statico 'istanza' verrà ricreato come nullo.
Tom,

2
Almeno meglio dell'iterazione attraverso tutti quei servizi in isMyServiceRunning che ritarda davvero roba se eseguita su ogni rotazione del dispositivo :)
Gunnar Forsgren - Mobimazione

1
La variabile di istanza non deve essere dichiarata come definitiva, altrimenti non può essere impostata o annullata dai metodi onCreate () o onDestroy ().
k2col

27

Puoi usarlo (non l'ho ancora provato, ma spero che funzioni):

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

Il metodo startService restituisce un oggetto ComponentName se esiste già un servizio in esecuzione. In caso contrario, verrà restituito null.

Vedi abstract pubblico ComponentName startService (servizio Intent) .

Penso che non sia come controllare, perché sta avviando il servizio, quindi puoi aggiungerlo stopService(someIntent);sotto il codice.


11
Non è esattamente quello che dicono i documenti. In base al collegamento: "Restituisce Se il servizio viene avviato o è già in esecuzione, viene restituito il ComponentName del servizio effettivo avviato; in caso contrario, se il servizio non esiste null viene restituito."
Gabriel

Bel pensiero ... ma non si adatta alla situazione attuale.
Code_Life,

5
non è il modo corretto, perché quando l'IDE si attiva if(startService(someIntent) != null)verificherà ciò, IsserviceRunningma riprodurrà anche un nuovo servizio.
Chintan Khetiya,

Come indicato, se si interrompe il servizio dopo questo controllo sarà utile per questo problema. Ma perché avviare e interrompere un servizio per niente?
Taner,

6
questo avvierà il servizio, no? Voglio solo controllare lo stato del servizio invece di avviarlo ...
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;
}

21

Un estratto dai documenti Android :

Come sendBroadcast (Intent) , ma se ci sono ricevitori per l'Intent questa funzione li bloccherà e li spedirà immediatamente prima di tornare.

Pensa a questo hack come "ping" ilService . Dal momento che possiamo trasmettere in modo sincrono, possiamo trasmettere e ottenere un risultato in modo sincrono , sul thread dell'interfaccia utente.

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

Il vincitore in molte applicazioni è, ovviamente, un campo booleano statico sul servizio che è impostato su truein Service.onCreate()e in falsein Service.onDestroy()perché è molto più semplice.


Questa è una soluzione molto migliore di quella accettata, che fallisce se Android uccide il servizio poiché il metodo della variabile globale indicherebbe ancora che il servizio è in esecuzione quando in realtà non lo è più. Questo trucco di trasmissione ping-pong sincrono è in realtà l'UNICO metodo affidabile per verificare se un servizio è attivo. Da solo ti permette semplicemente di CHIEDERE il servizio se è lì. Se risponde, il servizio è attivo e in esecuzione, in caso contrario non è stato avviato o è stato chiuso, a livello di programmazione o dal sistema per recuperare memoria.
Phoenix Rivelato

13

Ho leggermente modificato una delle soluzioni presentate sopra, ma passando la classe anziché un nome di stringa generico, per essere sicuro di confrontare le stringhe che escono dallo stesso metodo 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;
    }
}

e poi

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

per essere più severi puoi cambiare la classe param inClass<? extends Service>
silentsudo

11

Il modo corretto per verificare se un servizio è in esecuzione è semplicemente richiederlo. Implementa un BroadcastReceiver nel tuo servizio che risponda ai ping delle tue attività. Registrare BroadcastReceiver all'avvio del servizio e annullare la registrazione quando il servizio viene distrutto. Dalla tua attività (o qualsiasi componente), invia una trasmissione locale al servizio e se risponde, sai che è in esecuzione. Nota la sottile differenza tra ACTION_PING e ACTION_PONG nel codice seguente.

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

1
In realtà mi piace questo approccio. È un po 'pesante codice saggio ma funzionerà sempre. Non vedo che gli intenti delle trasmissioni saranno presto deprezzati :)
ShellDude

8

Voglio solo aggiungere una nota alla risposta di @Snicolas. I seguenti passaggi possono essere utilizzati per controllare il servizio di arresto con / senza chiamare onDestroy().

  1. onDestroy() chiamato: vai in Impostazioni -> Applicazione -> Servizi in esecuzione -> Seleziona e interrompi il servizio.

  2. onDestroy()non chiamato: vai in Impostazioni -> Applicazione -> Gestisci applicazioni -> Seleziona e "Forza arresto" l'applicazione in cui è in esecuzione il servizio. Tuttavia, poiché l'applicazione viene arrestata qui, anche le istanze del servizio verranno arrestate.

Infine, vorrei menzionare che l'approccio menzionato lì utilizzando una variabile statica nella classe singleton funziona per me.


7

onDestroy non è sempre chiamato nel servizio quindi questo è inutile!

Ad esempio: esegui di nuovo l'app con una modifica da Eclipse. L'applicazione viene forzatamente chiusa utilizzando SIG: 9.


6

Prima di tutto non devi provare a raggiungere il servizio utilizzando ActivityManager. (discusso qui )

I servizi possono essere eseguiti da soli, essere associati a un'attività o entrambi. Il modo per verificare un'attività se il servizio è in esecuzione o meno consiste nel creare un'interfaccia (che estende Binder) in cui si dichiarano metodi comprensibili sia per l'attività che per il servizio. Puoi farlo creando la tua interfaccia in cui dichiari ad esempio "isServiceRunning ()". È quindi possibile associare l'attività al servizio, eseguire il metodo isServiceRunning (), il servizio verificherà se è in esecuzione o meno e restituisce un valore booleano all'attività.

È inoltre possibile utilizzare questo metodo per interrompere il servizio o interagire con esso in un altro modo.

Ho usato questo tutorial per imparare come implementare questo scenario nella mia applicazione.


3
Tale discussione si è svolta il "26/12/07". O è luglio di quest'anno (cioè in futuro), o prima ancora che Android fosse pubblico. In entrambi i casi ciò non mi fa fidare.
Tom,

Quella discussione è del 26 dicembre 2007. Stanno discutendo di una versione preliminare che penso ( developer.android.com/sdk/OLD_RELEASENOTES.html#m3-rc37a ) che è stata rilasciata il 14 dicembre 2007.
ingh.am

6

Ancora una volta, un'altra alternativa che le persone potrebbero trovare più pulite se usano intenti in sospeso (ad esempio 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);
}

Dov'è CODEuna costante che definisci privatamente nella tua classe per identificare gli intenti in sospeso associati al tuo servizio.


1
Combina o aggiorna la tua risposta precedente. Si prega di astenersi dal pubblicare più di una risposta per post.
ChuongPham,

È possibile espandere questa risposta, ovvero come si associa il valore di CODICE al servizio?
Dave Nottage,

Dove trovare il contesto?
basilico

6

Di seguito è riportato un elegante trucco che copre tutto Ifs. Questo è solo per servizi locali.

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

E poi in seguito:

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

L'unico problema con questo è se il servizio è un servizio Sticky e si riavvia da solo. La chiamata a isServiceCreated () restituirà false dopo il riavvio del servizio perché mInstance sarà null.
Mira_Cole,

1
OnCreate non verrebbe chiamato quando il servizio si riavvierà da solo?
TheRealChx101,

6

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

È necessario il "Contesto" per GetSystemService.
test del

5

Per il caso d'uso indicato qui possiamo semplicemente utilizzare il stopService()valore di ritorno del metodo. Restituisce truese esiste il servizio specificato e viene ucciso. Altrimenti ritorna false. Quindi è possibile riavviare il servizio se il risultato è falsediverso, si è certi che il servizio corrente è stato interrotto. :) Sarebbe meglio se si dispone di uno sguardo a questo .


5

Un altro approccio con kotlin. Ispirato nelle risposte di altri utenti

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

Come estensione 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)

4

In kotlin puoi aggiungere una variabile booleana nell'oggetto companion e verificarne il valore da qualsiasi classe desideri:

companion object{
     var isRuning = false

}

Cambiarlo valore quando il servizio viene creato e distrutto

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

override fun onDestroy() {
    super.onDestroy()
    isRuning = false
    }

3

Nella sottoclasse del servizio Utilizzare un valore booleano statico per ottenere lo stato del servizio come mostrato di seguito.

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

3

Per kotlin, puoi usare il codice qui sotto.

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
}

Questa è un'ottima risposta per la scrittura di test, poiché è possibile utilizzarlo senza modificare il codice di lavoro.
Robert Liberatore,

2

La risposta di geekQ ma nella classe di Kotlin. Grazie 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 chiamata

isMyServiceRunning(NewService::class.java)

6
ActivityManager.getRunningServicesè obsoleto da Android O
Daniel Shatz il

1

Possono esserci diversi servizi con lo stesso nome di classe.

Ho appena creato due app. Il nome del pacchetto della prima app è com.example.mock. Ho creato un subpackage chiamato loremnell'app e un servizio chiamato Mock2Service. Quindi il suo nome completo ècom.example.mock.lorem.Mock2Service .

Quindi ho creato la seconda app e un servizio chiamato Mock2Service. Il nome del pacchetto della seconda app è com.example.mock.lorem. Anche il nome completo del servizio è com.example.mock.lorem.Mock2Service.

Ecco il mio output 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

Un'idea migliore è quella di confrontare le ComponentNameistanze a causa equals()dei ComponentNameconfronti sia dei nomi dei pacchetti che dei nomi delle classi. E non ci possono essere due app con lo stesso nome di pacchetto installato su un dispositivo.

Il metodo equals () di 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;
}

Nome del componente


1

Si prega di utilizzare questo codice

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

0

Questo vale maggiormente per il debug del servizio di intenti poiché generano un thread, ma può funzionare anche per i servizi regolari. Ho trovato questa discussione grazie a Binging

Nel mio caso, ho giocato con il debugger e ho trovato la vista thread. Sembra un po 'come l'icona del punto elenco in MS Word. Ad ogni modo, non è necessario essere in modalità debugger per usarlo. Fare clic sul processo e fare clic su quel pulsante. Qualsiasi Intent Services verrà visualizzato mentre sono in esecuzione, almeno sull'emulatore.


0

Se il servizio appartiene a un altro processo o APK, utilizzare la soluzione basata su ActivityManager.

Se hai accesso alla sua fonte, usa semplicemente la soluzione basata su un campo statico. Ma invece usando un booleano suggerirei di usare un oggetto Date. Mentre il servizio è in esecuzione, basta aggiornare il suo valore su "now" e quando termina impostarlo su null. Dall'attività è possibile verificare se il suo valore nullo o la data è troppo vecchia, il che significa che non è in esecuzione.

Puoi anche inviare una notifica di trasmissione dal tuo servizio indicando che sono in esecuzione ulteriori informazioni come i progressi.


0

All'interno di TheServiceClass definisci:

 public static Boolean serviceRunning = false;

Quindi in onStartCommand (...)

 public int onStartCommand(Intent intent, int flags, int startId) {

    serviceRunning = true;
    ...
}

 @Override
public void onDestroy()
{
    serviceRunning = false;

} 

Quindi, chiama if(TheServiceClass.serviceRunning == true)da qualsiasi classe.


4
Questo non funziona se il tuo servizio viene ucciso da Android.
Heisenberg,

@Heisenberg L'ho provato da solo. Sai perché no?
Tim

@Heisenberg quando la mia app viene uccisa dal sistema operativo, il servizio si riavvia e imposta il valore bool statico su true, ma quando lo ottiene segnala false
Tim

questo non funzionerà se chiami stopService. Almeno per i servizi Intent. onDestroy()verrà chiamato immediatamente, ma onHandleIntent()continuerà a funzionare
serggl

1
@Heisenberg Non uccidere il servizio a causa della memoria insufficiente significa anche uccidere il processo?
sviluppatore Android

0

uso semplice associa con non creare auto - vedi ps. e aggiorna ...

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

esempio :

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

perché non usare? getRunningServices ()

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

Nota: questo metodo è destinato esclusivamente al debug o all'implementazione delle interfacce utente del tipo di gestione del servizio.


ps. la documentazione di Android è fuorviante, ho aperto un problema sul tracker di Google per eliminare qualsiasi dubbio:

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

come possiamo vedere il servizio bind in realtà richiama una transazione tramite il binder ActivityManager attraverso i binder della cache del servizio - non tengo traccia di quale servizio sia responsabile dell'associazione ma come possiamo vedere il risultato per il bind è:

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

la transazione viene effettuata tramite raccoglitore:

ServiceManager.getService("activity");

Il prossimo:

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

questo è impostato in ActivityThread tramite:

 public final void bindApplication(...) {

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

questo viene chiamato in ActivityManagerService nel metodo:

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

poi:

 private HashMap<String, IBinder> getCommonServicesLocked() {

ma non c'è "attività" solo pacchetto finestra e allarme.

quindi dobbiamo tornare a chiamare:

 return getIServiceManager().getService(name);

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

questo fa chiamare attraverso:

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

che conduce a :

BinderInternal.getContextObject()

e questo è un metodo 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();

non ho tempo ora di scavare in c così fino a quando non seziono la chiamata di riposo sospendo la mia risposta.

ma il modo migliore per verificare se il servizio è in esecuzione è creare un bind (se il bind non viene creato, il servizio non esiste) e interrogare il servizio sul suo stato attraverso il bind (usando il flag interno memorizzato sullo stato).

aggiornamento il 23.06.2018

ho trovato quelli interessanti:

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

in breve :)

"Fornire un raccoglitore a un servizio già associato. Questo metodo è sincrono e non avvierà il servizio di destinazione se non è presente."

public IBinder peekService (Servizio intento, String resolvedType, String callingPackage) genera 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;
}

*


bindResult (valore di ritorno del metodo bindService) non viene considerato falso se il servizio non è in esecuzione.
Shangeeth Sivan,

0

La mia conversione kotlin delle ActivityManager::getRunningServicesrisposte basate. Metti questa funzione in un'attività-

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

-2

È possibile utilizzare queste opzioni dalle opzioni per gli sviluppatori Android per vedere se il servizio è ancora in esecuzione in background.

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.

-5

Vacci piano ragazzi ... :)

Penso che la soluzione più adatta sia tenere una coppia chiave-valore SharedPreferencesse il servizio è in esecuzione o meno.

La logica è molto semplice; in qualsiasi posizione desiderata nella tua classe di servizio; inserisci un valore booleano che fungerà da flag per te se il servizio è in esecuzione o meno. Quindi leggi questo valore dove vuoi nella tua applicazione.

Di seguito è riportato un codice di esempio che sto utilizzando nella mia app:

Nella mia classe di servizio (A service for Audio Stream), eseguo il codice seguente quando il servizio è attivo;

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

Quindi in qualsiasi attività della mia applicazione, sto controllando lo stato del servizio con l'aiuto del seguente codice;

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

Nessuna autorizzazione speciale, nessun loop ... Modo semplice, soluzione pulita :)

Se hai bisogno di ulteriori informazioni, consulta il link

Spero che sia di aiuto.


19
Solo che nessuno aggiornerà il valore per te quando uccideranno il servizio
Gunnar Forsgren - Mobimazione

quando si interrompe il servizio, verrà attivato onDestroy () e sarà possibile aggiornarne lo stato
Jongz Puangput

5
@JongzPuangput, onDestroynon viene sempre chiamato quando il servizio viene interrotto . Ad esempio, ho visto i miei servizi uccisi in situazioni di memoria insufficiente senza onDestroyessere chiamato.
Sam,

@Sam Allora come verrà chiamato?
Ruchir Baronia,

2
@RuchirBaronia Per quanto mi ricordo, semplicemente non vieni avvisato quando le tue cose vengono uccise. Credo che Android sia progettato per uccidere le app quando necessario e che le app dovrebbero essere progettate per aspettarsi di essere uccise in qualsiasi momento senza notifica.
Sam,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.