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

我想要一个能够切换服务状态的Android活动 - 如果它处于打开状态,它可以让我打开它。

有帮助吗?

解决方案

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

编辑(记录):

以下是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事件或Binders或静态变量的所有方法都无法可靠地运行,因为作为开发人员,您永远不会知道,当Android决定终止您的进程或调用哪些回调时。请注意“killable”和“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或调用可以被服务捕获的intent不是一个好主意,因为如果服务没有运行它将启动它。

因此,正如miracle2k建议的那样,最好的方法是在服务类中使用静态字段来了解服务是否已经启动。

为了使它更干净,我建议用一个非常非常懒的读取来转换服务:也就是说,根本没有实例化 singleton 实例通过静态方法。 service / 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。

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

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(意图服务)

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

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

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

摘自 Android 文档:

  

sendBroadcast(Intent) ),但如果有任何接收器   Intent这个函数会阻塞并立即发送它们   在回来之前。

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

<代码>服务

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

<代码>活动

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

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

  1. onDestroy()调用:转到设置 - &gt;申请 - &gt;正在运行的服务 - &gt;选择并停止服务。

  2. onDestroy()未调用:转到设置 - &gt;申请 - &gt;管理应用程序 - &gt;选择并“强制停止”您的服务运行的应用程序。但是,由于您的应用程序在此处已停止,因此服务实例也将被停止。

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

服务中并不总是调用

onDestroy ,所以这没用!

例如:只需从Eclipse进行一次更改即可再次运行应用程序。使用SIG:9强行退出应用程序。

检查服务是否正在运行的正确方法是简单地询问它。在您的服务中实现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;
            }
        }
    };
}

首先,您不会尝试使用ActivityManager来访问服务。 (讨论此处

服务可以自己运行,绑定到一个Activity或两者都绑定。如果您的服务正在运行,则检入Activity的方法是创建一个接口(扩展Binder),您可以在其中声明Activity和Service都能理解的方法。您可以通过在声明例如“isServiceRunning()”的位置创建自己的接口来完成此操作。 然后,您可以将Activity绑定到您的Service,运行方法isServiceRunning(),如果它正在运行,Service将自行检查并为您的Activity返回一个布尔值。

您也可以使用此方法停止服务或以其他方式与服务进行交互。

我使用了这个教程了解如何在我的应用程序中实现此方案。

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

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

同样,如果他们使用待定意图,人们可能会发现更清洁的另一种选择(例如使用 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 是您在类中私下定义的常量,用于标识与您的服务关联的待处理意图。

下面是一个优雅的黑客,涵盖了所有 Ifs 。这仅适用于本地服务。

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

geekQ的反应,但在Kotlin类。谢谢geekQ

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)

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

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

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

这更适用于Intent Service调试,因为它们会产生一个线程,但也可能适用于常规服务。我发现这个帖子归功于Binging

在我的情况下,我玩了调试器并找到了线程视图。它有点像MS Word中的项目符号点图标。无论如何,您不必处于调试器模式即可使用它。单击该过程,然后单击该按钮。任何Intent Services都会在它们运行时显示,至少在模拟器上显示。

如果服务属于另一个进程,或者APK使用基于ActivityManager的解决方案。

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

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

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

简单使用bind with do not create auto - 请参阅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);

为什么不使用? getRunningServices()

List<ActivityManager.RunningServiceInfo> getRunningServices (int maxNum)
Return a list of the services that are currently running.

注意:此方法仅用于调试或实现服务管理类型用户界面。


<强> PS。 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;
}

简而言之:)

&quot;为已绑定的服务提供绑定器。此方法是同步的,如果不存在,则不会启动目标服务。“

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

*

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

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

基于 ActivityManager :: getRunningServices 的答案我的kotlin转换。将此功能放在活动中 -

private fun isMyServiceRunning(serviceClass: Class<out Service>) =
    (getSystemService(ACTIVITY_SERVICE) as ActivityManager)
        .getRunningServices(Int.MAX_VALUE)
        ?.map { it.service.className }
        ?.contains(serviceClass.name) ?: false

请使用此代码。

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

轻松一点......:)

我认为最合适的解决方案是在 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);
}

没有特殊权限,没有循环......简单的方法,干净的解决方案:)

如果您需要更多信息,请参阅链接

希望这有帮助。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top