Как сохранить обновления местоположения приемника GPS, когда приложение находится в фоновом режиме (API местоположения службы Google Play)

Извините, это длинный вопрос, но я надеюсь, что один из вас, экспертов, сможет помочь новичку, который тихо сходит с ума!!

У меня есть приложение для Android, которое использует фоновую службу для получения исправлений GPS (с использованием сервисов Google Play) каждые 20 секунд. Он сравнивает широту и долготу со значениями в списке и, если находит совпадение, отправляет широковещательную рассылку приемнику, который запускает активность переднего плана, чтобы предупредить пользователя.

Я использую фоновую службу, потому что обычно между предупреждениями пользователя проходит от 2 до 20 минут, и между ними нет взаимодействия с пользователем. Приложение использует действие переднего плана, чтобы пользователь мог выбрать нужные параметры, но затем закрывает все действия переднего плана, оставляя только фоновое действие.

Это хорошо работало на Android 4.3 на моем старом устройстве, но сейчас я обновляю его для Android 8 (Oreo) (тестирую на Sony Xperia XZ1Compact). я добавил

<uses-permission 
android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/

в манифест, и это заставило приложение запрашивать разрешение при первом запуске. Затем в настройках моего устройства приложение показывает, что разрешено исключение энергосбережения.

Код для фоновой службы (извините, его довольно много, но я включил его весь на случай, если это уместно!) выглядит следующим образом.

package com.barney.trackgps;

import android.Manifest;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.os.Bundle;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.util.Log;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;



public class ApiTrackService extends Service implements
    GoogleApiClient.ConnectionCallbacks, 
GoogleApiClient.OnConnectionFailedListener,
    LocationListener {

GoogleApiClient mLocationClient;
LocationRequest mLocationRequest = new LocationRequest();
public static final String ACTION_LOCATION_BROADCAST = 
ApiTrackService.class.getName() + "LocationBroadcast";
public static final String EXTRA_LATITUDE = "extra_latitude";
public static final String EXTRA_LONGITUDE = "extra_longitude";

int interval=20; //time between fixes in seconds


@Override
public void onDestroy(){
    super.onDestroy();
    stopSelf();
}


@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.v("sTag","Got to apitrack");
    mLocationClient = new GoogleApiClient.Builder(this)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(LocationServices.API)
            .build();
    mLocationRequest.setInterval(interval*1000);
    mLocationRequest.setFastestInterval(1000);
    int priority = LocationRequest.PRIORITY_HIGH_ACCURACY;
    mLocationRequest.setPriority(priority);
    mLocationClient.connect();
    return START_STICKY;
}


@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}


@Override
public void onConnected(Bundle dataBundle) {
    if (ActivityCompat.checkSelfPermission(this, 
Manifest.permission.ACCESS_FINE_LOCATION) != 
PackageManager.PERMISSION_GRANTED && 
ActivityCompat.checkSelfPermission(this, 
Manifest.permission.ACCESS_COARSE_LOCATION) != 
PackageManager.PERMISSION_GRANTED) {
        return;
    }

LocationServices.FusedLocationApi.requestLocationUpdates(mLocationClient, 
mLocationRequest, this);
}


@Override
public void onConnectionSuspended(int i) {
}


//to get the location change
@Override
public void onLocationChanged(Location location) {
    boolean locFound=false;
    Log.v("sTag","Got a fix");
        /*does stuff to compare latitude and longitude with places in a list 
        and sets locFound=true if it finds a match*/
    if (location != null) {
        GPSLog=GPSLog+"Found";
        if (locFound) {
            Log.v("sTag", "Sending broadcast");
            Intent intent = new Intent();
            intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
            intent.setAction("com.AboutMyJourney.posBroadcast");
            sendBroadcast(intent);
        }
    }
}


@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
    //Log.d(TAG, "Failed to connect to Google API");
}

}

Приложение работает нормально, пока одно из его действий переднего плана открыто и видно. Отслеживание с помощью logcat показывает, что фоновая служба получает исправления GPS, а код onLocationChanged (см. код выше) запускается каждые 20 секунд.

