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

如何检查后台服务(在Android上)是否正在运行?

我想要一个切换服务状态的Android活动 – 如果它处于closures状态,则可以将其打开。

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

编辑(备案):

这是hackbod提出的解决scheme:

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

我们故意没有一个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事件或绑定器或静态variables的方法将无法可靠地工作,因为作为一个开发人员,你永远不知道,当Android决定杀死你的过程,或者是否调用上述callback。 请注意Android文档的生命周期事件表中的“killable”列。

得到它了!

必须调用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或调用服务可以捕获的意图不是一个好主意,那么它将在服务未运行时启动服务。

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

为了使它更清洁,我build议用一个非常非常懒的方式来改变服务的单例:也就是说,没有通过静态方法实例化所有的单例实例。 您的服务/单例的静态getInstance方法只是返回单例的实例,如果它已被创build。 但它并不真正启动或实例化单身人士本身。 该服务只能通过正常的服务启动方法启动。

然后修改单例devise模式,将混淆的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 

这个解决scheme是优雅的,但是只有当你有权访问服务类,并且只有服务的应用程序/包才是类。 如果您的课程不在服务应用程序/包中,那么您可以使用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。

请参阅public abstract ComponentName startService(Intent service)

这不像检查我想,因为它启动服务,所以你可以添加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; } 

首先你不用通过使用ActivityManager来获得服务。 ( 在这里讨论)

服务可以自行运行,绑定到一个活动或两个。 如果您的服务正在运行,检查Activity的方式是通过创build一个接口(扩展了Binder)来声明Activity和Service所了解的方法。 你可以通过在声明例如“isServiceRunning()”的地方创build你自己的接口来做到这一点。 然后,您可以将您的活动绑定到您的服务,运行方法isServiceRunning(),服务将检查自身是否正在运行,并返回布尔值到您的活动。

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

我使用这个教程来学习如何在我的应用程序中实现这个场景。

我只想给@Snicolas添加注释。 可以使用以下步骤检查停止服务是否调用onDestroy()

  1. 调用onDestroy() :进入设置 – >应用程序 – >运行服务 – >select并停止服务。

  2. onDestroy()没有调用:进入设置 – >应用程序 – >pipe理应用程序 – >select和“强制停止”您的应用程序在其中运行您的服务。 但是,由于您的应用程序在此处停止,所以服务实例也将被停止。

最后,我想提一下,在单例类中使用静态variables提到的方法正在为我工​​作。

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

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

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

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

我稍微修改了上面提到的解决scheme之一,但是传递类而不是通用string名,以确保比较来自相同方法的stringclass.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); 

再一次,如果人们使用未决的意图,人们可能会发现更清洁的替代scheme(例如,使用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是您在课堂上私下定义的常数,用于识别与您的服务相关的未决意图。

在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文档的摘录

有序广播(使用Context.sendOrderedBroadcast发送)一次传递给一个接收者。 由于每个接收器依次执行,它可以将结果传播到下一个接收器,或者它可以完全中止广播,使其不会传递给其他接收器。

这是一个黑客,但认为这是“ping” Service因为我们可以同步播放,我们可以在UI线程上同步播放和获取结果。

Service

BroadcastReceiver

 @Override public void onCreate() { LocalBroadcastManager .getInstance(this) .registerReceiver(new ServiceEchoReceiver(), IntentFilter("echo"); } private class ServiceEchoReceiver{ public void onReceive (Context context, Intent intent) { LocalBroadcastManager .getInstance(this) .sendBroadcastSync(new Intent("echo")); } } 

Activity

  bool serviceRunning = false; protected void onCreate (Bundle savedInstanceState){ LocalBroadcastManager.getInstance(this).registerReceiver(echo); LocalBroadcastManager.getInstance(this).sendBroadcastSync(new Intent("echo")); if(!serviceRunning){ //try and run the service } } private BroadcastReceiver echo = new BroadcastReceiver(){ public void onReceive (Context context, Intent intent) { serviceRunning = true; } } 

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

我刚刚创build了两个应用程序。 第一个应用程序的包名是com.example.mock 。 我在应用程序中创build了一个名为lorem的子包,并创build了一个名为Mock2Service的服务。 所以它的完全限定名是com.example.mock.lorem.Mock2Service

然后我创build了第二个应用程序和一个名为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; } 

组件名称

这更适用于Intent Servicedebugging,因为它们产生了一个线程,但是也可以用于常规服务。 感谢Binging,我发现了这个主题

在我的情况下,我玩弄了debugging器,发现了线程视图。 它有点像MS Word中的项目符号图标。 无论如何,你不必在debugging器模式下使用它。 点击进程并点击该button。 任何意图服务将在运行时显示,至less在模拟器上。

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

如果您有权访问其源,只需使用基于静态字段的解决scheme即可。 但是,而是使用布尔值,我会build议使用date对象。 服务正在运行时,只需将其值更新为“现在”,并在完成时将其设置为空。 从这个活动你可以检查它是否为空或date太旧,这将意味着它没有运行。

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

检查服务是否正常运行的正确方法是简单地询问它。 在您的服务中实施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; } } }; } 
 public static boolean isServiceRunning; @Override public int onStartCommand(Intent intent, int flags, int startId) { isServiceRunning = true; return START_NOT_STICKY; } @Override public void onDestroy() { super.onDestroy(); isServiceRunning = false; } 

要么

如果不想使用静态,那么用户SharedPreferences

 @Override public int onStartCommand(Intent intent, int flags, int startId) { PrefManager.getPref().saveBoolean(AppConstants.Pref.IS_SERVICE_RUNNING, true); return START_NOT_STICKY; } @Override public void onDestroy() { super.onDestroy(); PrefManager.getPref().saveBoolean(AppConstants.Pref.IS_SERVICE_RUNNING, false); } 

PrefManager是我的单身类pipe理所有偏好。

简单的家伙… 🙂

我认为最合适的解决scheme是在SharedPreferences保存关键值对,以确定服务是否正在运行。

逻辑很直接; 在您的服务级别的任何位置; 把一个布尔值,这将作为一个标志为你是否服务是否运行。 然后在您的应用程序中随心所欲地阅读这个值。

我在我的应用程序中使用的示例代码如下:

在我的Service类(audiostream服务)中,当服务启动时,我执行下面的代码;

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

没有特殊的权限,没有循环…简单的方法,清洁解决scheme:)

如果您需要额外的信息,请参阅链接

希望这可以帮助。

简单的使用绑定与不创build自动

 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. 

注意:此方法仅用于debugging或实现服务pipe理types的用户界面。

这对我有用

  if(startService(new Intent(this, YourService.class)) != 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(); }