ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

正确答案在下方,而不是标记的答案:stackoverflow.com/a/5921190/2369122
@toidiu 如果还没有像 getRunningTasks() 那样被削弱,它可能会。
使用 getSystemService() 函数,您可以检索所有正在运行的服务。遍历它并检查您的服务是否存在于列表中,您可以看到一个小示例 wiki.workassis.com/android-check-the-service-is-running

P
Peter Mortensen

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

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 何时决定终止您的进程或调用或不调用哪个回调。请注意 Android 文档中 lifecycle events table 中的“可杀死”列。


感谢您提供此解决方案。我想补充一点:“com.example.MyService”更适合使用 MyService.class.getName()
就个人而言,我选择使用静态字段。尽管使用 getRunningServices() 是一种更健壮的解决方案,但我相信这两种解决方案在健壮性和效率/简单性之间进行了权衡。如果您需要经常检查服务是否正在运行,那么循环遍历可能运行的 30 多个服务并不是很理想。服务被系统破坏的罕见情况可以通过 try/catch 块或使用 START_STICKY 来处理。
不,这不是正确的答案,因为它也写在文档中:“注意:此方法仅用于调试或实现服务管理类型的用户界面。”它不适用于控制流!
人们发现必须通过所有这些来检查服务器是否正在运行是一件很优雅的事情?
Android O 开始,getRunningServices 已弃用。这个答案需要更新更新版本。
H
Haresh Chaudhary

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

编辑(备案):

这是hackbod提出的解决方案:

如果您的客户端和服务器代码是同一个 .apk 的一部分,并且您使用具体的 Intent(指定确切服务类的意图)绑定到服务,那么您可以简单地让您的服务在运行时设置一个全局变量您的客户可以检查。我们故意没有 API 来检查服务是否正在运行,因为几乎没有失败,当您想做类似的事情时,您的代码中会出现竞争条件。


@Pacerier,您引用的解决方案需要启动服务,我认为最好的灵活解决方案应该允许您在不启动服务的情况下检查服务是否正在运行。
如果服务被系统停止了怎么办,你如何检测到并切换你的变量?
当应用程序被杀死时,它启动的服务也被杀死,但服务的 onDestroy() 没有被调用。因此,在这种情况下无法更新静态变量,从而导致行为不一致。
@faizal 静态变量不会也被重新初始化,从而将其设置回指示服务不再运行的默认值吗?
@faizal,本地服务不是一个单独的进程,所以如果服务被杀死,那么应用程序也会被杀死。
k
kenju

知道了!

必须调用 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;
     }
}

这将仅列出系统服务,不是吗?!所以我的本地服务被排除在列表之外,我会得到错误的;(
这适用于外部服务,因为如果您正在运行,本地服务非常明显。
对不起,但我需要说这是超级愚蠢的答案..为什么它超级明显?!
不清楚你在这里的意思......谁在谈论崩溃?!我对撞毁它并不感兴趣。服务可以启动,停止,也许它是意图服务,完成后它会自行停止......问题是如何知道它是否仍在运行,例如 3 分钟后。
给人的印象是绑定服务也必须启动是不正确的。不。绑定自动创建完全按照它所说的进行。如果服务尚未运行,它将创建(并因此“启动”)该服务。
P
Peter Mortensen

一个小的补充是:

我的目标是知道一个服务是否正在运行,如果它没有运行,它实际上没有运行它。

调用 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

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


这是有缺陷的。不保证会调用 onDestroy。
当系统内存不足时,您的服务将在不调用 onDestroy 的情况下自动终止,这就是为什么我说这是有缺陷的。
@Pacerier,但如果系统终止进程,那么实例标志仍将被重置。我猜当接收器下一次被加载(发布系统终止服务)时,静态标志“实例”将被重新创建为空。
至少比在 isMyServiceRunning 中遍历所有这些服务要好,如果在每次设备旋转时都完成,这真的会延迟事情:)
您的实例变量不应声明为 final,否则不能通过 onCreate() 或 onDestroy() 方法设置或为空。
P
Peter Mortensen

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

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。

请参阅 public abstract ComponentName startService (Intent service)

这不像我认为的检查,因为它正在启动服务,因此您可以在代码下添加stopService(someIntent);


不完全是文档所说的。根据您的链接:“返回如果服务正在启动或已经在运行,则返回已启动的实际服务的 ComponentName;否则,如果服务不存在,则返回 null。”
不错的想法……但不适合目前的情况。
这不是正确的方法,因为当 IDE 触发 if(startService(someIntent) != null) 时,它会检查 IsserviceRunning 但也会播放新服务。
如前所述,如果您在此控制之后停止服务,它将很方便解决此问题。但是为什么要白白启动和停止服务呢?
这将启动服务,不是吗?只想检查服务的状态而不是启动它...
J
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;
}

