Как проверить, работает ли сервис на Android?

936

Как проверить, работает ли фоновая служба?

Я хочу, чтобы активность Android изменяла состояние службы - она ​​позволяла мне включать его, если он выключен, и выключен, если он включен.

пчелка
источник
2
Проверьте это немецкое руководство .
Маркус Перобнер
17
правильный ответ приведен ниже, а не помечен следующим: stackoverflow.com/a/5921190/2369122
toidiu
1
@toidiu Если это еще не сделано getRunningTasks(), вероятно, так и будет.
Кевин Крумвиде,
используя функцию getSystemService (), вы можете получить все запущенные сервисы. Зациклите его и проверьте, что ваш сервис существует в списке, здесь вы можете увидеть небольшой пример wiki.workassis.com/android-check-the-service-is-running
Bikesh M

Ответы:

292

У меня была такая же проблема не так давно. Так как мой сервис был локальным, я просто использовал статическое поле в классе сервиса для переключения состояния, как описано здесь hackbod

РЕДАКТИРОВАТЬ (для записи):

Вот решение, предложенное hackbod:

Если ваш клиентский и серверный код является частью одного и того же .apk, и вы привязываетесь к сервису с конкретным намерением (которое указывает точный класс сервиса), то вы можете просто сделать так, чтобы ваш сервис установил глобальную переменную, когда он выполняет Ваш клиент может проверить.

У нас намеренно нет API, чтобы проверить, работает ли служба, потому что, почти всегда, когда вы хотите сделать что-то подобное, вы сталкиваетесь с условиями гонки в своем коде.

miracle2k
источник
27
@Pacerier, решение, на которое вы ссылаетесь, требует запуска службы, и я думаю, что лучшее гибкое решение должно позволить вам проверить, работает ли служба, не запуская ее.
Том
17
Что если служба остановлена ​​системой, как вы обнаружите это и переключите свою переменную?
jmng
23
Когда приложение уничтожено, запущенная служба также уничтожается, но служба не onDestroy()вызывается. Таким образом, статическая переменная не может быть обновлена ​​в таком сценарии, что приводит к противоречивому поведению.
Файзал
5
@faizal Не будет ли статическая переменная также повторно инициализирована, таким образом, возвращая ей значение по умолчанию, которое указывает, что служба больше не работает?
PabloC
12
@faizal, локальный Сервис - это не отдельный процесс, поэтому, если сервис убит, приложение также будет убито.
Sever
1674

Я использую следующее изнутри деятельности:

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

И я называю это используя:

isMyServiceRunning(MyService.class)

Это работает надежно, поскольку основано на информации о запущенных сервисах, предоставляемых операционной системой Android через ActivityManager # getRunningServices .

Все подходы, использующие события onDestroy или onSometing, или Binder, или статические переменные, не будут работать надежно, потому что как разработчик вы никогда не знаете, когда Android решает убить ваш процесс или какой из упомянутых обратных вызовов вызван или нет. Обратите внимание на столбец «killable» в таблице событий жизненного цикла в документации Android.

geekQ
источник
85
Спасибо за это решение. Я хотел бы добавить: вместо этого «com.example.MyService» более элегантно использовать MyService.class.getName ()
peter.bartos
10
Лично я пошел с использованием статического поля. Хотя использование getRunningServices () является более надежным решением, я считаю, что в этих двух решениях есть компромисс между надежностью и эффективностью / простотой. Если вам нужно часто проверять, работает ли служба, циклически проходить через потенциально более 30 запущенных служб не очень идеально. Редкий случай уничтожения сервиса системой может быть обработан, возможно, блоком try / catch или с помощью START_STICKY.
Робиннес
80
Нет, это не правильный ответ, потому что он также написан в документации: «Примечание: этот метод предназначен только для отладки или реализации пользовательских интерфейсов типа управления сервисом». Это не предназначено для контроля потока!
2012 г.,
40
Люди находят элегантным пройти через все это, чтобы проверить, работает ли сервер?
Руи Маркиз
81
Запуск Android O , getRunningServicesявляется устаревшим. Этот ответ требует обновления для более новой версии.
poring91
75

Понял!

Вы ДОЛЖНЫ призвать startService()к тому, чтобы ваша служба была должным образом зарегистрирована, и передачи BIND_AUTO_CREATEне будет достаточно.

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

