如何检查服务是否在Android上运行?
-
03-07-2019 - |
题
如何检查后台服务是否正在运行?
我想要一个能够切换服务状态的Android活动 - 如果它处于打开状态,它可以让我打开它。
解决方案
不久前我遇到了同样的问题。由于我的服务是本地的,我最终只是在服务类中使用静态字段来切换状态,如hackbod所述这里
编辑(记录):
以下是hackbod提出的解决方案:
如果您的客户端和服务器代码属于同一个.apk,那么您就是 使用具体的Intent绑定到服务(一个指定 确切的服务类),然后您可以简单地设置您的服务 运行时客户端可以检查的全局变量。
我们故意没有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事件或Binders或静态变量的所有方法都无法可靠地运行,因为作为开发人员,您永远不会知道,当Android决定终止您的进程或调用哪些回调时。请注意“killable”和“killable”。 Android文档中生命周期事件表中的列。
知道了!
您必须调用 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或调用可以被服务捕获的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。
你可以使用这个(我还没试过,但我希望这有效):
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);
。
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;
}
我稍微修改了上面提到的一个解决方案,但传递了类而不是通用的字符串名称,以确保比较来自同一方法的字符串 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);
摘自 Android 文档:
如 sendBroadcast(Intent) ),但如果有任何接收器 Intent这个函数会阻塞并立即发送它们 在回来之前。
将此黑客视为“ping” Service
,因为我们可以同步广播,我们可以在UI线程上广播并同步获得结果。
<代码>服务代码>
@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"));
}
}
<代码>活动代码>
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;
}
}
我只想在@Snicolas的答案中添加一个注释。以下步骤可用于检查停止服务,无需调用 onDestroy()
。
-
onDestroy()
调用:转到设置 - &gt;申请 - &gt;正在运行的服务 - &gt;选择并停止服务。 -
onDestroy()
未调用:转到设置 - &gt;申请 - &gt;管理应用程序 - &gt;选择并“强制停止”您的服务运行的应用程序。但是,由于您的应用程序在此处已停止,因此服务实例也将被停止。
醇>
最后,我想提一下,在单例类中使用静态变量提到的方法对我有用。
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;
}
对于这里给出的用例,我们可以简单地使用 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
是您在类中私下定义的常量,用于标识与您的服务关联的待处理意图。
下面是一个优雅的黑客,涵盖了所有 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)
可以有多个具有相同类名的服务。
我刚创建了两个应用。第一个应用程序的包名称是 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;
}
这更适用于Intent Service调试,因为它们会产生一个线程,但也可能适用于常规服务。我发现这个帖子归功于Binging
在我的情况下,我玩了调试器并找到了线程视图。它有点像MS Word中的项目符号点图标。无论如何,您不必处于调试器模式即可使用它。单击该过程,然后单击该按钮。任何Intent Services都会在它们运行时显示,至少在模拟器上显示。
如果服务属于另一个进程,或者APK使用基于ActivityManager的解决方案。
如果您有权访问其来源,只需使用基于静态字段的解决方案。但是使用布尔值我建议使用Date对象。当服务正在运行时,只需将其值更新为“now”,并在完成时将其设置为null。从活动中你可以检查它是否为空或日期太旧,这意味着它没有运行。
您还可以从您的服务发送广播通知,指示正在运行的进一步信息。
在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)
。
简单使用bind with do not create auto - 请参阅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() {
但没有“活动”只有窗口包装和报警..
所以我们需要回电话:
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;
}
简而言之:)
&quot;为已绑定的服务提供绑定器。此方法是同步的,如果不存在,则不会启动目标服务。“
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;
}
*
在您的服务子类中使用静态布尔值来获取服务的状态,如下所示。
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 :: 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
请使用此代码。
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;
}
轻松一点......:)
我认为最合适的解决方案是在 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);
}
没有特殊权限,没有循环......简单的方法,干净的解决方案:)
如果您需要更多信息,请参阅链接
希望这有帮助。