Неубиваемый сервис в текущих реалиях

Нужно сделать сервис который остаётся активным, даже если телефон находится в режиме ожидания. То есть заблокирован и лежит в кармане. Или телефон перезагрузился/выключился, сервис перезапустился. В сервисе использую alarmmanager который раз в минуту вызывает определённые методы(получение gps и отправка на сервер). Но пока решил не торопится с сервисам как таковым. Может кто то уже сталкивался с подобным? Как обходили Doze Mode? Может есть примеры? Сейчас сервис написан так(пока даже не запускал): Запускается активность, нажимается кнопка, запускается сервис. В сервисе в oncreate вызывается alarmmanager и выполняет раз в минуту методы. Для предотвращения убийства используется:

 @Override
public void onDestroy()
{
    super.onDestroy();
    startService(new Intent(this, GpsService.class));
}

@Override
public void onTaskRemoved(Intent rootIntent) {
    super.onTaskRemoved(rootIntent);
    startService(new Intent(this, GpsService.class));
}

Верен ли сейчас подход? Или пока не поздно лучше делать иначе? Дополняю свой вопрос. Администрация, если нужно создать новый то напишите пожалуйста. Итак, дремота вроде как побеждена... Для получения координат использую следующее:

public void requsetLocationUpdate()
{
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
        fusedLocationProviderClient.requestLocationUpdates(locationRequest, new LocationCallback() {
            @Override
            public void onLocationResult(LocationResult locationResult) {
                super.onLocationResult(locationResult);
                locations = locationResult.getLocations();
                currentLat = locations.get(0).getLatitude();
                currentLon = locations.get(0).getLongitude();
            }
        }, Looper.myLooper());
    }
}

Вызываю его в OnStartComand. Предвидел что OnLocationResult поведёт себя так, а именно будет вызываться сам по себе не взирая ни на что... В методе OnCreate прописал следующее:

fusedLocationProviderClient = new FusedLocationProviderClient(this);
    locationRequest = new LocationRequest();
    locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    locationRequest.setFastestInterval(1000*60);
    locationRequest.setInterval(1000*60);

Сервис пробуждается по сигналу тревоги AlarmManager, вызывает метод requsetLocationUpdate() он работает как положено. Но потом вызывается ещё несколько раз и типо обновляет координаты. Выявил данный косяк с помощью отладчика android studio. Вижу такой выход: вызывать removelocationupdates после получения координат. Но тогда появляется проблема с синхронизацией потоков(Метод отправки координат на сервер вызывается в том же OnStartCommand естественно в потоке). В итоге происходит следующее, отправляются координаты полученные ранее, а новые просто висят, и отправятся в следующий раз. И всё по новой...


Ответы (2 шт):

Автор решения: Vlad Orlinskas

Еще можно успеть в ваш поезд. Ориентируйтесь на API 29 (до осени это еще проходит). Вас интересует Foreground Service. Важное замечание, обязательно наличие видимого notification (оповещения), которое висит в шторке. Своему бизнесу попробуйте это обосновать, покажите примеры gps трекеров. Если будете работать с местоположением, вам необходимо разрешение android.permission.ACCESS_BACKGROUND_LOCATION Это новое разрешение для доступа к местоположению в фоне, тема достойна отдельного вопроса.

Показываю вам пример кода, которые можно использовать. Для инъекции используется dagger 2.28.3

class EventRecorderService : LifecycleService() {

  // тут все ваши зависимости, обычно это livedata которая поставляет какие-то данные, например местоположение.

override fun onCreate() {
        super.onCreate()
        AndroidInjection.inject(this)
        // подписываетесь и обрабатываете результат вашей livedata
}

override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        // создание того самого висящего в шторке оповещения
        val notification = NotificationHelper.trackingNotification(this)
            .setContentTitle(getString(R.string.notification_title))
            .setContentText(getString(R.string.notification_text))
            .setVibrate(longArrayOf(0L))
            .build()
        startForeground(NOTIFICATION_ID, notification)

        if (intent.action == null) {
            Timber.e("action was null, flags = $flags +  bits = ${Integer.toBinaryString(flags)}")
            return START_STICKY
        }

        when (intent.action) {
            KEY_START -> startTracking()
            KEY_STOP -> stopTracking()
        }

        return super.onStartCommand(intent, flags, startId)
    }

private fun stopTracking() {
        // пример оставновки сервиса
        stopForeground(true)
        stopSelf()
    }

companion object {
        private const val NOTIFICATION_ID = 1
        private const val KEY_START = "START"
        private const val KEY_STOP = "STOP"

        fun start(context: Context) {
            sendAction(context, KEY_START)
        }

        fun stop(context: Context) {
            sendAction(context, KEY_STOP)
        }

        private fun sendAction(context: Context, action: String) {
            val intent = Intent(context, EventRecorderService::class.java)
            intent.action = action
            ContextCompat.startForegroundService(context, intent)
        }

        @Suppress("DEPRECATION")
        fun isRunning(context: Context): Boolean {
        // это проверка запущен ли сервис, могут быть отключены оповещения для вашего приложения, это нужно проверять
            val manager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
            for (service in manager.getRunningServices(Integer.MAX_VALUE)) {
                if (EventRecorderService::class.java.name == service.service.className) {
                    return true
                }
            }
            return false
        }

        @WorkerThread
        fun autoLaunchService(context: Context) {
            // тут некая логика запуска, которая решит стартовать сервис или нет
            start(context)
    }
}

autoLaunchService должен запускаться с некой переодичностью с помощью androidx.work.Worker, например вот так

fun enqueueServiceCheck() {
        WorkManager.getInstance(context).cancelAllWorkByTag(TAG_SERVICE_CHECK)

        val worker = PeriodicWorkRequestBuilder<ServiceCheckWorker>(15, TimeUnit.MINUTES)
            .addTag(TAG_SERVICE_CHECK)
            .build()

        WorkManager.getInstance(context).enqueue(worker)
    }
→ Ссылка
Автор решения: Wlad

https://androidwave.com/foreground-service-android-example/
только если так и только на API 29.
и то, если попадется оболочка Redmi MIUI, то ничего не поможет.

еще важным моментом в работе с фоновыми сервисами важно "есть ли запущенное Активити?"
поэтому рекомендую переписать кнопку "Назад" словно она является кнопкой "Домой".
приложение должно быть в состоянии onPause. это увеличит шансы "жизни" Сервиса.

→ Ссылка