Как проверить, работает ли сервис на Android?
Как проверить, работает ли фоновая служба (на Android)?
Я хочу, чтобы активность Android изменяла состояние службы - она позволяла мне включать его, если он выключен, и если он включен.
36 ответов
У меня была такая же проблема не так давно. Так как мой сервис был локальным, я просто использовал статическое поле в классе сервиса для переключения состояния, как описано здесь hackbod
РЕДАКТИРОВАТЬ (для записи):
Вот решение, предложенное hackbod:
Если ваш клиентский и серверный код является частью одного и того же.apk, и вы привязываетесь к сервису с конкретным намерением (которое указывает точный класс сервиса), тогда вы можете просто сделать так, чтобы ваш сервис установил глобальную переменную, когда он работает, Ваш клиент может проверить.
У нас намеренно нет 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, или Binder, или статические переменные, не будут работать надежно, потому что как разработчик вы никогда не знаете, когда Android решает убить ваш процесс или какой из упомянутых обратных вызовов вызван или нет. Обратите внимание на столбец "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 или вызов намерения, которое может быть перехвачено службой, не является хорошей идеей, так как он запустит службу, если она не запущена.
Итак, как предположил 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
Это элегантное решение, но оно актуально только в том случае, если у вас есть доступ к классу обслуживания и только для классов, кроме приложения / пакета службы. Если ваши классы находятся вне приложения / пакета службы, вы можете запросить 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, если уже есть запущенная служба. Если нет, возвращается ноль.
Смотрите публичный абстрактный ComponentName startService (сервис Intent).
Это не похоже на проверку, я думаю, потому что она запускает службу, так что вы можете добавить 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;
}
Это выдержка из документов Android:
Подобно sendBroadcast (Intent), но если для Intent есть приемники, эта функция заблокирует их и немедленно отправит перед возвратом.
Думайте об этом взломе как о "пингующем"Service
поскольку мы можем транслировать синхронно, мы можем транслировать и получать результат - синхронно - в потоке пользовательского интерфейса.
Service
@Override
public void onCreate() {
LocalBroadcastManager
.getInstance(this)
.registerReceiver(new ServiceEchoReceiver(), IntentFilter("ping");
}
private class ServiceEchoReceiver{
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){
//try and run the service
}
}
private BroadcastReceiver pong = new BroadcastReceiver(){
public void onReceive (Context context, Intent intent) {
serviceRunning = true;
}
}
Другой подход с использованием котлина. Вдохновленный ответами других пользователей
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 }
}
Как расширение котлина
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)
Правильный способ проверить, работает ли служба, - просто спросить ее. Внедрите в вашу службу BroadcastReceiver, который отвечает на запросы вашей работы. Зарегистрируйте 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;
}
}
};
}
Я немного изменил одно из представленных выше решений, но передал класс вместо общего имени строки, чтобы быть уверенным в сравнении строк, исходящих из одного и того же метода. 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);
Прежде всего, вы не должны пытаться связаться с сервисом с помощью ActivityManager. (Обсуждается здесь)
Службы могут работать самостоятельно, быть привязаны к действию или к обоим. Чтобы проверить в Деятельности, работает ли ваша Служба или нет, нужно создать интерфейс (расширяющий Binder), в котором вы объявляете методы, понятные как Службе, так и Службе. Вы можете сделать это, создав собственный интерфейс, в котором вы объявляете, например, "isServiceRunning()". Затем вы можете привязать свою активность к своей службе, запустить метод isServiceRunning(), служба сама проверит, запущена она или нет, и вернет логическое значение вашей активности.
Вы также можете использовать этот метод, чтобы остановить службу или взаимодействовать с ней другим способом.
Я использовал этот учебник, чтобы узнать, как реализовать этот сценарий в моем приложении.
Опять же, еще одна альтернатива, которую люди могут найти чище, если они используют ожидаемые намерения (например, с 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
это константа, которую вы определяете в частном порядке в своем классе, чтобы определить ожидающие намерения, связанные с вашим сервисом.
Я просто хочу добавить примечание к ответу @Snicolas. Следующие шаги могут быть использованы для проверки остановки службы с / без вызова onDestroy()
,
onDestroy()
Вызов: перейдите в Настройки -> Приложение -> Запущенные сервисы -> Выберите и остановите сервис.onDestroy()
не вызывается: перейдите в "Настройки" -> "Приложение" -> "Управление приложениями" -> "Выбрать" и "принудительно остановить" приложение, в котором запущена ваша служба. Однако, поскольку ваше приложение здесь остановлено, экземпляры службы также будут остановлены.
Наконец, я хотел бы отметить, что упомянутый там подход с использованием статической переменной в одноэлементном классе работает для меня.
Ниже элегантный хак, который охватывает все 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(...);
}
onDestroy
не всегда вызывается в сервисе, так что это бесполезно!
Например: просто запустите приложение еще раз с одним изменением из Eclipse. Приложение принудительно закрывается с помощью SIG: 9.
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
иначе он уверен, что текущий сервис был остановлен.:) Было бы лучше, если вы посмотрите на это.
В kotlin вы можете добавить логическую переменную в сопутствующий объект и проверить ее значение из любого нужного вам класса:
companion object{
var isRuning = false
}
Измените его значение, когда служба создается и уничтожается
override fun onCreate() {
super.onCreate()
isRuning = true
}
override fun onDestroy() {
super.onDestroy()
isRuning = false
}
Для котлина вы можете использовать приведенный ниже код.
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)
В своем подклассе службы Используйте статическое логическое значение, чтобы получить состояние службы, как показано ниже.
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)
}
}
}
простое использование связать с не создавать автоматически - см. пс. и обновить...
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 вводит в заблуждение. Я открыл проблему на трекере 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();
у меня сейчас нет времени копаться в с, так что пока я не проанализирую покой, я приостановлю свой ответ.
но лучший способ проверить, работает ли служба, - это создать привязку (если привязка не создана, служба не существует) и запросить службу о ее состоянии через привязку (используя хранимый внутренний флаг для этого состояния).
обновление 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;
}
короче:)
"Предоставьте привязку для уже связанной службы. Этот метод является синхронным и не запускает целевую службу, если она отсутствует".
public IBinder peekService(служба намерений, String resolvedType, String callPackage) выдает 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;
}
*
Внутри 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)
из любого класса.
Пожалуйста, используйте этот код.
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;
}
Может быть несколько служб с одинаковым именем класса.
Я только что создал два приложения. Имя пакета первого приложения 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;
}
Единственный эффективный / самый быстрый / чистый способ определить, запущена ли служба, - это создать функциональность PING/PONG.
Реализуйте Messenger или метод AIDL внутри службы: isAlive()
- которые возвращают состояние сервиса.
Вы не осуществлять вещание, потому что они могут быть пропущены.
Вот хорошее решение, которое я придумал, но оно работает только для служб, работающих в отдельных процессах. Это может быть достигнуто путем добавления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())
}
Тогда он работает идеально.
Если у вас многомодульное приложение и вы хотите знать, запущена служба или нет из модуля, который не зависит от модуля, содержащего службу, вы можете использовать эту функцию:
fun isServiceRunning(context: Context, serviceClassName: String): Boolean {
val manager = ContextCompat.getSystemService(
context,
ActivityManager::class.java
) ?: return false
return manager.getRunningServices(Integer.MAX_VALUE).any { serviceInfo ->
serviceInfo.service.shortClassName.contains(vpnServiceClassName)
}
}
Использование для
MyService
оказание услуг:
isServiceRunning(context, "MyService")
Эта функция может работать неправильно, если имя класса службы изменилось, а вызывающая функция не изменилась соответствующим образом.
Мое решение использует . Это похоже на метод пинг-понга с использованием устаревшего методаLocalBroadcastManager
.
При использовании для проверки связи службы происходит следующее:
Если сервис жив:
- Система отправляет действие ping. Поскольку для действия ping зарегистрирован один широковещательный приемник, т. е. служба, действие ping будет отправлено ему в первую очередь.
- Служба
pingActionBroadcastReceiver
получает действие ping и устанавливает для действия ping значение . - Поскольку больше нет широковещательных получателей, которые могли бы обработать действие ping, система отправляет действие ping обратно в аргумент, который мы предоставили , т.е.
- получает действие ping, для которого установлено значение
1
.
Если служба НЕ активна:
- Система отправляет действие ping. Поскольку для действия ping не зарегистрировано ни одного широковещательного приемника, система отправляет действие ping обратно в аргумент, который мы предоставили
sendOrderedBroadcast
'sresultReceiver: BroadcastReceiver?
, то есть . -
pingActionResultBroadcastReceiver
получает действие ping со своимresultCode
равен аргументу, который мы предоставилиsendOrderedbroadcast
'sinitialCode: Int
который0
.
- Система отправляет действие ping. Поскольку для действия ping не зарегистрировано ни одного широковещательного приемника, система отправляет действие ping обратно в аргумент, который мы предоставили
Шаги:
- Настройте часть понга: создайте
BroadcastReceiver
для получения действия ping вService
вы хотели бы проверить.
class SomeService : Service() {
// instantiate your ping action broadcast receiver
private val pingActionBroadcastReceiver = PingActionBroadcastReceiver()
override fun onCreate() {
// ...
/*
register the ping action broadcast receiver
you may set the `flags` to whatever you want depending
your use case, e.g. if you want other apps to be able
to check for your service's presence, set the `flags` to
`ContextCompat.RECEIVER_EXPORTED` instead
*/
ContextCompat.registerReceiver(
/* context = */ this,
/* receiver = */ pingActionBroadcastReceiver,
/* filter = */ IntentFilter(PING_ACTION),
/* flags = */ ContextCompat.RECEIVER_NOT_EXPORTED,
)
// ...
}
override fun onDestroy() {
// ...
// don't forget to unregister your broadcast receiver
unregisterReceiver(pingActionBroadcastReceiver)
// ...
}
private class PingActionBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (!isOrderedBroadcast) {
/*
we only want broadcasts that were sent using
`sendOrderedBroadcast`
*/
return
}
/*
this is the pong part
we call `setResult` with `code` equal to `1` because
we want to tell the broadcaster that the service is alive.
you may set `data: String?` and `extras: Bundle?` to
anything you want depending on your use case.
*/
setResult(
/* code = */ 1,
/* data = */ null,
/* extras = */ null,
)
/*
if you're only interested in the result code just set
`resultCode` to `1` instead of calling `setResult`.
*/
resultCode = 1
}
}
companion object {
const val PING_ACTION = "<your ping action>"
}
// ...
}
- Настройте часть ping. В этом примере кода мы хотим проверить, запускается ли служба каждый раз, когда пользователь переходит к нашей активности.
class SomeActivity : AppCompatActivity() {
// instantiate your ping action result broadcast receiver
private val pingActionResultBroadcastReceiver =
PingActionResultBroadcastReceiver()
override fun onStart() {
// ...
// this is the ping part
sendOrderedBroadcast(
/* intent = */ Intent(SomeService.PING_ACTION),
/* receiverPermission = */ null,
/* resultReceiver = */ pingActionResultBroadcastReceiver,
/* scheduler = */ null,
/* initialCode = */ 0,
/* initialData = */ null,
/* initialExtras = */ null,
)
// ...
}
private inner class PingActionResultBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (resultCode == 1) {
/*
do something if the service is alive
`resultCode` is equal to `1` since it's the
integer value we set from the service's ping
action broadcast receiver
*/
} else if (resultCode == 0) {
/*
do something if the service is NOT alive
`resultCode` is equal to `0` since it's the
`initialCode` integer value we passed to
the `sendOrderedBroadcast` call above
*/
}
}
}
// ...
}
public static boolean isServiceRunning;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
isServiceRunning = true;
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
isServiceRunning = false;
}
ИЛИ ЖЕ
если не хочу использовать статические, то пользователь SharedPreferences
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
PrefManager.getPref().saveBoolean(AppConstants.Pref.IS_SERVICE_RUNNING, true);
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
PrefManager.getPref().saveBoolean(AppConstants.Pref.IS_SERVICE_RUNNING, false);
}
PrefManager - мой синглтон-класс для управления всеми предпочтениями.