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.
getRunningTasks()
, prawdopodobnie tak będzie.
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.
getRunningTasks()
, prawdopodobnie tak będzie.
Odpowiedzi:
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.
onDestroy()
nie jest wywoływana. Tak więc zmiennej statycznej nie można zaktualizować w takim scenariuszu, co spowoduje niespójne zachowanie.
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.
getRunningServices
jest przestarzała. Ta odpowiedź wymaga aktualizacji do nowszej wersji.
Rozumiem!
Państwo musi zadzwonić startService()
do usługi mają być prawidłowo zarejestrowane i mijania BIND_AUTO_CREATE
nie 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;
}
}
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() : boolean
metody.
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.
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.
if(startService(someIntent) != null)
to sprawdzi, IsserviceRunning
ale to również uruchomi nową usługę.
/**
* 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;
}
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 true
in Service.onCreate()
i false
in, Service.onDestroy()
ponieważ jest o wiele prostsza.
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);
Class<? extends Service>
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;
}
}
};
}
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()
.
onDestroy()
o nazwie: Przejdź do Ustawienia -> Aplikacja -> Uruchamianie usług -> Wybierz i zatrzymaj usługę.
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.
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.
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.
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 CODE
jest stała, którą definiujesz prywatnie w swojej klasie, aby zidentyfikować oczekujące zamiary związane z twoją usługą.
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(...);
}
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;
}
GetSystemService
.
W podanym tutaj przypadku użycia możemy po prostu użyć stopService()
wartości zwracanej przez metodę. Zwraca, true
jeś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 false
inny, masz pewność, że bieżąca usługa została zatrzymana. :) Byłoby lepiej, jeśli spojrzeć na to .
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)
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
}
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)
}
}
}
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
}
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)
ActivityManager.getRunningServices
jest przestarzałe, ponieważ Android O
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 lorem
w 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 ComponentName
przypadkach, 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;
}
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;
}
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.
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.
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.
stopService
. Przynajmniej w przypadku usług Intent. onDestroy()
zostanie wezwany natychmiast, ale onHandleIntent()
nadal będzie działał
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).
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;
}
*
Moja konwersja kotlin na ActivityManager::getRunningServices
podstawie 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
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.
Spokojnie chłopaki ... :)
Myślę, że najbardziej odpowiednim rozwiązaniem jest utrzymanie pary klucz-wartość, SharedPreferences
czy 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.
onDestroy
nie 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 onDestroy
wywołania.