Jak sprawdzić, czy usługa działa na Androidzie?

936

Jak sprawdzić, czy usługa w tle jest uruchomiona?

Chcę aktywność Androida, która przełącza stan usługi - pozwala mi ją włączać, jeśli jest wyłączona, i wyłączać, jeśli jest włączona.

pszczoła
źródło
2
Sprawdź ten niemiecki przewodnik .
Markus Peröbner
17
poprawna odpowiedź znajduje się poniżej, a nie ta oznaczona: stackoverflow.com/a/5921190/2369122
toidiu
1
@toidiu Jeśli to jeszcze nie było osłabione getRunningTasks(), prawdopodobnie tak będzie.
Kevin Krumwiede
za pomocą funkcji getSystemService () można pobrać wszystkie uruchomione usługi. przejrzyj go i sprawdź, czy twoja usługa istnieje na liście. Możesz zobaczyć małą próbkę wiki.workassis.com/android-check-the-service-is-running
Bikesh M

Odpowiedzi:

292

Niedawno miałem ten sam problem. Ponieważ moja usługa była lokalna, po prostu użyłem pola statycznego w klasie usługi do przełączania stanu, jak opisano tutaj przez hackbod

EDYCJA (dla zapisu):

Oto rozwiązanie zaproponowane przez hackbod:

Jeśli kod klienta i serwera jest częścią tego samego pliku .apk i wiążesz się z usługą z konkretnym zamiarem (takim, który określa dokładną klasę usługi), możesz po prostu ustawić swoją usługę jako zmienną globalną podczas jej działania twój klient może to sprawdzić.

Celowo nie dysponujemy interfejsem API do sprawdzania, czy usługa jest uruchomiona, ponieważ prawie zawsze, gdy chcesz zrobić coś takiego, w twoim kodzie występują warunki wyścigu.

miracle2k
źródło
27
@Pacerier, rozwiązanie, do którego się odwołujesz, wymaga uruchomienia usługi i uważam, że najlepsze elastyczne rozwiązanie powinno pozwolić ci sprawdzić, czy usługa działa bez jej uruchamiania.
Tom
17
Co jeśli usługa zostanie zatrzymana przez system, jak to wykryć i przełączyć zmienną?
jmng
23
Po zabiciu aplikacji usługa, którą uruchomiła, zostaje również zabita, ale usługa onDestroy()nie jest wywoływana. Tak więc zmiennej statycznej nie można zaktualizować w takim scenariuszu, co spowoduje niespójne zachowanie.
faizal
5
@faizal Czy zmienna statyczna również nie zostanie ponownie zainicjalizowana, ustawiając w ten sposób wartość domyślną wskazującą, że usługa nie jest już uruchomiona?
PabloC,
12
@faizal, usługa lokalna nie jest osobnym procesem, więc jeśli usługa zostanie zabita, aplikacja również zabije.
Sever
1674

Używam następujących czynności z działania:

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

I nazywam to za pomocą:

isMyServiceRunning(MyService.class)

Działa to niezawodnie, ponieważ opiera się na informacjach o uruchomionych usługach dostarczanych przez system operacyjny Android za pośrednictwem ActivityManager # getRunningServices .

Wszystkie podejścia wykorzystujące zdarzenia onDestroy lub onSometing, Bindery lub zmienne statyczne nie będą działać niezawodnie, ponieważ jako programista nigdy nie wiadomo, kiedy Android decyduje się zabić proces lub które z wymienionych wywołań zwrotnych są wywoływane, czy nie. Zwróć uwagę na kolumnę „możliwą do przełknięcia” w tabeli zdarzeń cyklu życia w dokumentacji systemu Android.

