如何检查后台服务是否正在运行?
我想要一个可切换服务状态的Android活动-它可以让我在关闭状态下将其打开,在开启状态下将其关闭。
这更适用于Intent Service调试,因为它们会生成线程,但也可能适用于常规服务。 我发现这个线程归功于Binging
就我而言,我与调试器一起玩耍并找到了线程视图。 看起来有点像MS Word中的项目符号图标。 无论如何,您不必处于调试器模式即可使用它。 单击该过程,然后单击该按钮。 任何Intent Services都会在运行时显示,至少在模拟器上会显示。
我只想在@Snicolas的答案中添加注释。 以下步骤可用于检查有/无调用onDestroy()
停止服务。
onDestroy()
称为:转到设置->应用程序->运行服务->选择并停止您的服务。
未调用onDestroy()
:转到“设置”->“应用程序”->“管理应用程序”->选择并“强制停止”运行服务的应用程序。 但是,由于您的应用程序在这里停止,因此肯定也将停止服务实例。
最后,我要提到的是,在那里提到的在单例类中使用静态变量的方法对我有效。
对于此处给出的用例,我们可以简单地利用stopService()
方法的返回值。 如果存在指定的服务并将其终止,则返回true
。 否则返回false
。 因此,如果结果为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);
}
没有特殊权限,没有循环...简单的方法,干净的解决方案:)
如果您需要其他信息,请参阅链接
希望这可以帮助。
如果服务属于另一个进程或APK,请使用基于ActivityManager的解决方案。
如果您有权访问其源代码,只需使用基于静态字段的解决方案。 但是我建议使用Date对象,而不是使用布尔值。 在服务运行时,只需将其值更新为“ now”,完成后将其设置为null。 从活动中,您可以检查其空值或日期是否过旧,这表示该活动未运行。
您还可以从您的服务发送广播通知,指示正在运行的其他信息包括进度。
可以有多个具有相同类名的服务。
我刚刚创建了两个应用程序。 第一个应用程序的软件包名称为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
实例,因为equals()
的ComponentName
比较两个包名和类名。 而且,设备上不能安装两个具有相同程序包名称的应用程序。
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;
}
组件名称
/**
* 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;
}
同样,如果人们使用未决的意图(例如使用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)
。
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;
}
您可以使用它(我还没有尝试过,但是我希望它能起作用):
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);
在代码下。
我对上述解决方案之一进行了一些修改,但是传递了类而不是通用字符串名称,以确保比较来自同一方法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 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线程上同步广播并获取结果。
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;
}
}
在许多应用中的赢家,当然,对被设置为服务的静态布尔场true
的Service.onCreate()
并以false
的Service.onDestroy()
因为它是一个简单多了。
检查服务是否正在运行的正确方法是简单地询问它。 在您的服务中实现一个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;
}
}
};
}
简单使用bind with不要创建自动 -请参见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);
boolean bindResult = context.bindService(bindIntent, ServiceConnection, 0);
为什么不使用? getRunningServices()
List 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 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中挖洞,所以直到我剖析了剩余的呼叫之后,我才暂停回答。
但是检查服务是否正在运行的最佳方法是创建绑定(如果未创建绑定,则服务不存在)-并通过绑定查询服务的状态(在其状态上使用存储的内部标志)。
我发现那些有趣:
/**
* 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;
}
简而言之 :)
“为已绑定的服务提供绑定器。此方法是同步的,如果不存在,则不会启动目标服务。”
公共IBinder peekService(意图服务,字符串resolveType,字符串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;
}
*
以下是涵盖所有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)
服务中并不总是调用onDestroy
,因此这是没有用的!
例如:从Eclipse进行一次更改即可再次运行该应用程序。 使用SIG强制退出该应用程序:9。
得到它了!
您必须调用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 services = activityManager.getRunningServices(Integer.MAX_VALUE);
for (RunningServiceInfo runningServiceInfo : services) {
if (runningServiceInfo.service.getClassName().equals(serviceClassName)){
return true;
}
}
return false;
}
}
在服务子类中,使用静态布尔值获取服务状态,如下所示。
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来获得服务。 ( 在这里讨论)
服务可以自己运行,可以绑定到活动,也可以两者都运行。 检入Activity是否服务的方法是通过创建一个接口(扩展Binder),在该接口中声明Activity和Service都可以理解的方法。 您可以通过在声明例如“ isServiceRunning()”的地方创建自己的接口来实现。 然后,您可以将Activity绑定到Service,运行方法isServiceRunning(),Service将检查自身是否正在运行,并向Activity返回一个布尔值。
您还可以使用此方法停止服务或以其他方式与其交互。
我使用本教程来学习如何在应用程序中实现此方案。
我的基于ActivityManager::getRunningServices
的答案的Kotlin转换。 将此功能放在活动中-
private fun isMyServiceRunning(serviceClass: Class) =
(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;
}
对于kotlin,可以使用以下代码。
fun isMyServiceRunning(calssObj: Class): 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
}
在kotlin中,您可以在随播对象中添加布尔变量,并从所需的任何类中检查其值:
companion object{
var isRuning = false
}
创建和销毁服务时更改其值
override fun onCreate() {
super.onCreate()
isRuning = true
}
override fun onDestroy() {
super.onDestroy()
isRuning = false
}
另一种使用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 }
}
您可以使用Android开发者选项中的此选项来查看您的服务是否仍在后台运行。
1. Open Settings in your Android device.
2. Find Developer Options, open it.
3. Find Running Services option, open it.
4. Find your app icon, open it.
5. You will then see all the service that belongs to your app running in the background.
我在活动中使用以下内容:
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”列。
一个小的补充是:
我的目标是知道服务是否正在运行,如果服务未运行,则实际上不会运行它。
调用bindService或调用服务可能捕获的意图不是一个好主意,因为如果服务未运行,它将启动服务。
因此,正如miracle2k建议的那样,最好的办法是在服务类中有一个静态字段,以了解服务是否已启动。
为了使其更加整洁,我建议通过非常懒惰的获取将服务转换为单例:也就是说,通过静态方法在所有单例实例上都没有实例化。 服务/单例的静态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。
我不久前有同样的问题。 因为我的服务是本地的,最后我简单地使用在服务类切换状态静态字段,如hackbod描述这里
编辑(记录):
这是hackbod提出的解决方案:
如果您的客户端和服务器代码是同一.apk的一部分,并且您使用具体的Intent(用于指定确切的服务类)绑定到服务,则只需在服务运行时将其设置为全局变量即可您的客户可以检查。
我们故意没有API来检查服务是否正在运行,因为当您想要执行类似的操作时,几乎会失败,您最终会在代码中出现竞争条件。