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

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

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

Ответы [ 26 ]

0 голосов
/ 08 февраля 2019

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

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)
        }
    }
}
0 голосов
/ 11 марта 2017

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

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, чтобы устранить любые сомнения:

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

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

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

обновление 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, Строка 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;
}

*

0 голосов
/ 03 июля 2013

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

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

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

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

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

0 голосов
/ 04 августа 2013

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

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

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

0 голосов
/ 04 августа 2012

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

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

0 голосов
/ 23 октября 2015

Внутри 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) из любого класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...