geekQ
źródło
85
Dzięki za to rozwiązanie. Chciałbym dodać: Zamiast tego „com.example.MyService” jest bardziej elegancki w użyciu MyService.class.getName ()
peter.bartos
10
Osobiście korzystałem z pola statycznego. Chociaż użycie getRunningServices () jest bardziej niezawodnym rozwiązaniem, uważam, że w tych dwóch rozwiązaniach występuje kompromis między wytrzymałością a wydajnością / prostotą. Jeśli musisz często sprawdzać, czy usługa jest uruchomiona, przechodzenie przez potencjalnie ponad 30 działających usług nie jest zbyt idealne. Rzadki przypadek zniszczenia usługi przez system może być rozwiązany przez blok try / catch lub za pomocą START_STICKY.
robguinness
80
Nie, to nie jest poprawna odpowiedź, ponieważ jest również napisana w dokumentacji: „Uwaga: ta metoda jest przeznaczona tylko do debugowania lub implementacji interfejsów użytkownika typu zarządzania usługami”. Nie jest przeznaczony do sterowania przepływem!
seb
40
Ludzie uważają za elegancko przejść przez to wszystko, aby sprawdzić, czy serwer działa?
Rui Marques,
79
Począwszy Android O , getRunningServicesjest przestarzała. Ta odpowiedź wymaga aktualizacji do nowszej wersji.
poring91
75

Rozumiem!

Państwo musi zadzwonić startService()do usługi mają być prawidłowo zarejestrowane i mijania BIND_AUTO_CREATEnie wystarczą.

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

A teraz klasa 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
źródło
Spowoduje to wyświetlenie tylko usług systemowych, nie ?! Więc moja lokalna usługa jest wykluczona z listy i popełniam błąd; (
Ewoks
Działa to z usługami zewnętrznymi, ponieważ usługi lokalne są dość oczywiste, jeśli używasz.
Kevin Parker
11
Przepraszam, ale muszę powiedzieć, że to super głupia odpowiedź .. Dlaczego to jest super oczywiste ?!
Ewoks
10
Nie wiem, co masz na myśli ... Kto w ogóle mówił o awarii ?! Nie interesuje mnie zawieszanie go. Usługa może zostać uruchomiona, zatrzymana, być może była to zamierzona usługa i po zakończeniu zostanie zatrzymana samodzielnie. Pytanie brzmi, jak się dowiedzieć, czy nadal działa, czy nie po 3 minutach.
Ewoks,
1
Nie można sprawiać wrażenia, że ​​należy również uruchomić usługę powiązaną. NIE. Automatyczne tworzenie powiązań robi dokładnie to, co mówi. Stworzy (a zatem „uruchomi”) usługę, jeśli usługa jeszcze nie działa.
Sreedevi J
57

Małe uzupełnienie to:

Moim celem jest wiedzieć, czy usługa jest uruchomiona bez faktycznego uruchomienia, jeśli nie jest uruchomiona.

Wywołanie bindService lub zamiaru, który może zostać przechwycony przez usługę, nie jest dobrym pomysłem, ponieważ uruchomi usługę, jeśli nie jest uruchomiona.

Tak więc, jak sugerował miracle2k, najlepiej jest mieć pole statyczne w klasie usług, aby wiedzieć, czy usługa została uruchomiona, czy nie.

Aby uczynić go jeszcze czystszym, proponuję przekształcić usługę w singleton z bardzo bardzo leniwym pobieraniem: to znaczy, nie ma instancji w żadnej instancji singletonu metodami statycznymi. Statyczna metoda getInstance usługi / singletonu po prostu zwraca instancję singletonu, jeśli została utworzona. Ale tak naprawdę nie uruchamia ani nie zinstytucjonalizuje samego singletona. Usługa jest uruchamiana tylko za pomocą normalnych metod uruchamiania usługi.

Byłoby wtedy jeszcze czystsze zmodyfikowanie wzorca projektowego singletonu, aby zmienić nazwę mylącej metody getInstance na coś w rodzaju isInstanceCreated() : booleanmetody.

Kod będzie wyglądał następująco:

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

To rozwiązanie jest eleganckie, ale jest istotne tylko wtedy, gdy masz dostęp do klasy usługi i tylko dla klas znajduje się aplikacja / pakiet usługi. Jeśli Twoje klasy znajdują się poza aplikacją / pakietem usług, możesz przesłać zapytanie do ActivityManager z ograniczeniami podkreślonymi przez Pietera-Jana Van Robaysa.

Snicolas
źródło
32
To jest wadliwe. Nie można zagwarantować, że onDestroy zostanie wywołany.
Pacerier
8
Kiedy system ma mało pamięci, twoja usługa zostanie automatycznie zabita bez połączenia z twoim onDestroy, dlatego mówię, że jest to wadliwe.
Pacerier
17
@Pacerier, ale jeśli system zabije proces, flaga instancji nadal zostanie zresetowana. Zgaduję, że kiedy następny odbiornik zostanie załadowany (po zabiciu usługi przez system) statyczna „instancja” flagi zostanie odtworzona jako null.
Tom
2
Przynajmniej lepiej niż iteracja po tych wszystkich usługach w isMyServiceRunning, która naprawdę opóźnia rzeczy, jeśli jest wykonywana przy każdym obrocie urządzenia :)
Gunnar Forsgren - Mobimation
1
Zmienna instancji nie powinna być deklarowana jako końcowa, w przeciwnym razie nie można jej ustawić ani zerować za pomocą metod onCreate () lub onDestroy ().
k2col
27

