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

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


当前回答

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

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

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

其他回答

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

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

companion object{
     var isRuning = false

}

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

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

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

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

编辑(供记录):

下面是hackbod提出的解决方案:

如果你的客户端和服务器代码是同一个.apk的一部分,而你是 绑定到服务的具体意图(一个指定 确切的服务类别),然后您可以简单地将您的服务设置为a 全局变量,当它正在运行时,您的客户端可以检查。 我们故意没有API来检查服务是否正确 跑步是因为,当你想做某事时,几乎没有失败 这样,代码中就会出现竞争条件。

简单使用绑定不创建自动查看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.

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


附注:android文档具有误导性,我已经在谷歌跟踪器上打开了一个问题,以消除任何疑问:

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的method中调用:

 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,所以在我解析rest call之前,我暂停了我的回答。

但是检查服务是否正在运行的最好方法是创建bind(如果没有创建bind,则服务不存在)-并通过bind查询服务的状态(在其状态上使用存储的内部标志)。

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

简而言之:)

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

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

*

你可以使用Android开发者选项中的这个选项来查看你的服务是否仍然在后台运行。

1. Open Settings in your Android device.
2. Find Developer Options.
3. Find Running Services option.
4. Find your app icon.
5. You will then see all the service that belongs to your app running in the background.