А теперь класс 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;
     }
}
Кевин Паркер
источник
Это перечислит только системные службы, нет ?! Так что мое местное служение исключено из списка, и я буду ложным; (
Ewoks
Это работает с внешними сервисами, для локальных сервисов это довольно очевидно, если вы работаете.
Кевин Паркер,
11
Извините, но я должен сказать, что это супер глупый ответ .. Почему это супер очевидно ?!
Ewoks
10
Непонятно, что ты имеешь в виду здесь ... Кто вообще говорил о крушении ?! Мне не интересно разбивать его. Служба может быть запущена, остановлена, может быть, это была намеренная служба, и она остановится сама по себе, когда это будет сделано ... Вопрос в том, как узнать, работает ли она по-прежнему или нет, например, через 3 минуты.
Эвокс
1
Неверно создавать впечатление, что связанная служба также должна быть запущена. NO. Bind auto create делает именно то, что говорит. Он создаст (и, следовательно, "запустит") службу, если служба еще не запущена.
Sreedevi J
57

Небольшое дополнение это:

Моя цель - знать, запущен ли сервис без его фактического запуска, если он не запущен.

Вызов bindService или вызов намерения, которое может быть перехвачено службой, не является хорошей идеей, поскольку он запустит службу, если она не запущена.

Итак, как предположил miracle2k, лучше всего иметь статическое поле в классе обслуживания, чтобы знать, запущена служба или нет.

Чтобы сделать его еще чище, я предлагаю преобразовать службу в синглтон с очень и очень ленивым извлечением: т. Е. Вообще нет экземпляра синглтона например , через статические методы. Статический метод getInstance вашего сервиса / синглтона просто возвращает экземпляр синглтона, если он был создан. Но на самом деле он не запускает и не создает экземпляр самого синглтона. Служба запускается только обычными методами запуска службы.

Тогда было бы еще чище изменить шаблон проектирования синглтона, чтобы переименовать запутанный метод getInstance во что-то вроде isInstanceCreated() : boolean метода.

Код будет выглядеть так:

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

Это элегантное решение, но оно актуально только в том случае, если у вас есть доступ к классу обслуживания и только для классов, кроме приложения / пакета службы. Если ваши занятия находятся за пределами приложения / пакета службы, вы можете запросить ActivityManager с ограничениями, подчеркнутыми Питером-Яном Ван Робайсом.

Snicolas
источник
32
Это ошибочно. onDestroy не гарантированно будет вызван.
Pacerier
8
Когда в системе недостаточно памяти, ваш сервис будет автоматически убит без вызова вашего onDestroy, поэтому я говорю, что это некорректно.
Pacerier
17
@Pacerier, но если система убивает процесс, флаг экземпляра все равно будет сброшен. Я предполагаю, что при следующей загрузке получателя (после публикации системы, убивающей службу) статический флаг 'instance' будет воссоздан как ноль.
Том
2
По крайней мере, лучше, чем перебирать все эти сервисы в isMyServiceRunning, который действительно затягивает, если выполняется при каждом повороте устройства :)
Гуннар Форсгрен - Mobimation
1
Ваша переменная экземпляра не должна быть объявлена ​​как final, иначе она не может быть установлена ​​или обнулена методами onCreate () или onDestroy ().
k2col
27

Вы можете использовать это (я еще не пробовал это, но я надеюсь, что это работает):

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

Метод startService возвращает объект ComponentName, если уже есть запущенная служба. Если нет, ноль будет возвращен.

Смотрите публичный абстрактный ComponentName startService (сервис Intent) .

Я думаю, это не похоже на проверку, потому что она запускает службу, поэтому вы можете добавить ее stopService(someIntent);под код.

Кинан Гебзе
источник
11
Не совсем то, что говорят документы. По вашей ссылке: «Возвращает Если служба запущена или уже запущена, возвращается ComponentName фактической службы, которая была запущена; иначе, если служба не существует, возвращается null».
Габриэль
Хорошее мышление ... но не вписывается в текущую ситуацию.
Code_Life
5
это не правильный путь, потому что когда триггер IDE if(startService(someIntent) != null)это проверит, IsserviceRunningно это также сыграет новый сервис.
Чинтан Хетия
Как уже говорилось, если вы остановите службу после этого контроля, это будет полезно для этой проблемы. Но зачем запускать и останавливать сервис даром?
Танер
6
это запустит службу, не так ли? Просто хочу проверить статус сервиса вместо его запуска ...
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
источник
21

Выдержка из документов Android :

Подобно sendBroadcast (Intent) , но если для Intent есть приемники, эта функция заблокирует их и немедленно отправит перед возвратом.

Думайте об этом взломе как о "пингующем"Service . Поскольку мы можем транслировать синхронно, мы можем транслировать и получать результат синхронно в потоке пользовательского интерфейса.

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