Możesz użyć tego (jeszcze tego nie próbowałem, ale mam nadzieję, że to zadziała):

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

Metoda startService zwraca obiekt ComponentName, jeśli istnieje już uruchomiona usługa. Jeśli nie, zwracana jest wartość null.

Zobacz publiczne streszczenie ComponentName startService (usługa zamierzona) .

Myślę, że to nie jest jak sprawdzanie, ponieważ uruchamia usługę, więc możesz dodać stopService(someIntent);kod.

Keenan Gebze
źródło
11
Nie do końca to, co mówią doktorzy. Zgodnie z linkiem: „Zwraca Jeśli usługa jest uruchamiana lub jest już uruchomiona, zwracana jest nazwa ComponentName faktycznie uruchomionej usługi; w przeciwnym razie, jeśli usługa nie istnieje, zwracana jest wartość null”.
Gabriel
Fajne myślenie ... ale nie pasuje do obecnej sytuacji.
Code_Life
5
to nie jest właściwy sposób, ponieważ kiedy wyzwalacz IDE if(startService(someIntent) != null)to sprawdzi, IsserviceRunningale to również uruchomi nową usługę.
Chintan Khetiya
Jak stwierdzono, jeśli zatrzymasz usługę po tej kontroli, przyda się ten problem. Ale po co uruchamiać i zatrzymywać usługę za darmo?
Taner
6
to uruchomi usługę, prawda? Chcę tylko sprawdzić status usługi zamiast ją uruchamiać ...
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
źródło
21

Wyciąg z dokumentów Androida :

Podobnie jak sendBroadcast (Intent) , ale jeśli są jakieś odbiorniki dla Intent, ta funkcja zablokuje się i natychmiast wyśle ​​je przed powrotem.

Pomyśl o tym hacku jako o „pingowaniu”Service . Ponieważ możemy nadawać synchronicznie, możemy nadawać i uzyskiwać wyniki synchronicznie w wątku interfejsu użytkownika.

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

Zwycięzcą w wielu aplikacjach jest oczywiście statyczne pole boolowskie w usłudze, która jest ustawiona na truein Service.onCreate()i falsein, Service.onDestroy()ponieważ jest o wiele prostsza.

peterchaula
źródło
Jest to znacznie lepsze rozwiązanie niż zaakceptowane, które kończy się niepowodzeniem, jeśli Android zabije usługę, ponieważ metoda zmiennej globalnej nadal wskazywałaby, że usługa działa, gdy w rzeczywistości już nie jest. Ta synchroniczna sztuczka polegająca na wysyłaniu ping-ponga jest w rzeczywistości JEDYNĄ niezawodną metodą sprawdzania, czy usługa działa. Samo to pozwala po prostu ZAPYTAĆ usługę, jeśli tam jest. Jeśli odpowie, oznacza to, że usługa jest aktywna i uruchomiona, jeśli nie, nie została uruchomiona lub została zamknięta, programowo lub przez system w celu odzyskania pamięci.
PhoenixRevealed
13

