如何检查后台服务是否正在运行?
我想要一个可切换服务状态的Android活动-它可以让我在关闭状态下将其打开,在开启状态下将其关闭。
getRunningTasks()
,那可能会。
如何检查后台服务是否正在运行?
我想要一个可切换服务状态的Android活动-它可以让我在关闭状态下将其打开,在开启状态下将其关闭。
getRunningTasks()
,那可能会。
Answers:
我不久前有同样的问题。因为我的服务是本地的,最后我简单地使用在服务类切换状态静态字段,如hackbod描述这里
编辑(记录):
这是hackbod提出的解决方案:
如果您的客户端和服务器代码是同一.apk的一部分,并且您使用具体的Intent(用于指定确切的服务类)绑定到服务,则只需在服务运行时将其设置为全局变量即可您的客户可以检查。
我们故意没有API来检查服务是否正在运行,因为当您想要执行类似的操作时,几乎会失败,您最终会在代码中出现竞争条件。
onDestroy()
不会调用该服务。因此,在这种情况下无法更新静态变量,从而导致行为不一致。
我在活动中使用以下内容:
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”列。
getRunningServices
已被弃用。该答案需要更新才能获得较新的版本。
得到它了!
您必须要求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建议的那样,最好的方法是在服务类中具有一个静态字段,以了解服务是否已启动。
为了使它更整洁,我建议通过非常懒惰的获取将服务转换为单例:也就是说,通过静态方法在所有单例实例上都没有实例化。您的服务/单例的静态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);
在代码下添加。
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线程上同步广播并获取结果。
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
in Service.onCreate()
和to false
in 的服务上的静态布尔字段,Service.onDestroy()
因为它要简单得多。
我对上述解决方案之一进行了一些修改,但传递了类而不是通用字符串名称,以确保比较来自同一方法的字符串 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>
检查服务是否正在运行的正确方法是简单地询问它。在您的服务中实现一个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是否服务正在运行的方法是通过创建一个接口(扩展Binder),在该接口中声明Activity和Service都可以理解的方法。您可以通过在声明例如“ isServiceRunning()”的地方创建自己的接口来实现。然后,您可以将Activity绑定到Service,运行方法isServiceRunning(),Service将检查自身是否正在运行,并将布尔值返回给Activity。
您还可以使用此方法停止服务或以其他方式与其交互。
我使用本教程来学习如何在应用程序中实现此方案。
同样,如果人们使用未决的意图(例如使用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(...);
}
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
可以确保当前服务已停止,则可以重新启动服务。:)这将是更好,如果你看看这个。
另一种使用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)
在服务子类中,使用静态布尔值获取服务的状态,如下所示。
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)
}
}
}
对于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
}
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)
ActivityManager.getRunningServices
自Android O以来已弃用
可以有多个具有相同类名的服务。
我刚刚创建了两个应用程序。第一个应用的软件包名称为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
两个包名和类名进行比较。而且,设备上不能安装两个具有相同程序包名称的应用程序。
的equals()方法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;
}
请使用此代码。
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;
}
在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()
仍会运行
简单使用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<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中挖洞,所以直到我剖析了剩余的呼叫之后,我才暂停回答。
但是检查服务是否正在运行的最佳方法是创建绑定(如果未创建绑定,则服务不存在)-并通过绑定查询服务的状态(在其状态上使用存储的内部标志)。
我发现那些有趣:
/**
* 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;
}
*
我的Kotlin转换的ActivityManager::getRunningServices
基础答案。将此功能放在活动中-
private fun isMyServiceRunning(serviceClass: Class<out Service>) =
(getSystemService(ACTIVITY_SERVICE) as ActivityManager)
.getRunningServices(Int.MAX_VALUE)
?.map { it.service.className }
?.contains(serviceClass.name) ?: 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);
}
没有特殊权限,没有循环...简单的方法,干净的解决方案:)
如果您需要其他信息,请参阅链接
希望这可以帮助。
onDestroy
在服务被终止时并不总是被调用。例如,我看到我的服务在内存不足的情况下onDestroy
被杀死而没有被调用。