Конечно, во многих приложениях победителем является статическое логическое поле в сервисе, для которого задано значение truein Service.onCreate()и falsein, Service.onDestroy()потому что это намного проще.

peterchaula
источник
Это гораздо лучшее решение, чем принятое, которое не срабатывает, если Android убивает службу, так как метод глобальной переменной будет по-прежнему указывать на то, что служба работает, когда ее на самом деле больше нет. Этот трюк с синхронным пинг-понгом на самом деле является ЕДИНСТВЕННЫМ надежным методом проверки работоспособности службы. Это само по себе позволяет вам просто спросить службу, если она там есть. Если он отвечает, то служба работает и работает, если нет, то она либо не была запущена, либо была закрыта, либо программно, либо системой для восстановления памяти.
PhoenixRevealed
13

Я немного изменил одно из представленных выше решений, но передал класс вместо общего имени строки, чтобы быть уверенным в сравнении строк, исходящих из одного и того же метода. 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;
    }
}

а потом

Boolean isServiceRunning = ServiceTools.isServiceRunning(
                    MainActivity.this.getApplicationContext(),
                    BackgroundIntentService.class);
loretoparisi
источник
чтобы быть более строгим, вы можете изменить параметр класса наClass<? extends Service>
silentsudo
11

Правильный способ проверить, работает ли служба, - просто спросить ее. Внедрите в вашу службу BroadcastReceiver, который отвечает на запросы вашей работы. Зарегистрируйте BroadcastReceiver при запуске службы и отмените ее регистрацию при уничтожении службы. Исходя из вашей активности (или любого компонента), отправьте в службу локальную широковещательную рассылку, и если она ответит, вы знаете, что она работает. Обратите внимание на небольшую разницу между ACTION_PING и ACTION_PONG в приведенном ниже коде.

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
Мне действительно нравится этот подход. Это немного тяжелый код, но он всегда будет работать. Я не вижу, чтобы в ближайшем
будущем было прекращено вещание
8

Я просто хочу добавить примечание к ответу @Snicolas. Следующие шаги могут быть использованы для проверки остановки службы с / без вызова onDestroy().

  1. onDestroy() Вызов: перейдите в Настройки -> Приложение -> Запущенные сервисы -> Выберите и остановите сервис.

  2. onDestroy()не вызывается: выберите «Настройки» -> «Приложение» -> «Управление приложениями» -> выберите и «принудительно остановите» приложение, в котором запущена ваша служба. Однако, поскольку ваше приложение здесь остановлено, экземпляры службы также будут остановлены.

Наконец, я хотел бы отметить, что упомянутый там подход с использованием статической переменной в синглтон-классе работает для меня.

Павел
источник
7

onDestroy не всегда вызывается в сервисе, так что это бесполезно!

Например: просто запустите приложение еще раз с одним изменением из Eclipse. Приложение принудительно закрывается с помощью SIG: 9.

Кевин Паркер
источник
6

Прежде всего, вы не должны пытаться связаться с сервисом с помощью ActivityManager. (Обсуждается здесь )

Службы могут работать самостоятельно, быть привязаны к действию или к обоим. Чтобы проверить в Деятельности, работает ли ваша Служба или нет, нужно создать интерфейс (расширяющий Binder), в котором вы объявляете методы, понятные как Службе, так и Службе. Вы можете сделать это, создав собственный интерфейс, где вы объявляете, например, «isServiceRunning ()». Затем вы можете привязать свою активность к своей службе, запустить метод isServiceRunning (), служба проверит себя, работает ли она или нет, и вернет логическое значение вашей активности.

Вы также можете использовать этот метод, чтобы остановить службу или взаимодействовать с ней другим способом.

Я использовал этот учебник, чтобы узнать, как реализовать этот сценарий в моем приложении.

Питер-Ян Ван Робайс
источник
3
Эта дискуссия состоялась 12/26/07. Либо это июль этого года (то есть в будущем), либо это было до того, как Android стал публичным. В любом случае, это заставляет меня не верить этому.
Том
Это обсуждение с 26 декабря 2007 года. Они обсуждают предварительную версию, я думаю ( developer.android.com/sdk/OLD_RELEASENOTES.html#m3-rc37a ), которая была выпущена 14 декабря 2007 года.
ingh.am
6

Опять же, еще одна альтернатива, которую люди могут найти чище, если они используют ожидающие намерения (например, с помощью 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);
}

Где CODEконстанта, которую вы определяете в частном порядке в своем классе, чтобы определить ожидающие намерения, связанные с вашим сервисом.