Lekko zmodyfikowałem jedno z przedstawionych powyżej rozwiązań, ale przekazałem klasę zamiast ogólnej nazwy łańcucha, aby mieć pewność, że porównasz łańcuchy wychodzące z tej samej metody 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;
    }
}

i wtedy

Boolean isServiceRunning = ServiceTools.isServiceRunning(
                    MainActivity.this.getApplicationContext(),
                    BackgroundIntentService.class);
loretoparisi
źródło
aby być bardziej Class<? extends Service>
konsekwentnym
11

Właściwym sposobem sprawdzenia, czy usługa jest uruchomiona, jest po prostu zapytanie. Zaimplementuj BroadcastReceiver w swojej usłudze, który reaguje na pingi z twoich działań. Zarejestruj BroadcastReceiver przy uruchomieniu usługi i wyrejestruj go, gdy usługa zostanie zniszczona. Ze swojej aktywności (lub dowolnego komponentu) wyślij lokalny zamiar transmisji do usługi, a jeśli odpowie, wiesz, że jest uruchomiony. Zwróć uwagę na subtelną różnicę między ACTION_PING i ACTION_PONG w poniższym kodzie.

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.
źródło
1
Właściwie podoba mi się to podejście. Jest to trochę ciężkie pod względem kodu, ale zawsze będzie działać. W najbliższym czasie nie widzę, aby
zamiary
8

Chcę tylko dodać notatkę do odpowiedzi @Snicolas. Poniższych kroków można użyć do sprawdzenia zatrzymania usługi z / bez połączenia onDestroy().

  1. onDestroy() o nazwie: Przejdź do Ustawienia -> Aplikacja -> Uruchamianie usług -> Wybierz i zatrzymaj usługę.

  2. onDestroy()brak połączenia: Wybierz Ustawienia -> Aplikacja -> Zarządzaj aplikacjami -> Wybierz i „Wymuś zatrzymanie” aplikacji, w której działa usługa. Ponieważ jednak aplikacja zostanie zatrzymana, więc instancje usługi również zostaną zatrzymane.

Na koniec chciałbym wspomnieć, że podejście wspomniane tam przy użyciu zmiennej statycznej w klasie singleton działa dla mnie.

Paweł
źródło
7

onDestroy nie zawsze jest wywoływany w serwisie, więc jest to bezużyteczne!

Na przykład: Wystarczy uruchomić aplikację ponownie z jedną zmianą w stosunku do Eclipse. Aplikacja jest zamykana przy użyciu SIG: 9.

Kevin Parker
źródło
6

Przede wszystkim nie możesz próbować uzyskać dostępu do usługi za pomocą menedżera aktywności ActivityManager. (Omówiono tutaj )

Usługi mogą być uruchamiane samodzielnie, być powiązane z Działaniem lub jednym i drugim. Sposobem na sprawdzenie działania, jeśli usługa jest uruchomiona, jest utworzenie interfejsu (który rozszerza Binder), w którym deklarujesz metody zrozumiałe zarówno dla działania, jak i usługi. Możesz to zrobić, tworząc własny interfejs, w którym deklarujesz na przykład „isServiceRunning ()”. Następnie możesz powiązać działanie z usługą, uruchomić metodę isServiceRunning (), usługa sprawdzi, czy jest uruchomiona, i zwróci wartość logiczną do działania.

Możesz także użyć tej metody, aby zatrzymać Usługę lub wejść w interakcję z nią w inny sposób.

Skorzystałem z tego samouczka, aby dowiedzieć się, jak zaimplementować ten scenariusz w mojej aplikacji.