P
Peter Chaula

Android 文档的摘录:

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

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

服务

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

活动

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

当然,许多应用程序的赢家是服务上的静态布尔字段,它在 Service.onCreate() 中设置为 true,在 Service.onDestroy() 中设置为 false,因为它更简单。


这是一个比公认的解决方案更好的解决方案,如果 Android 终止服务,则会失败,因为全局变量方法仍会指示服务正在运行,而实际上它不再运行。这种同步乒乓广播技巧实际上是检查服务是否处于活动状态的唯一可靠方法。它单独允许您简单地询问服务是否存在。如果它回答,则服务处于活动状态并正在运行,如果不是,则它尚未启动或已关闭,以编程方式或由系统恢复内存。
C
ClassA

检查服务是否正在运行的正确方法是简单地询问它。在您的服务中实现一个 BroadcastReceiver 来响应来自您的活动的 ping。服务启动时注册BroadcastReceiver,服务销毁时注销。从您的活动(或任何组件)中,向服务发送 local broadcast 意图,如果它响应,您就知道它正在运行。请注意下面代码中 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;
            }
        }
    };
}

我其实很喜欢这种方法。明智的代码有点繁重,但总是可以工作。我看不到广播意图很快就会被弃用:)
考虑到可能会错过广播。
还有...LocalBroadcastManager is now deprecated...(边缘镜头)。不过,这仍然是一个很好的解决方案。
l
loretoparisi

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

为了更加严格,您可以将类参数更改为 Class<? extends Service>
O
Oscar Emilio Perez Martinez

使用 kotlin 的另一种方法。受到其他用户回答的启发

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

作为 kotlin 扩展

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)

注意:getRunningServices 现在已被弃用,似乎该方法不会被删除。
a
apaderno

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

onDestroy() 调用:转到设置 -> 应用程序 -> 运行服务 -> 选择并停止您的服务。未调用 onDestroy():转到设置 -> 应用程序 -> 管理应用程序 -> 选择并“强制停止”您的服务正在其中运行的应用程序。但是,由于您的应用程序在这里停止,所以服务实例肯定也会停止。

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


服务可能在不同的进程上,考虑到这一点
r
ricardopereira

首先,您不应该使用 ActivityManager 访问该服务。 (讨论过 here

服务可以单独运行,也可以绑定到 Activity 或两者兼而有之。如果您的服务正在运行,检查 Activity 的方法是创建一个接口(扩展 Binder),在该接口中声明 ActivityService 都可以理解的方法。您可以通过在声明例如“isServiceRunning()”的地方创建自己的接口来做到这一点。然后,您可以将您的 Activity 绑定到您的服务,运行 isServiceRunning() 方法,服务将检查自己是否正在运行,并向您的 Activity 返回一个布尔值。

您还可以使用此方法停止您的服务或以其他方式与之交互。


那次讨论发生在 '12/26/07'。要么是今年 7 月(即未来),要么是在 Android 公开之前。不管怎样,这让我不信任它。
该讨论是从 2007 年 12 月 26 日开始的。他们正在讨论我认为是 2007 年 12 月 14 日发布的预发布版本 (developer.android.com/sdk/OLD_RELEASENOTES.html#m3-rc37a)。
n
nhaarman

onDestroy 并不总是在服务中调用,所以这没用!

例如:只需在 Eclipse 中进行一次更改,再次运行该应用程序。使用 SIG: 9 强制退出应用程序。


S
Snicolas

同样,如果人们使用挂起的意图(例如使用 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 是您在类中私下定义的常量,用于标识与您的服务关联的待处理意图。


合并或更新您之前的答案。请避免在每个帖子中发布多个答案。
可以扩展这个答案,即如何将 CODE 的值与服务联系起来?
从哪里获取上下文?
T
TheRealChx101

下面是涵盖所有 Ifs 的优雅 hack。这仅适用于本地服务。

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

唯一的问题是该服务是否是粘性服务并且它会自行重新启动。再次启动服务后调用 isServiceCreated() 将返回 false,因为 mInstance 将为空。
当服务重新启动时,不会调用 onCreate 吗?
t
testing

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 的“上下文”。
r
rahulrvp

对于此处给出的用例,我们可以简单地使用 stopService() 方法的返回值。如果存在指定的服务并被杀死,则返回 true。否则返回 false。因此,如果结果为 false,您可以重新启动服务,否则可以确保当前服务已停止。 :) 如果你看看 this 会更好。


L
Luis Moreno

geekQ 的响应,但在 Kotlin 类中。感谢极客Q

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)

自 Android O 起已弃用 ActivityManager.getRunningServices
E
EdgeDev

在您的服务子类中使用静态布尔值来获取服务的状态,如下所示。

我的服务.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)
        }
    }
}