Snicolas
источник
1
Совместить или обновить свой предыдущий ответ. Пожалуйста, воздержитесь от публикации более одного ответа на пост.
ChuongPham
Можно ли расширить этот ответ, то есть как связать значение CODE со службой?
Дейв Ноттаж
Где взять контекст?
базилик
6

Ниже приведен элегантный хак, который охватывает все Ifs. Это только для местных услуг.

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

А потом позже:

    if(AService.isServiceCreated()){
        ...
    }else{
        startService(...);
    }
TheRealChx101
источник
Единственная проблема заключается в том, что служба является Sticky и перезапускается сама. Вызов isServiceCreated () вернет false после повторного запуска службы, поскольку mInstance будет нулевым.
Mira_Cole
1
Не будет ли вызываться onCreate тогда, когда служба перезапускается сама?
TheRealChx101
6

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.
тестирование
5

Для приведенного здесь варианта использования мы можем просто использовать stopService()возвращаемое значение метода. Он возвращает, trueесли существует указанный сервис, и он убит. Остальное возвращается false. Таким образом, вы можете перезапустить службу, если в результате falseполучится уверенность, что текущая служба была остановлена. :) Было бы лучше, если вы посмотрите на это .

Рахул Равеендран
источник
5

Другой подход с использованием котлина. Вдохновленные другими пользователями ответы

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

Как расширение котлин

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

Применение

context.isMyServiceRunning(MyService::class.java)
Оскар Эмилио Перес Мартинес
источник
4

В kotlin вы можете добавить логическую переменную в объект-компаньон и проверить ее значение из любого класса, который вы хотите:

companion object{
     var isRuning = false

}

Измените это значение, когда служба создана и уничтожена

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

override fun onDestroy() {
    super.onDestroy()
    isRuning = false
    }
Мохамед Сэйбер
источник
3

В вашем подклассе службы используйте статическое логическое значение, чтобы получить состояние службы, как показано ниже.

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
источник
3

Для kotlin вы можете использовать приведенный ниже код.

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
}
П.А.Госай
источник
Это отличный ответ для написания тестов, поскольку вы можете использовать его без изменения рабочего кода.
Роберт Либераторе
2

Ответ geekQ, но в классе Kotlin. Спасибо 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
}

Звонок

isMyServiceRunning(NewService::class.java)
Луис Эдуардо Морено
источник
6
ActivityManager.getRunningServicesустарела с Android O
Даниэль Шац
1

Может быть несколько служб с одинаковым именем класса.

Я только что создал два приложения. Имя пакета первого приложения com.example.mock. Я создал подпакет loremв приложении и сервис под названием Mock2Service. Таким образом, его полностью квалифицированное имяcom.example.mock.lorem.Mock2Service .

Затем я создал второе приложение и сервис под названием Mock2Service . Название пакета второго приложения com.example.mock.lorem. Полное название услуги:com.example.mock.lorem.Mock2Service тоже.

Вот мой вывод 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

Лучшая идея - сравнить ComponentName примеров , поскольку equals()из ComponentNameсравнения как имена пакетов и имена классов. И на устройстве не может быть установлено два приложения с одинаковым именем пакета.

Метод equals () 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

Максим Дмитриев
источник
1

Пожалуйста, используйте этот код.

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

Это в большей степени относится к отладке Intent Service, поскольку они порождают поток, но могут работать и для обычных сервисов. Я нашел эту тему благодаря Binging

В моем случае я поиграл с отладчиком и нашел представление потока. Это выглядит как значок маркера в MS Word. В любом случае, вам не нужно находиться в режиме отладчика, чтобы использовать его. Нажмите на процесс и нажмите на эту кнопку. Любые Intent Services будут отображаться во время работы, по крайней мере, на эмуляторе.

Джо Планте
источник
0

Если служба принадлежит другому процессу или APK, используйте решение на основе ActivityManager.

Если у вас есть доступ к его источнику, просто используйте решение на основе статического поля. Но вместо использования логического значения я бы предложил использовать объект Date. Пока служба работает, просто измените ее значение на «сейчас», а когда оно завершится, установите значение «ноль». Из действия вы можете проверить, является ли его ноль или дата слишком старой, что будет означать, что она не запущена.

Вы также можете отправить широковещательное уведомление от вашего сервиса, указывающее, что он работает вместе с дополнительной информацией, такой как прогресс

FranMowinckel
источник
0

Внутри TheServiceClass определяют:

 public static Boolean serviceRunning = false;

Тогда в onStartCommand (...)

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

    serviceRunning = true;
    ...
}

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

} 