Pieter-Jan Van Robays
źródło
3
Ta dyskusja odbyła się w dniu „12/26/07”. Albo to lipiec tego roku (tj. W przyszłości), albo jeszcze zanim Android był jeszcze publiczny. Tak czy inaczej sprawia, że ​​nie ufam.
Tom
Dyskusja trwa od 26 grudnia 2007 r. Sądzę, że omawiają wersję przedpremierową ( developer.android.com/sdk/OLD_RELEASENOTES.html#m3-rc37a ), która została wydana 14 grudnia 2007 r.
ingh.am
6

Znowu kolejna alternatywa, którą ludzie mogą znaleźć czystszą, jeśli użyją oczekujących zamiarów (na przykład z 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);
}

Gdzie CODEjest stała, którą definiujesz prywatnie w swojej klasie, aby zidentyfikować oczekujące zamiary związane z twoją usługą.

Snicolas
źródło
1
Połącz lub zaktualizuj swoją poprzednią odpowiedź. Powstrzymaj się od zamieszczania więcej niż jednej odpowiedzi na post.
ChuongPham
Czy można rozszerzyć tę odpowiedź, tj. Jak powiązać wartość KODU z usługą?
Dave Nottage
Skąd wziąć kontekst?
bazylia
6

Poniżej znajduje się elegancki hack, który obejmuje wszystkie Ifs. Dotyczy to tylko usług lokalnych.

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

A później:

    if(AService.isServiceCreated()){
        ...
    }else{
        startService(...);
    }
TheRealChx101
źródło
Jedynym problemem jest to, że usługa jest usługą lepką i uruchamia się ponownie. Wywołanie isServiceCreated () zwróci false po ponownym uruchomieniu usługi, ponieważ mInstance będzie miało wartość null.
Mira_Cole
1
Czy funkcja onCreate nie zostanie wywołana, gdy usługa uruchomi się ponownie?
TheRealChx101
6

Wersja 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
źródło
Potrzebujesz „Kontekstu” dla GetSystemService.
testowanie
5

W podanym tutaj przypadku użycia możemy po prostu użyć stopService()wartości zwracanej przez metodę. Zwraca, truejeśli istnieje określona usługa i zostanie zabita. W przeciwnym razie powróci false. Możesz więc ponownie uruchomić usługę, jeśli wynik jest falseinny, masz pewność, że bieżąca usługa została zatrzymana. :) Byłoby lepiej, jeśli spojrzeć na to .

Rahul Raveendran
źródło
5

Kolejne podejście z wykorzystaniem kotlina. Inspirowane odpowiedziami innych użytkowników

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

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

Stosowanie

context.isMyServiceRunning(MyService::class.java)
Oscar Emilio Perez Martinez
źródło
4

W kotlin możesz dodać zmienną boolowską do obiektu towarzyszącego i sprawdzić jej wartość z dowolnej klasy:

companion object{
     var isRuning = false

}

Zmień wartość, gdy usługa zostanie utworzona i zniszczona

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

override fun onDestroy() {
    super.onDestroy()
    isRuning = false
    }
Mohamed Sabre
źródło
3

W swojej podklasie usługi użyj statycznego boolean, aby uzyskać stan usługi, jak pokazano poniżej.

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
źródło
3

W przypadku kotlin możesz użyć poniższego kodu.

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
źródło
To świetna odpowiedź na pisanie testów, ponieważ można jej używać bez zmiany działającego kodu.
Robert Liberatore,
2

Odpowiedź geekQ, ale w klasie Kotlin. Dzięki 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
}

Telefon

isMyServiceRunning(NewService::class.java)
Luis Eduardo Moreno
źródło
6
ActivityManager.getRunningServicesjest przestarzałe, ponieważ Android O
Daniel Shatz
1

Może istnieć kilka usług o tej samej nazwie klasy.

Właśnie stworzyłem dwie aplikacje. Nazwa pakietu pierwszej aplikacji to com.example.mock. Utworzyłem sub-pakiet o nazwie loremw aplikacji i usługę o nazwie Mock2Service. Więc jego w pełni kwalifikowana nazwa to com.example.mock.lorem.Mock2Service.

Następnie stworzyłem drugą aplikację i usługę o nazwie Mock2Service. Nazwa pakietu drugiej aplikacji to com.example.mock.lorem. Pełna nazwa usługi tocom.example.mock.lorem.Mock2Service .

