Служба Foreground не получает обновления местоположения на Android 7. 0+, когда экран выключен
Я пытаюсь создать приложение Android, которое непрерывно регистрирует данные о местоположении устройства в реальном времени, пока экран устройства выключен. Мой код работает правильно с Android 6.0 и ранее, но кажется, что Android 7. 0+ ломает мое приложение.
Я внедрил службу Android foreground, которая использует wakelock и подписывается на API Google FusedLocation. Когда экран выключен, callback onLocationChanged
никогда не запускается.
Кто-нибудь видел решение этой проблемы? Я попытался отключить Оптимизацию батареи с помощью настроек устройства Android для своего приложения, а также для Google Services Framework и Fused Location.
public class ForegroundLocationService extends Service implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
LocationListener {
private static final String TAG = ForegroundLocationService.class.getSimpleName();
// the notification id for the foreground notification
public static final int GPS_NOTIFICATION = 1;
// the interval in seconds that gps updates are requested
private static final int UPDATE_INTERVAL_IN_SECONDS = 15;
// is this service currently running in the foreground?
private boolean isForeground = false;
// the google api client
private GoogleApiClient googleApiClient;
// the wakelock used to keep the app alive while the screen is off
private PowerManager.WakeLock wakeLock;
@Override
public void onCreate() {
super.onCreate();
// create google api client
googleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
// get a wakelock from the power manager
final PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!isForeground) {
Log.v(TAG, "Starting the " + this.getClass().getSimpleName());
startForeground(ForegroundLocationService.GPS_NOTIFICATION,
notifyUserThatLocationServiceStarted());
isForeground = true;
// connect to google api client
googleApiClient.connect();
// acquire wakelock
wakeLock.acquire();
}
return START_REDELIVER_INTENT;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
Log.v(TAG, "Stopping the " + this.getClass().getSimpleName());
stopForeground(true);
isForeground = false;
// disconnect from google api client
googleApiClient.disconnect();
// release wakelock if it is held
if (null != wakeLock && wakeLock.isHeld()) {
wakeLock.release();
}
super.onDestroy();
}
private LocationRequest getLocationRequest() {
LocationRequest locationRequest = LocationRequest.create();
// we always want the highest accuracy
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// we want to make sure that we get an updated location at the specified interval
locationRequest.setInterval(TimeUnit.SECONDS.toMillis(0));
// this sets the fastest interval that the app can receive updates from other apps accessing
// the location service. for example, if Google Maps is running in the background
// we can update our location from what it sees every five seconds
locationRequest.setFastestInterval(TimeUnit.SECONDS.toMillis(0));
locationRequest.setMaxWaitTime(TimeUnit.SECONDS.toMillis(UPDATE_INTERVAL_IN_SECONDS));
return locationRequest;
}
private Notification notifyUserThatLocationServiceStarted() {
// pop up a notification that the location service is running
Intent notificationIntent = new Intent(this, NotificationActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
final Notification.Builder builder = new Notification.Builder(this)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle(getString(R.string.foreground_location_service))
.setContentText(getString(R.string.service_is_running))
.setContentIntent(pendingIntent)
.setWhen(System.currentTimeMillis());
final Notification notification;
if (Build.VERSION.SDK_INT < 16) {
notification = builder.getNotification();
} else {
notification = builder.build();
}
return notification;
}
@Override
public void onConnected(@Nullable Bundle bundle) {
try {
// request location updates from the fused location provider
LocationServices.FusedLocationApi.requestLocationUpdates(
googleApiClient, getLocationRequest(), this);
} catch (SecurityException securityException) {
Log.e(TAG, "Exception while requesting location updates", securityException);
}
}
@Override
public void onConnectionSuspended(int i) {
Log.i(TAG, "Google API Client suspended.");
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Log.e(TAG, "Failed to connect to Google API Client.");
}
@Override
public void onLocationChanged(Location location) {
Log.e(TAG, "onLocationChanged: " + location.toString());
}
}
Я включил полный рабочий образец кода, который я использую для тестирования здесь: https://github.com/joshuajwitter/ForegroundLocationService
У FitBit, похоже, такая же проблема, вот дискуссия
Вот ресурсы, которые я уже рассмотрел:
Образцы кода для Android O
Храните приложение в фоновом режиме на Android все время
Android, получить местоположение, когда экран выключен
Фоновые ограничения местоположения
Непоследовательные обновления местоположения в режиме переднего плана при глубоком сне (дозе)
Служба Foreground не получает обновления местоположения в режиме Doze в Android O
EDIT 2018.01.30: Я также попытался запустить ForegroundLocationService в своем собственном процессе:
<service
android:name="foregroundlocation.stackoverflowexample.com.foregroundlocation.ForegroundLocationService"
android:enabled="true"
android:stopWithTask="false"
android:process=":ForegroundLocationService"
android:exported="false" >
</service>
а также обновление моего кода для использования FusedLocationProviderClient
, все равно не повезло.
Ответы
Ответ 1
Я испытывал эту проблему на эмулированных устройствах, работающих под управлением 7.0 и 8.0, и постоянный обратный вызов местоположения не запускался, когда экран был выключен (cmd + P). Наконец, я смог получить реальное устройство 7.0 (Galaxy S7), и я не смог воспроизвести проблему. Извините, если я потратил впустую любое время, видимо, это проблема с эмулятором.
Ответ 2
У меня была такая же проблема, и я нашел рабочее решение. Чтобы прослушать обновления местоположения, вы не должны вызывать этот метод:
public Task<Void> requestLocationUpdates (LocationRequest request,
LocationCallback callback, Looper looper)
Вы должны использовать ожидающее намерение вместо обратного вызова, то есть вы должны вызвать метод:
public Task<Void> requestLocationUpdates (LocationRequest request,
PendingIntent callbackIntent)
Оформить ссылку на ссылку: https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderClient.html#requestLocationUpdates(com.google.android.gms.location.LocationRequest, android.app.PendingIntent)
Ответ 3
LocationServices.FusedLocationApi устарел.
Я просто реорганизовал свое приложение следующим образом. Надеюсь, поможет:
Это то, что я вызываю в onCreate-Method моего сервиса:
public void start(Context ctx) {
FusedLocationProviderClient client = LocationServices
.getFusedLocationProviderClient(ctx);
//Define quality of service:
LocationRequest request = LocationRequest.create();
request.setInterval(1000); //Every second
request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
if (locCallback != null) {
client.removeLocationUpdates(locCallback);
}
locCallback = createNewLocationCallback();
client.requestLocationUpdates(request, locCallback, null);
}
И это метод создания обратного вызова:
private LocationCallback createNewLocationCallback() {
LocationCallback locationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult result) {
Location loc = result.getLastLocation();
// Do something with the location (may be null!)
}
};
return locationCallback;
}
Полагаю, я также нашел это где-то в качестве примера, но не сохранил ссылку.
У меня нет Oreo самостоятельно. Но некоторые тестеры подтвердили, что он работает.
Чтобы сохранить сервис на переднем плане, я просто называю "startForeground" впоследствии в методе onCreate моей службы.
Ответ 4
Одним из решений может быть следующее:
проверьте, не было ли получено местоположение в течение 2 минут, остановите/снова запустите обновление всего начального местоположения. хотя передний план должен решить проблему
Ответ 5
Кто-нибудь сталкивался с этой проблемой на Android Pie9? Работает нормально на устройствах O и ниже. Как преодолеть это?