Затем позвоните if(TheServiceClass.serviceRunning == true)из любого класса.

Бадр
источник
4
Это не работает, если ваш сервис убит Android.
Гейзенберг
@ Heisenberg Я только что испытал это сам. Ты знаешь почему нет?
Тим
@ Heisenberg, когда мое приложение убито ОС, служба перезапускается и устанавливает статическое значение bool в значение true, но при получении оно выдает сообщение false
Тим
это не сработает, если вы позвоните stopService. По крайней мере, для услуг Intent. onDestroy()будет вызван немедленно, но onHandleIntent()все еще будет запущен
serggl
1
@ Heisenberg Не будет ли уничтожение сервиса из-за нехватки памяти также означать уничтожение процесса?
Android-разработчик
0

простое использование связать с не создавать авто - см. пс. и обновить ...

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

пример :

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

почему не используете? getRunningServices ()

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

Примечание: этот метод предназначен только для отладки или реализации пользовательских интерфейсов типа управления услугами.


пс. Документация Android вводит в заблуждение. Я открыл проблему на Google Tracker, чтобы устранить любые сомнения:

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

как мы можем видеть, служба связывания фактически вызывает транзакцию через привязку ActivityManager через привязки кеша службы - я отслеживаю, какая служба отвечает за привязку, но, как мы видим, результат для привязки:

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

Транзакция производится через связующее:

ServiceManager.getService("activity");

следующий:

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

это устанавливается в ActivityThread через:

 public final void bindApplication(...) {

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

это вызывается в ActivityManagerService в методе:

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

тогда:

 private HashMap<String, IBinder> getCommonServicesLocked() {

но нет "активности", только окно пакета и будильник ..

поэтому нам нужно вернуться, чтобы позвонить:

 return getIServiceManager().getService(name);

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

это делает вызов через:

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

что приводит к :

BinderInternal.getContextObject()

а это родной метод ....

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

у меня сейчас нет времени копаться в с, так что пока я не проанализирую покой, я приостановлю свой ответ.

но лучший способ проверить, работает ли сервис это создать привязку (если привязка не создана, служба не существует) - и запросить службу о ее состоянии через привязку (используя хранимый внутренний флаг для этого состояния).

обновление 23.06.2018

я нашел эти интересные:

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

короче говоря :)

«Предоставьте привязку для уже связанной службы. Этот метод является синхронным и не запускает целевую службу, если она отсутствует».

public IBinder peekService (Служба намерений, String resolvedType, String callPackage) выдает 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
источник
bindResult (возвращаемое значение метода bindService) не принимает значение false, если служба не запущена.
Шенгит Сиван
0

Мой kotlin преобразование ActivityManager::getRunningServicesоснованных ответов. Поместите эту функцию в активность

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

Вы можете использовать эту опцию из опций Android Developer, чтобы увидеть, работает ли ваша служба в фоновом режиме.

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
источник
-5

Полегче, ребята ... :)

Я думаю, что наиболее подходящим решением является удержание пары ключ-значение в SharedPreferencesтом, запущена служба или нет.

Логика очень прямая; на любой желаемой позиции в вашем классе обслуживания; укажите логическое значение, которое будет для вас указывать, работает ли служба. Затем прочитайте это значение везде, где вы хотите в своем приложении.

Пример кода, который я использую в своем приложении, приведен ниже:

В моем классе обслуживания (услуга для аудиопотока) я выполняю следующий код, когда служба работает;

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

Затем в любой активности моего приложения я проверяю статус сервиса с помощью следующего кода;

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

Никаких специальных разрешений, никаких петель ... Простой способ, чистое решение :)

Если вам нужна дополнительная информация, пожалуйста, обратитесь по ссылке

Надеюсь это поможет.

Танер
источник
19
Только то, что никто не будет обновлять значение для вас, когда они убивают службу
Гуннар Форсгрен - Mobimation
когда убивают сервис onDestroy () срабатывает и можно обновлять его состояние
Jongz Puangput
5
@JongzPuangput, onDestroyне всегда вызывается, когда служба убита. Например, я видел, как мои службы убивали в ситуациях с нехваткой памяти без onDestroyвызова.
Сэм
@Sam Тогда что будет называться?
Ruchir Baronia
2
@RuchirBaronia Насколько я помню, вы просто не получаете уведомления, когда ваши вещи убивают. Я считаю, что Android предназначен для уничтожения приложений по мере необходимости, и приложения должны быть разработаны так, чтобы их можно было ожидать в любой момент без уведомления.
Сэм