Oto mój wynik 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

Lepszym pomysłem jest porównanie ComponentNameprzypadkach, ponieważ equals()wComponentName porównuje obie nazwy pakietów i nazwy klas. I na urządzeniu nie mogą być zainstalowane dwie aplikacje o tej samej nazwie pakietu.

Metoda equals () z 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

Maksim Dmitriew
źródło
1

Proszę użyć tego kodu.

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
źródło
0

Dotyczy to bardziej debugowania usługi intent, ponieważ odradzają one wątek, ale mogą również działać w przypadku zwykłych usług. Znalazłem ten wątek dzięki Binging

W moim przypadku bawiłem się debuggerem i znalazłem widok wątku. Wygląda to jak ikona punktora w MS Word. W każdym razie nie musisz być w trybie debugowania, aby z niego korzystać. Kliknij proces i kliknij ten przycisk. Wszelkie usługi zamierzone pojawią się podczas ich działania, przynajmniej na emulatorze.

Joe Plante
źródło
0

Jeśli usługa należy do innego procesu lub pakietu APK, użyj rozwiązania opartego na menedżerze aktywności ActivityManager.

Jeśli masz dostęp do jego źródła, skorzystaj z rozwiązania opartego na polu statycznym. Zamiast tego używając wartości logicznej sugerowałbym użycie obiektu Date. Gdy usługa jest uruchomiona, po prostu zaktualizuj jej wartość na „teraz”, a kiedy zakończy, ustaw ją na null. Na podstawie działania możesz sprawdzić, czy jego wartość zerowa lub data jest za stara, co oznacza, że ​​nie jest uruchomiona.

Możesz również wysłać powiadomienie o transmisji ze swojej usługi, wskazując, że działa on wraz z dalszymi informacjami, takimi jak postęp.

FranMowinckel
źródło
0

Wewnątrz klasy TheServiceClass zdefiniuj:

 public static Boolean serviceRunning = false;

Następnie w onStartCommand (...)

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

    serviceRunning = true;
    ...
}

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

} 

Następnie zadzwoń if(TheServiceClass.serviceRunning == true)z dowolnej klasy.

Badr
źródło
4
To nie działa, jeśli usługa zostanie zabita przez Androida.
Heisenberg
@Heisenberg Właśnie tego doświadczyłem. Czy wiesz dlaczego nie?
Tim
@Heisenberg, gdy moja aplikacja została zabita przez system operacyjny, usługa uruchamia się ponownie i ustawia statyczne bool na true, ale po otrzymaniu zgłasza fałsz
Tim
to nie zadziała, jeśli zadzwonisz stopService. Przynajmniej w przypadku usług Intent. onDestroy()zostanie wezwany natychmiast, ale onHandleIntent()nadal będzie działał
serggl
1
@Heisenberg Czy zabicie usługi z powodu małej ilości pamięci nie oznacza również zabicia procesu?
programista Androida
0

proste użycie powiązania z nie twórz auto - patrz ps. i zaktualizuj ...

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

przykład:

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

dlaczego nie używasz? getRunningServices ()

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

Uwaga: ta metoda jest przeznaczona wyłącznie do debugowania lub implementacji interfejsów użytkownika typu zarządzania usługami.


ps. Dokumentacja Androida wprowadza w błąd. W celu wyeliminowania wątpliwości otworzyłem problem w Google Tracker:

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

jak widzimy, usługa powiązania faktycznie wywołuje transakcję za pomocą segregatora ActivityManager za pośrednictwem segregatorów pamięci podręcznej usługi - śledzę, która usługa jest odpowiedzialna za wiązanie, ale jak widzimy, wynik dla wiązania jest następujący:

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

transakcja odbywa się za pomocą spoiwa:

ServiceManager.getService("activity");

Kolejny:

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

