如何检查后台服务是否正在运行?
我想要一个能够切换服务状态的Android活动 - 如果它处于打开状态,它可以让我打开它。
答案 0 :(得分:1594)
我在活动中使用以下内容:
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文档中lifecycle events table中的“killable”列。
答案 1 :(得分:267)
不久前我遇到了同样的问题。由于我的服务是本地的,我最终只是在服务类中使用静态字段来切换状态,如hackbod所述[{3}}
编辑(记录):
以下是hackbod提出的解决方案:
如果您的客户端和服务器代码属于同一个.apk,那么您就是 使用具体的Intent绑定到服务(一个指定 确切的服务类),然后您可以简单地设置您的服务 运行时客户端可以检查的全局变量。
我们故意没有API来检查服务是否是 因为,几乎没有失败,当你想要做某事时 就像你最终在代码中遇到竞争条件一样。
答案 2 :(得分:72)
知道了!
您必须致电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;
}
}
答案 3 :(得分:54)
一个小补充是:
我的目标是了解服务是否正在运行,如果它没有运行则不会实际运行。
调用bindService或调用可以被服务捕获的intent不是一个好主意,因为如果服务没有运行它将启动它。
因此,正如miracle2k建议的那样,最好的方法是在服务类中使用静态字段来了解服务是否已经启动。
为了使它更干净,我建议使用一个非常非常懒的读取来转换单个服务中的服务:也就是说,通过静态方法没有实例化singleton实例。 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。
答案 4 :(得分:24)
你可以使用这个(我还没试过,但我希望这有效):
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);
。
答案 5 :(得分:18)
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;
}
答案 6 :(得分:11)
摘自 Android 文档:
与sendBroadcast(Intent)类似,但是如果有任何接收器 Intent这个函数会阻塞并立即发送它们 在回来之前。
把这个黑客想象成&#34; pinging&#34; Service
,因为我们可以同步广播,我们可以在UI线程上广播并同步获得结果。
Service
@Override
public void onCreate() {
LocalBroadcastManager
.getInstance(this)
.registerReceiver(new ServiceEchoReceiver(), new IntentFilter("ping"));
}
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;
}
}
答案 7 :(得分:11)
我稍微修改了上面提到的一个解决方案,但是传递了类而不是通用的字符串名称,以确保比较来自同一方法的字符串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);
答案 8 :(得分:7)
我只想在@Snicolas的答案中添加一个注释。以下步骤可用于在有/无调用onDestroy()
的情况下检查停止服务。
onDestroy()
来电:转到设置 - &gt;申请 - &gt;正在运行的服务 - &gt;选择并停止服务。
onDestroy()
未调用:转到设置 - &gt;申请 - &gt;管理应用程序 - &gt;选择并“强制停止”运行服务的应用程序。但是,由于您的应用程序在此处已停止,因此服务实例也将被停止。
最后,我想提一下,在单例类中使用静态变量提到的方法对我有用。
答案 9 :(得分:6)
检查服务是否正在运行的正确方法是简单地询问它。在您的服务中实现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;
}
}
};
}
答案 10 :(得分:6)
onDestroy
,所以这没用!
例如:只需从Eclipse进行一次更改即可再次运行应用程序。使用SIG:9强行退出应用程序。
答案 11 :(得分:5)
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;
}
答案 12 :(得分:5)
首先,您不会尝试使用ActivityManager来访问服务。 (讨论here)
服务可以自己运行,绑定到一个Activity或两者都绑定。如果您的服务正在运行,则检入Activity的方法是创建一个接口(扩展Binder),您可以在其中声明Activity和Service都能理解的方法。您可以通过在声明“isServiceRunning()”的位置创建自己的接口来完成此操作。 然后,您可以将Activity绑定到您的Service,运行方法isServiceRunning(),如果它正在运行,Service将自行检查并为您的Activity返回一个布尔值。
您也可以使用此方法停止服务或以其他方式与服务进行交互。
我使用此tutorial来了解如何在我的应用程序中实现此方案。
答案 13 :(得分:4)
对于此处给出的用例,我们可以简单地使用stopService()
方法的返回值。如果存在指定的服务并且它被终止,则返回true
。否则它返回false
。因此,如果结果为false
,您可以重新启动服务,否则可以确保当前服务已停止。 :)如果你看一下this会更好。
答案 14 :(得分:4)
同样,如果他们使用待定意图,人们可能会发现更清洁的另一种选择(例如使用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
是您在类中私下定义的常量,用于标识与您的服务关联的待处理意图。
答案 15 :(得分:3)
以下是涵盖所有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(...);
}
答案 16 :(得分:2)
在kotlin中,您可以在伴侣对象中添加布尔变量,并从任何所需的类中检查其值:
companion object{
var isRuning = false
}
在创建和销毁服务时更改其值
override fun onCreate() {
super.onCreate()
isRuning = true
}
override fun onDestroy() {
super.onDestroy()
isRuning = false
}
答案 17 :(得分:2)
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)
答案 18 :(得分:1)
使用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 }
}
答案 19 :(得分:1)
检测服务是否正在运行的唯一高效/最快/干净方法是创建PING / PONG功能。
服务内部的实现Messenger或AIDL方法:isAlive()
-返回服务状态。
不要不要实施广播,因为它们可能会丢失。
答案 20 :(得分:1)
可以有多个具有相同类名的服务。
我刚创建了两个应用。第一个应用的包名称为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
的等号()方法。
@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;
}
答案 21 :(得分:0)
简单使用bind并不要创建自动 - 请参阅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.
注意:此方法仅用于调试或实现服务管理类型用户界面。
<强> 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<String, IBinder> getCommonServicesLocked() {
但没有&#34;活动&#34;只有窗口包装和报警..
所以我们需要回电话:
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;
}
简而言之:)
&#34;为已绑定的服务提供绑定器。此方法是同步的,如果不存在,则不会启动目标服务。&#34;
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;
}
*
答案 22 :(得分:0)
在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)
。
答案 23 :(得分:0)
在您的服务子类中,使用静态布尔值获取服务的状态,如下所示。
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)
}
}
}
答案 24 :(得分:0)
我的ActivityManager::getRunningServices
答案的Kotlin转换。将此功能放在活动中-
private fun isMyServiceRunning(serviceClass: Class<out Service>) =
(getSystemService(ACTIVITY_SERVICE) as ActivityManager)
.getRunningServices(Int.MAX_VALUE)
?.map { it.service.className }
?.contains(serviceClass.name) ?: false
答案 25 :(得分:0)
请使用此代码。
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;
}
答案 26 :(得分:0)
对于Kotlin,您可以使用以下代码。
MediaType.APPLICATION_JSON_VALUE
答案 27 :(得分:0)
如果服务属于另一个进程,或者APK使用基于ActivityManager的解决方案。
如果您有权访问其来源,只需使用基于静态字段的解决方案。但是使用布尔值我建议使用Date对象。当服务正在运行时,只需将其值更新为“now”,并在完成时将其设置为null。从活动中你可以检查它是否为空或日期太旧,这意味着它没有运行。
您还可以从您的服务发送广播通知,指示正在运行的进一步信息。
答案 28 :(得分:0)
这更适用于Intent Service调试,因为它们会产生一个线程,但也可能适用于常规服务。我发现这个帖子归功于Binging
在我的情况下,我玩了调试器并找到了线程视图。它有点像MS Word中的项目符号点图标。无论如何,您不必处于调试器模式即可使用它。单击该过程,然后单击该按钮。任何Intent Services都会在它们运行时显示,至少在模拟器上显示。
答案 29 :(得分:-1)
您可以使用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.
答案 30 :(得分:-4)
轻松一点......:)
我认为最合适的解决方案是在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);
}
没有特殊权限,没有循环......简单的方法,干净的解决方案:)
如果您需要更多信息,请参阅link
希望这有帮助。