Приложение по-прежнему работает нормально, и метод onLocationChanged по-прежнему запускается каждые 20 секунд, если я разрешаю моему приложению закрывать все свои действия переднего плана и запускать только фоновую службу выше, пока другое (совершенно не связанное) приложение, которое вызывает GPS локации открыты и видны.

Однако, если у меня нет видимого на экране приложения, использующего GPS, отслеживание GPS прекращается; метод onLocationChanged больше не запускается, и я больше не получаю исправления GPS.

Если я затем открою одно из действий переднего плана моего приложения, GPS-отслеживание запустится снова, показывая, что фоновая служба не была убита.

Я предполагаю, что это как-то связано с изменениями, которые были внесены для реализации режима энергосбережения / DOZE, и что я просто не понимаю этого должным образом. Тем не менее, все советы, которые я могу найти, предполагают, что фоновая служба будет продолжать работать до тех пор, пока она разрешена в качестве исключения в белом списке устройства, но этого явно не происходит.

Я бы предпочел, чтобы фоновая служба работала, так как это означает наименьшую работу по написанию нового кода! Однако в некоторых ответах на похожие вопросы предлагается вместо этого использовать службу переднего плана с уведомлением. Удовлетворит ли это мое требование, чтобы пользовательский интерфейс не был виден (за исключением, может быть, уведомления) большую часть времени, только предлагая пользователю что-то делать каждые несколько минут, но оставляя ему или ей свободу делать другие вещи тем временем? Будет ли это нормально работать и в старых версиях Android?

Может ли кто-нибудь, кто разбирается в этом лучше меня (вероятно, нетрудно понять это лучше меня!) Помогите, пожалуйста?


person Chris S    schedule 04.09.2018    source источник


Ответы (2)


В соответствующей документации в режиме ожидания/дребезжания говорится

Приложение, занесенное в белый список, может использовать сеть и удерживать частичные блокировки пробуждения во время дремоты и ожидания приложения. Однако другие ограничения по-прежнему применяются к приложению из белого списка, как и к другим приложениям. Например, задания и синхронизация приложения из белого списка откладываются (на уровне API 23 и ниже), а его обычные тревоги AlarmManager не срабатывают.

Похоже, ваш вариант использования подпадает под «другие ограничения все еще применяются».

Ваше приложение разряжает батарею, когда оно не активно используется, и это именно то поведение, которое App Standby и Doze призваны противодействовать.

person mattm    schedule 04.09.2018

начиная с Android marshmallow Google представляет новые способы оптимизации батареи, такие как

  1. Режим сна
  2. Режим ожидания приложения

вы можете узнать о них больше через режим сна и ожидания и это в этих концепциях в спорном улучшении до Android Oreo. Когда Google анонсирует Android Oreo, они в значительной степени полагаются на способы оптимизации батареи, они представили новый Apis для обработки фоновых операций, таких как диспетчер работы и планировщик заданий, но они также входят в режим ожидания и ожидания. и работать в спящем режиме в окне обслуживания.
После долгих поисков и практики наилучшего способа отслеживать местоположение пользователя каждые 20 секунд вам нужно запустить службу переднего плана с липким уведомлением переднего плана. прийти в соответствии с интервалом. вот объяснение для Service, а также пример для запуск службы переднего плана

person Ramzy Hassan    schedule 04.09.2018
comment
Большое спасибо. Работает отлично! - person Chris S; 04.09.2018
comment
Большое спасибо. Работает отлично! Кому интересно, я вызвал ту же службу, которая была у меня до использования startForegroundService вместо StarService. В сервисе (в onCreate и в onStartCommand) я создал уведомление и вызвал startForeground. Затем я заменил трансляцию, которая запускала мою активность переднего плана через приемник, на простое намерение запустить активность переднего плана напрямую. Думаю, мне понадобится отдельная версия сервиса для старых версий Android. - person Chris S; 04.09.2018