jest to ustawiane w ActivityThread poprzez:

 public final void bindApplication(...) {

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

jest to wywoływane w ActivityManagerService w metodzie:

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

następnie:

 private HashMap<String, IBinder> getCommonServicesLocked() {

ale nie ma pakietu okien i alarmu tylko dla „aktywności”

więc musimy wrócić, aby zadzwonić:

 return getIServiceManager().getService(name);

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

dzięki temu połączenia:

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

który prowadzi do :

BinderInternal.getContextObject()

i to jest natywna metoda ....

  /**
     * 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();

nie mam teraz czasu, żeby wykopać c, więc dopóki nie rozbiorę wezwania na odpoczynek, zawieszam swoją odpowiedź.

ale najlepszym sposobem sprawdzenia, czy usługa jest uruchomiona, jest utworzenie powiązania (jeśli powiązanie nie zostało utworzone, usługa nie istnieje) - i zapytanie usługi o jej stan za pośrednictwem powiązania (przy użyciu zapisanej flagi wewnętrznej na nim).

aktualizacja 23.06.2018

znalazłem te interesujące:

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

w skrócie :)

„Zapewnij spoiwo do już powiązanej usługi. Ta metoda jest synchroniczna i nie uruchomi usługi docelowej, jeśli nie jest obecna.”

public IBinder peekService (usługa zamiaru, ciąg znaków resolvedType, ciąg wywołujący pakiet) zgłasza 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
źródło
bindResult (zwracana wartość metody bindService) nie ma wartości false, jeśli usługa nie jest uruchomiona.
Shangeeth Sivan
0

Moja konwersja kotlin na ActivityManager::getRunningServicespodstawie odpowiedzi. Umieść tę funkcję w działaniu

private fun isMyServiceRunning(serviceClass: Class<out Service>) =
    (getSystemService(ACTIVITY_SERVICE) as ActivityManager)
        .getRunningServices(Int.MAX_VALUE)
        ?.map { it.service.className }
        ?.contains(serviceClass.name) ?: false
Gulszan
źródło
-2

Możesz użyć tej opcji z opcji dla programistów Androida, aby sprawdzić, czy Twoja usługa nadal działa w tle.

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
źródło
-5

Spokojnie chłopaki ... :)

Myślę, że najbardziej odpowiednim rozwiązaniem jest utrzymanie pary klucz-wartość, SharedPreferencesczy usługa jest uruchomiona, czy nie.

Logika jest bardzo prosta; na dowolnym pożądanym miejscu w klasie usług; wstaw wartość logiczną, która będzie działać jako flaga dla tego, czy usługa jest uruchomiona, czy nie. Następnie przeczytaj tę wartość w dowolnym miejscu w aplikacji.

Przykładowy kod, którego używam w mojej aplikacji, znajduje się poniżej:

W mojej klasie usług (usługa dla strumienia audio) wykonuję następujący kod, gdy usługa jest uruchomiona;

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

Następnie w dowolnej aktywności mojej aplikacji sprawdzam status usługi za pomocą następującego kodu;

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

Bez specjalnych uprawnień, bez pętli ... Łatwy sposób, czyste rozwiązanie :)

Jeśli potrzebujesz dodatkowych informacji, zapoznaj się z linkiem

Mam nadzieję że to pomoże.

Taner
źródło
19
Tyle tylko, że nikt nie zaktualizuje wartości dla ciebie, gdy zabiją usługę
Gunnar Forsgren - Mobimation
po zabiciu usługi uruchomi się onDestroy () i możliwe jest zaktualizowanie jej stanu
Jongz Puangput
5
@JongzPuangput, onDestroynie zawsze jest wywoływany po zabiciu usługi. Na przykład widziałem, jak moje usługi są zabijane w sytuacjach braku pamięci bez onDestroywywołania.
Sam
@Sam Więc jak się nazywa?
Ruchir Baronia
2
@RuchirBaronia O ile pamiętam, po prostu nie otrzymujesz powiadomienia o zabiciu twoich rzeczy. Wierzę, że Android ma na celu zabijanie aplikacji w razie potrzeby, a aplikacje powinny być zaprojektowane tak, aby oczekiwać zabicia w dowolnym momencie bez powiadomienia.
Sam