如何检查后台服务(在Android上)是否正在运行?
我想要一个能够切换服务状态的Android活动 - 它可以让我打开它,如果它打开则关闭.
我在活动中使用以下内容:
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决定终止您的进程或调用哪些回调时.请注意Android文档中生命周期事件表中的"killable"列.
不久前我遇到了同样的问题.因为我的服务是本地的,最后我简单地使用在服务类中的静态字段来切换状态,如hackbod描述这里
编辑(记录):
这是hackbod提出的解决方案:
如果您的客户端和服务器代码是同一个.apk的一部分,并且您使用具体的Intent(指定确切的服务类的那个)绑定到该服务,那么您可以简单地让您的服务在运行时设置一个全局变量你的客户可以检查.
我们故意没有API来检查服务是否正在运行,因为几乎没有失败,当你想要做类似的事情时,你最终会遇到代码中的竞争条件.
得到它了!
您必须要求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 Listservices = activityManager.getRunningServices(Integer.MAX_VALUE); for (RunningServiceInfo runningServiceInfo : services) { if (runningServiceInfo.service.getClassName().equals(serviceClassName)){ return true; } } return false; } }
一个小补充是:
我的目标是知道一个服务正在运行,如果没有运行它没有实际运行它.
调用bindService或调用可以被服务捕获的intent并不是一个好主意,因为如果它没有运行它将启动服务.
因此,正如miracle2k建议的那样,最好的方法是在服务类中使用静态字段来了解服务是否已经启动.
为了使它更干净,我建议使用一个非常非常懒的读取来转换服务:也就是说,通过静态方法没有实例化所有单例实例.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);
在代码下添加.
/** * 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; }
这是Android文档的摘录:
像sendBroadcast(Intent)一样,但是如果Intent有任何接收器,这个函数会阻塞并在返回之前立即发送它们.
把这个黑客想象为"ping",Service
因为我们可以同步广播,我们可以在UI线程上广播并同步获得结果.
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
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; } }
我稍微修改了上面提到的解决方案之一,但传递了类而不是通用的字符串名称,以确保比较来自同一方法的字符串 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 Listservices = 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);
我只想在@Snicolas的答案中添加一个注释.以下步骤可用于在有/无呼叫的情况下检查停止服务onDestroy()
.
onDestroy()
被叫:转到设置 - >应用程序 - >运行服务 - >选择并停止服务.
onDestroy()
未调用:转到设置 - >应用程序 - >管理应用程序 - >选择并"强制停止"运行服务的应用程序.但是,由于您的应用程序在此处停止,因此服务实例也将被停止.
最后,我想提一下,在单例类中使用静态变量提到的方法对我有用.
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; }
以下是涵盖所有内容的优雅技巧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(...); }
对于此处给出的用例,我们可以简单地利用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
您在班级中私下定义的常量是哪里,以标识与您的服务关联的未决意图。