如何查看后台服务是否正在运行?
我想要一个切换服务状态的Android活动——它让我打开它,如果它是关闭的,如果它是关闭的。
如何查看后台服务是否正在运行?
我想要一个切换服务状态的Android活动——它让我打开它,如果它是关闭的,如果它是关闭的。
当前回答
对于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
}
其他回答
如果服务属于另一个进程或APK,则使用基于ActivityManager的解决方案。
如果您可以访问它的源,只需使用基于静态字段的解决方案。但是我建议使用Date对象来代替布尔值。在服务运行时,只需将其值更新为'now',当它完成时将其设置为null。从活动中,您可以检查它是否为空或日期太旧,这将意味着它没有运行。
您还可以从您的服务发送广播通知,表明正在运行以及进一步的信息,如进度。
一个小补语是:
我的目标是知道一个服务是否在运行,如果它没有运行。
调用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。
你可以使用这个(我还没有尝试过,但我希望它能工作):
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 (Intent服务)。
这和检查不一样,因为它正在启动服务,所以你可以添加stopService(someIntent);按照规定。
可以有多个具有相同类名的服务。
我刚刚创建了两个应用程序。第一个应用程序的包名是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;
}
ComponentName
这是我提出的一个很好的解决方案,但它只适用于运行在单独进程中的服务。这可以通过在manifest中添加一个android:process属性来实现。
<service
android:name=".ExampleService"
android:process="com.example.service"
...
现在,您的服务将运行在具有给定名称的单独进程中。从你的应用程序你可以打电话
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
activityManager.runningAppProcesses.any { it.processName == "com.example.service" }
如果服务正在运行,则返回true,否则返回false。
重要提示:请注意,它将显示您的服务是何时启动的,但当您禁用它时(即在系统从它解除绑定后),该进程仍然是活跃的。所以你可以简单地强制移除它:
override fun onUnbind(intent: Intent?): Boolean {
stopSelf()
return super.onUnbind(intent)
}
override fun onDestroy() {
super.onDestroy()
killProcess(Process.myPid())
}
然后它就完美地工作了。