Как проверить, работает ли сервис на Android? - PullRequest
873 голосов
/ 01 марта 2009

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

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

Ответы [ 26 ]

1599 голосов
/ 07 мая 2011

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

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.

268 голосов
/ 04 марта 2009

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

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

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

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

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

72 голосов
/ 12 февраля 2011

Понял!

Вы ДОЛЖНЫ позвонить 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;
     }
}
53 голосов
/ 20 мая 2011

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

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

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

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

Чтобы сделать его еще чище, я предлагаю преобразовать службу в синглтон с очень и очень ленивым извлечением: т. Е. Вообще нет экземпляра singleton с помощью статических методов. Статический метод 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 с ограничениями, подчеркнутыми Питером-Яном Ван Робайсом.

24 голосов
/ 23 сентября 2010

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

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 (служба намерений) .

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

18 голосов
/ 14 июля 2014
    public boolean checkServiceRunning(){
         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;
    }
12 голосов
/ 13 июля 2016

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

Выписка из Android документы:

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

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

Service

@Override
public void onCreate() {
   LocalBroadcastManager
     .getInstance(this)
     .registerReceiver(new ServiceEchoReceiver(), new IntentFilter("ping"));
}

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;   
        }
    }
7 голосов
/ 16 сентября 2012

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

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

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

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

6 голосов
/ 29 октября 2016

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