如何检查后台服务是否正在运行?
我想要一个切换服务状态的 Android 活动——它让我在它关闭时打开它,如果它打开则关闭它。
getRunningTasks()
那样被削弱,它可能会。
我在活动中使用以下内容:
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 中的“可杀死”列。
不久前我遇到了同样的问题。由于我的服务是本地服务,因此我最终只是使用服务类中的静态字段来切换状态,如 hackbod here 所述
编辑(备案):
这是hackbod提出的解决方案:
如果您的客户端和服务器代码是同一个 .apk 的一部分,并且您使用具体的 Intent(指定确切服务类的意图)绑定到服务,那么您可以简单地让您的服务在运行时设置一个全局变量您的客户可以检查。我们故意没有 API 来检查服务是否正在运行,因为几乎没有失败,当您想做类似的事情时,您的代码中会出现竞争条件。
onDestroy()
没有被调用。因此,在这种情况下无法更新静态变量,从而导致行为不一致。
知道了!
您必须调用 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建议的那样,最好在服务类中有一个静态字段来知道服务是否已经启动。
为了使其更简洁,我建议将服务转换为单例,使用非常非常懒惰的获取:也就是说,完全没有通过静态方法实例化 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。
请参阅 public abstract ComponentName startService (Intent service)。
这不像我认为的检查,因为它正在启动服务,因此您可以在代码下添加stopService(someIntent);
。
if(startService(someIntent) != null)
时,它会检查 IsserviceRunning
但也会播放新服务。
/**
* 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 线程上广播并同步获得结果。
服务
@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
,因为它更简单。
检查服务是否正在运行的正确方法是简单地询问它。在您的服务中实现一个 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;
}
}
};
}
我稍微修改了上面介绍的解决方案之一,但传递了类而不是通用字符串名称,以确保比较来自同一方法 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>
使用 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
现在已被弃用,似乎该方法不会被删除。
我只想在@Snicolas 的答案中添加注释。以下步骤可用于检查是否调用 onDestroy()
停止服务。
onDestroy() 调用:转到设置 -> 应用程序 -> 运行服务 -> 选择并停止您的服务。未调用 onDestroy():转到设置 -> 应用程序 -> 管理应用程序 -> 选择并“强制停止”您的服务正在其中运行的应用程序。但是,由于您的应用程序在这里停止,所以服务实例肯定也会停止。
最后,我想提一下,那里提到的在单例类中使用静态变量的方法对我有用。
首先,您不应该使用 ActivityManager
访问该服务。 (讨论过 here)
服务可以单独运行,也可以绑定到 Activity 或两者兼而有之。如果您的服务正在运行,检查 Activity 的方法是创建一个接口(扩展 Binder),在该接口中声明 Activity
和 Service
都可以理解的方法。您可以通过在声明例如“isServiceRunning()
”的地方创建自己的接口来做到这一点。然后,您可以将您的 Activity 绑定到您的服务,运行 isServiceRunning() 方法,服务将检查自己是否正在运行,并向您的 Activity 返回一个布尔值。
您还可以使用此方法停止您的服务或以其他方式与之交互。
onDestroy
并不总是在服务中调用,所以这没用!
例如:只需在 Eclipse 中进行一次更改,再次运行该应用程序。使用 SIG: 9 强制退出应用程序。
同样,如果人们使用挂起的意图(例如使用 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
的优雅 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(...);
}
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
的“上下文”。
对于此处给出的用例,我们可以简单地使用 stopService()
方法的返回值。如果存在指定的服务并被杀死,则返回 true
。否则返回 false
。因此,如果结果为 false
,您可以重新启动服务,否则可以确保当前服务已停止。 :) 如果你看看 this 会更好。
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)
ActivityManager.getRunningServices
在您的服务子类中使用静态布尔值来获取服务的状态,如下所示。
我的服务.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)
}
}
}
对于 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
}
equals
?
在 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)
。
stopService
,这将不起作用。至少对于 Intent 服务。 onDestroy()
将立即被调用,但 onHandleIntent()
仍将运行
简单地使用绑定与不创建自动 - 见 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;
}
*
在 kotlin 中,您可以在伴生对象中添加布尔变量,并从您想要的任何类中检查其值:
companion object{
var isRuning = false
}
创建和销毁服务时更改它的值
override fun onCreate() {
super.onCreate()
isRuning = true
}
override fun onDestroy() {
super.onDestroy()
isRuning = false
}
onDestroy()
时,是因为整个进程都被杀死了,所以周围没有什么可以检查 isRuning
的值。事实上,此时 isRuning
已不存在。所以不调用 onDestroy()
不是问题。当然,这是假设所有组件都存在于同一个进程中(这是默认设置)。
可以有多个具有相同类名的服务。
我刚刚创建了两个应用程序。第一个应用程序的包名称是 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;
}
请使用此代码。
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;
}
检测服务是否正在运行的唯一有效/最快/干净的方法是创建 PING/PONG 功能。
在服务内实现 Messenger 或 AIDL 方法:isAlive()
- 返回服务的状态。
不要实施广播,因为它们可能会被错过。
如果您有一个多模块应用程序,并且您想知道该服务是否正在运行,而该模块不依赖于包含该服务的模块,则可以使用此功能:
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")
如果服务类名称发生变化而调用函数没有相应变化,则该函数可能无法正常工作。
getRunningServices
这更适用于 Intent Service 调试,因为它们会产生一个线程,但也可能适用于常规服务。感谢 Binging,我找到了这个帖子
就我而言,我使用了调试器并找到了线程视图。它有点像 MS Word 中的项目符号图标。无论如何,您不必处于调试器模式即可使用它。单击该过程,然后单击该按钮。任何 Intent 服务都会在它们运行时出现,至少在模拟器上是这样。
如果服务属于其他进程或 APK,则使用基于 ActivityManager 的解决方案。
如果您可以访问其源代码,只需使用基于静态字段的解决方案。但是我建议使用一个布尔值而不是使用一个 Date 对象。在服务运行时,只需将其值更新为“现在”,并在完成后将其设置为空。从活动中,您可以检查它是否为空或日期是否太旧,这意味着它没有运行。
您还可以从您的服务发送广播通知,指示该通知正在运行进一步的信息,如进度。
从 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
。
不定期副业成功案例分享
getRunningServices
已弃用。这个答案需要更新更新版本。