如何检查服务是否在 Android 上运行?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/600207/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 02:19:50  来源:igfitidea点击:

How to check if a service is running on Android?

androidandroid-service

提问by Bee

How do I check if a background service is running?

如何检查后台服务是否正在运行?

I want an Android activity that toggles the state of the service -- it lets me turn it on if it is off and off if it is on.

我想要一个切换服务状态的 Android 活动——它让我在它关闭时打开它,如果它打开则关闭它。

采纳答案by miracle2k

I had the same problem not long ago. Since my service was local, I ended up simply using a static field in the service class to toggle state, as described by hackbod here

不久前我遇到了同样的问题。由于我的服务是本地的,我最终只是使用服务类中的静态字段来切换状态,如 hackbod here 所述

EDIT (for the record):

编辑(记录):

Here is the solution proposed by hackbod:

这是hackbod提出的解决方案:

If your client and server code is part of the same .apk and you are binding to the service with a concrete Intent (one that specifies the exact service class), then you can simply have your service set a global variable when it is running that your client can check.

We deliberately don't have an API to check whether a service is running because, nearly without fail, when you want to do something like that you end up with race conditions in your code.

如果您的客户端和服务器代码是同一个 .apk 的一部分,并且您使用具体的 Intent(指定确切的服务类)绑定到服务,那么您可以简单地让您的服务在运行时设置一个全局变量您的客户可以检查。

我们故意没有 API 来检查服务是否正在运行,因为几乎没有失败,当您想要做类似的事情时,您最终会在代码中遇到竞争条件。

回答by geekQ

I use the following from inside an activity:

我在活动中使用以下内容:

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

And I call it using:

我称之为:

isMyServiceRunning(MyService.class)

This works reliably, because it is based on the information about running services provided by the Android operating system through ActivityManager#getRunningServices.

这工作可靠,因为它基于 Android 操作系统通过ActivityManager#getRunningServices提供的有关运行服务的信息。

All the approaches using onDestroy or onSometing events or Binders or static variables will not work reliably because as a developer you never know, when Android decides to kill your process or which of the mentioned callbacks are called or not. Please note the "killable" column in the lifecycle events tablein the Android documentation.

所有使用 onDestroy 或 onSometing 事件或 Binders 或静态变量的方法都无法可靠地工作,因为作为开发人员,您永远不知道 Android 何时决定终止您的进程或调用或不调用提到的哪些回调。请注意Android 文档中生命周期事件表中的“killable”列。

回答by Kevin Parker

Got it!

知道了!

You MUSTcall startService()for your service to be properly registered and passing BIND_AUTO_CREATEwill not suffice.

必须要求startService()正确注册您的服务,通过是BIND_AUTO_CREATE不够的。

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

And now the ServiceTools class:

现在是 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;
     }
}

回答by Snicolas

A small complement is:

一个小的补充是:

My goal is to know wether a service is running without actualy running it if it is not running.

我的目标是知道服务是否正在运行,而如果它没有运行,则不会实际运行它。

Calling bindService or calling an intent that can be caught by the service is not a good idea then as it will start the service if it is not running.

调用 bindService 或调用可以被服务捕获的意图并不是一个好主意,因为如果服务未运行,它将启动服务。

So, as miracle2k suggested, the best is to have a static field in the service class to know whether the service has been started or not.

所以,正如奇迹2k所建议的,最好是在服务类中有一个静态字段来知道服务是否已经启动。

To make it even cleaner, I suggest to transform the service in a singleton with a very very lazy fetching: that is, there is no instantiation at all of the singletoninstance through static methods. The static getInstance method of your service/singleton just returns the instance of the singleton if it has been created. But it doesn't actualy start or instanciate the singleton itself. The service is only started through normal service start methods.

为了使它更简洁,我建议将服务转换为具有非常非常懒惰的获取的单例:也就是说,没有通过静态方法对单例实例进行任何实例化。您的服务/单例的静态 getInstance 方法仅返回单例的实例(如果已创建)。但它实际上并没有启动或实例化单身人士本身。该服务仅通过正常的服务启动方法启动。

It would then be even cleaner to modify the singleton design pattern to rename the confusing getInstance method into something like the isInstanceCreated() : booleanmethod.

然后修改单例设计模式以将令人困惑的 getInstance 方法重命名为类似isInstanceCreated() : boolean方法的名称会更清晰。

