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

Как проверить, работает ли фоновый сервис (на Android)?

Мне нужна активность Android, которая переключает состояние службы – она ​​позволяет включить ее, если она выключена и выключена, если она включена.

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

EDIT (для записи):

Вот решение, предлагаемое hackbod:

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

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

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

 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 events, или Binders или статические переменные, не будут надежно работать, потому что, как разработчик, которого вы никогда не знаете, когда Android решает убить ваш процесс или какие из упомянутых обратных вызовов вызываются или нет. Обратите внимание на столбец «killable» в таблице событий жизненного цикла в документации на Android.

Понял!

Вы ДОЛЖНЫ называть 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; } } 

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

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

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

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

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

Тогда было бы еще чище изменить шаблон дизайна singleton, чтобы переименовать запутанный метод 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 с ограничениями, подчеркнутыми Pieter-Jan Van Robays.

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

 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, если есть уже запущенная служба. Если нет, null будет возвращен.

См. Публичный аннотация ComponentName startService (услуга Intent) .

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

 public boolean check(){ ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE); for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { if ("com.example.yourpackagename.YourserviceName" .equals(service.service.getClassName())) { return true; } } return false; } 

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

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

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

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

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

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

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

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

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

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

Xamarin C # verison.

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

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

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

Опять же, еще одна альтернатива, которую люди могут найти более чистым, если они используют ожидающие намерения (например, с 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 – это константа, которую вы определяете конфиденциально в своем классе для определения ожидающих намерений, связанных с вашим сервисом.

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

  public static Boolean serviceRunning = false; 

Затем In onStartCommand (…)

  public int onStartCommand(Intent intent, int flags, int startId) { serviceRunning = true; ... } @Override public void onDestroy() { serviceRunning = false; } 

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

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

Я только что создал два приложения. Имя пакета для первого приложения – 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

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

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

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

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

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

Успокойся, ребята … 🙂

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

Нет специальных разрешений, нет циклов … Простой способ, чистое решение 🙂

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

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

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

Заказываемые трансляции (отправленные с помощью Context.sendOrderedBroadcast ) доставляются одному приемнику за раз. Поскольку каждый приемник выполняет свою очередь, он может передать результат следующему приемнику, или он может полностью прервать широковещательную передачу, чтобы он не передавался другим приемникам.

Моя идея, вероятно, взломала, но я подумал о «проверке» Service поскольку мы можем транслировать синхронно, мы можем транслировать и мгновенно получать результат

Service

BroadcastReceiver .

 @Override public void onCreate() { LocalBroadcastManager .getInstance(this) .registerReceiver(new ServiceEchoReceiver(), IntentFilter("echo"); } private class ServiceEchoReceiver{ public void onReceive (Context context, Intent intent) { LocalBroadcastManager .getInstance(this) .sendBroadcastSync(new Intent("echo")); } } 

Activity

  bool serviceRunning = false; protected void onCreate (Bundle savedInstanceState){ LocalBroadcastManager.getInstance(this).registerReceiver(echo); LocalBroadcastManager.getInstance(this).sendBroadcastSync(new Intent("echo")); if(!serviceRunning){ //try and run the service } } private BroadcastReceiver echo = new BroadcastReceiver(){ public void onReceive (Context context, Intent intent) { serviceRunning = true; } } 

Правильный способ проверить, работает ли служба, – просто спросить об этом. Внедрите 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; } } }; } 
 public static boolean isServiceRunning; @Override public int onStartCommand(Intent intent, int flags, int startId) { isServiceRunning = true; return START_NOT_STICKY; } @Override public void onDestroy() { super.onDestroy(); isServiceRunning = false; } 

ИЛИ

Если вы не хотите использовать static, тогда пользователь SharedPreferences

 @Override public int onStartCommand(Intent intent, int flags, int startId) { PrefManager.getPref().saveBoolean(AppConstants.Pref.IS_SERVICE_RUNNING, true); return START_NOT_STICKY; } @Override public void onDestroy() { super.onDestroy(); PrefManager.getPref().saveBoolean(AppConstants.Pref.IS_SERVICE_RUNNING, false); } 

PrefManager – мой класс singleton для управления всеми настройками.

Простое использование bind с не создавайте автоматическое

 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. 

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

Это работает для меня

  if(startService(new Intent(this, YourService.class)) != 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(); }