如何查看后台服务是否正在运行?
我想要一个切换服务状态的Android活动——它让我打开它,如果它是关闭的,如果它是关闭的。
如何查看后台服务是否正在运行?
我想要一个切换服务状态的Android活动——它让我打开它,如果它是关闭的,如果它是关闭的。
当前回答
简单使用绑定不创建自动查看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.
注意:此方法仅用于调试或实现服务管理类型的用户界面。
附注:android文档具有误导性,我已经在谷歌跟踪器上打开了一个问题,以消除任何疑问:
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的method中调用:
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,所以在我解析rest call之前,我暂停了我的回答。
但是检查服务是否正在运行的最好方法是创建bind(如果没有创建bind,则服务不存在)-并通过bind查询服务的状态(在其状态上使用存储的内部标志)。
更新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;
}
简而言之:)
“为已经绑定的服务提供绑定器。此方法是同步的,如果目标服务不存在,则不会启动该服务。”
(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转换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
一个小补语是:
我的目标是知道一个服务是否在运行,如果它没有运行。
调用bindService或调用一个可以被服务捕获的意图不是一个好主意,因为它会在服务未运行时启动服务。
因此,正如miracle2k所建议的,最好是在服务类中有一个静态字段,以知道服务是否已经启动。
为了使它更简洁,我建议用非常非常懒惰的抓取来转换单例中的服务:也就是说,通过静态方法完全没有实例化单例实例。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。
onDestroy并不总是在服务中调用,所以这是无用的!
例如:只需要对Eclipse做一点更改就可以再次运行应用程序。使用SIG: 9强制退出应用程序。
/**
* 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;
}
如果服务属于另一个进程或APK,则使用基于ActivityManager的解决方案。
如果您可以访问它的源,只需使用基于静态字段的解决方案。但是我建议使用Date对象来代替布尔值。在服务运行时,只需将其值更新为'now',当它完成时将其设置为null。从活动中,您可以检查它是否为空或日期太旧,这将意味着它没有运行。
您还可以从您的服务发送广播通知,表明正在运行以及进一步的信息,如进度。