The code will look like:

代码将如下所示:

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

This solution is elegant, but it is only relevant if you have access to the service class and only for classes iside the app/package of the service. If your classes are outside of the service app/package then you could query the ActivityManager with limitations underlined by Pieter-Jan Van Robays.

此解决方案很优雅,但仅当您有权访问服务类并且仅适用于服务的应用程序/包旁边的类时才相关。如果您的类在服务应用程序/包之外,那么您可以使用 Pieter-Jan Van Robays 强调的限制查询 ActivityManager。

回答by Keenan Gebze

You can use this (I didn't try this yet, but I hope this works):

你可以使用这个(我还没有尝试过,但我希望它有效):

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

The startService method returns a ComponentName object if there is an already running service. If not, null will be returned.

如果已经有一个正在运行的服务,startService 方法将返回一个 ComponentName 对象。如果不是,则返回 null。

See public abstract ComponentName startService (Intent service).

请参阅公共抽象 ComponentName startService (Intent service)

This is not like checking I think, because it's starting the service, so you can add stopService(someIntent);under the code.

这不像我想的检查,因为它是启动服务,所以你可以stopService(someIntent);在代码下添加。

回答by J.R

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

回答by peterchaula

An extract from Androiddocs:

Android文档的摘录:

Like sendBroadcast(Intent), but if there are any receivers for the Intent this function will block and immediately dispatch them before returning.

sendBroadcast(Intent)类似,但如果Intent有任何接收器,此函数将阻塞并在返回之前立即分派它们。

Think of this hack as "pinging" the Service. Since we can broadcast synchronously, we can broadcast and get a result synchronously, on the UI thread.

将此黑客视为“ping”Service. 由于我们可以同步广播,所以我们可以在 UI 线程上同步广播并获取结果。

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

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

The winner in many applications is, of course, a static boolean field on the service that is set to truein Service.onCreate()and to falsein Service.onDestroy()because it's a lot simpler.

在许多应用程序中,获胜者当然是服务上设置为trueinService.onCreate()和 to falsein的静态布尔字段,Service.onDestroy()因为它要简单得多。

回答by loretoparisi

I have slightly modified one of the solutions presented above, but passing the class instead of a generic string name, in order to be sure to compare strings coming out from the same method class.getName()

我稍微修改了上面提出的解决方案之一,但传递了类而不是通用字符串名称,以确保比较来自同一方法的字符串 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;
    }
}

and then

进而

Boolean isServiceRunning = ServiceTools.isServiceRunning(
                    MainActivity.this.getApplicationContext(),
                    BackgroundIntentService.class);

回答by Ben H

The proper way to check if a service is running is to simply ask it. Implement a BroadcastReceiver in your service that responds to pings from your activities. Register the BroadcastReceiver when the service starts, and unregister it when the service is destroyed. From your activity (or any component), send a local broadcastintent to the service and if it responds, you know it's running. Note the subtle difference between ACTION_PING and ACTION_PONG in the code below.

检查服务是否正在运行的正确方法是简单地询问它。在您的服务中实现一个 BroadcastReceiver 来响应来自您的活动的 ping。服务启动时注册 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;
            }
        }
    };
}

回答by Paul

I just want to add a note to the answer by @Snicolas. The following steps can be used to check stop service with/without calling onDestroy().

我只想在@Snicolas 的答案中添加注释。以下步骤可用于在调用/不调用的情况下检查停止服务onDestroy()

  1. onDestroy()called: Go to Settings -> Application -> Running Services -> Select and stop your service.

  2. onDestroy()not Called: Go to Settings -> Application -> Manage Applications -> Select and "Force Stop" your application in which your service is running. However, as your application is stopped here, so definitely the service instances will also be stopped.

  1. onDestroy()调用:转到设置-> 应用程序-> 运行服务-> 选择并停止您的服务。

  2. onDestroy()未调用:转到设置 -> 应用程序 -> 管理应用程序 -> 选择并“强制停止”运行服务的应用程序。但是,由于您的应用程序在此处停止,因此服务实例肯定也会停止。

Finally, I would like to mention that the approach mentioned there using a static variable in singleton class is working for me.

最后,我想提一下,那里提到的在单例类中使用静态变量的方法对我有用。