Как сохранить обновления местоположения приемника GPS, когда приложение находится в фоновом режиме (API определения местоположения службы Google Play)
Извините, это длинный вопрос, но я надеюсь, что один из вас, экспертов, сможет помочь новичку, который просто сходит с ума!!
У меня есть приложение для Android, которое использует фоновый сервис для получения исправлений GPS (с использованием Google Play Services) каждые 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?
Может ли кто-нибудь, кто понимает это лучше меня (возможно, нетрудно понять это лучше, чем я!), Помогите, пожалуйста?
2 ответа
В соответствующей документации по дежурному режиму / дозе сказано
Приложение, занесенное в белый список, может использовать сеть и частично блокировать пробуждение во время Doze и App Standby. Однако к приложению, внесенному в белый список, по-прежнему применяются другие ограничения, как и к другим приложениям. Например, задания и синхронизации приложения из белого списка откладываются (на уровне API 23 и ниже), и его обычные тревоги AlarmManager не срабатывают.
Похоже, ваш вариант использования подпадает под "другие ограничения по-прежнему применяются".
Ваше приложение разряжает батарею, когда оно не активно используется, что в точности соответствует поведению App Standby и Doze, которые предназначены для противодействия.
Начиная с Android зефир Google представить новые способы оптимизации батареи, такие как
- Режим доза
- Режим ожидания приложения
Вы можете узнать о них больше через Doze и Stand by Mode, и это в этих концепциях будет постоянно улучшаться до Android Oreo. когда Google объявляет Android Oreo, они в значительной степени полагаются на способы оптимизации батареи, они представили новый Apis для обработки фоновых операций, таких как менеджер работ и планировщик заданий, но они также входят в режим ожидания и ожидания. и работать в окне обслуживания дремального режима.
после долгого поиска и отработки наилучшего способа отслеживания местоположения пользователя каждые 20 секунд необходимо запустить службу переднего плана с прикрепленным уведомлением переднего плана. Этот способ не завершается режимом ожидания или ожидания, и он сохраняет обновленное местоположение в соответствии с интервал. Вот объяснение службы, а также пример запуска службы переднего плана.