P
P A Gosai

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

这是编写测试的一个很好的答案,因为您可以在不更改工作代码的情况下使用它。
我们可以让这个返回 Flow 吗?
为什么在这里对 Kotlin 使用 equals
f
fullmoon

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


如果您的服务被 Android 杀死,这将不起作用。
@Heisenberg我自己也经历过。你知道为什么不吗?
@Heisenberg,当我的应用程序被操作系统杀死时,服务会重新启动并将静态布尔值设置为 true,但在获取它时会报告 false
如果您调用 stopService,这将不起作用。至少对于 Intent 服务。 onDestroy() 将立即被调用,但 onHandleIntent() 仍将运行
@Heisenberg不会因为内存不足而终止服务也意味着终止进程吗?
c
ceph3us

简单地使用绑定与不创建自动 - 见 ps。并更新...

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

为什么不使用?获取运行服务()

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

我现在没有时间挖掘 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(Intent service, String resolvedType, String callingPackage) 抛出 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;
}

*


如果服务未运行,bindResult(bindService 方法的返回值)不会为 false。
M
Mohamed Saber

在 kotlin 中,您可以在伴生对象中添加布尔变量,并从您想要的任何类中检查其值:

companion object{
     var isRuning = false

}

创建和销毁服务时更改它的值

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

override fun onDestroy() {
    super.onDestroy()
    isRuning = false
    }

onDestroy() 并不总是被调用
@user924 没有调用 onDestroy() 时,是因为整个进程都被杀死了,所以周围没有什么可以检查 isRuning 的值。事实上,此时 isRuning 已不存在。所以不调用 onDestroy() 不是问题。当然,这是假设所有组件都存在于同一个进程中(这是默认设置)。
M
Maksim Dmitriev

可以有多个具有相同类名的服务。

我刚刚创建了两个应用程序。第一个应用程序的包名称是 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 实例,因为 ComponentNameequals() 比较包名称和类名称。并且不能在设备上安装两个具有相同包名称的应用程序。

ComponentName 的 equals() 方法。

@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


N
Nikunj Sorathiya

请使用此代码。

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

getRunningServices 在 android O 中已弃用
D
Duna

检测服务是否正在运行的唯一有效/最快/干净的方法是创建 PING/PONG 功能。

在服务内实现 Messenger 或 AIDL 方法:isAlive() - 返回服务的状态。

不要实施广播,因为它们可能会被错过。


D
David

如果您有一个多模块应用程序,并且您想知道该服务是否正在运行,而该模块不依赖于包含该服务的模块,则可以使用此功能:

fun isServiceRunning(context: Context, serviceClassName: String): Boolean {

    val manager = ContextCompat.getSystemService(
        context,
        ActivityManager::class.java
    ) ?: return false

    return manager.getRunningServices(Integer.MAX_VALUE).any { serviceInfo ->
        serviceInfo.service.shortClassName.contains(vpnServiceClassName)
    }
}

MyService 服务的用法:

isServiceRunning(context, "MyService")

如果服务类名称发生变化而调用函数没有相应变化,则该函数可能无法正常工作。


自 Android O (8.1) 起已弃用 getRunningServices
是的。我不知道有什么选择。
J
Joe Plante

这更适用于 Intent Service 调试,因为它们会产生一个线程,但也可能适用于常规服务。感谢 Binging,我找到了这个帖子

就我而言,我使用了调试器并找到了线程视图。它有点像 MS Word 中的项目符号图标。无论如何,您不必处于调试器模式即可使用它。单击该过程,然后单击该按钮。任何 Intent 服务都会在它们运行时出现,至少在模拟器上是这样。


F
FranMowinckel

如果服务属于其他进程或 APK,则使用基于 ActivityManager 的解决方案。

如果您可以访问其源代码,只需使用基于静态字段的解决方案。但是我建议使用一个布尔值而不是使用一个 Date 对象。在服务运行时,只需将其值更新为“现在”,并在完成后将其设置为空。从活动中,您可以检查它是否为空或日期是否太旧,这意味着它没有运行。

您还可以从您的服务发送广播通知,指示该通知正在运行进一步的信息,如进度。


j
jlguenego

从 Android 8(或 Oreo)开始,API getRunningServices 已弃用。当然,您可以使用 @SuppressWarnings("deprecation") 来消除警告。

如果您的服务不需要有多个实例,以下是不使用 getRunninngServices 的方法:使用单例模式。

public class MyMusicService extends Service {

    private static MyMusicService instance = null;
    
    public static boolean isMyMusicServiceRunning() {
        return instance != null;
    }

然后,您可以从您的活动或其他地方调用 MyMusicService.isMyMusicServiceRunning