webentwicklung-frage-antwort-db.com.de

Der Vordergrunddienst empfängt keine Standortaktualisierungen in Android 7.0 und höher, wenn der Bildschirm ausgeschaltet ist

Ich versuche, eine Android-Anwendung zu erstellen, die fortlaufend Gerätestandortdaten in Echtzeit protokolliert, während der Bildschirm des Geräts ausgeschaltet ist. Mein Code funktioniert ordnungsgemäß mit Android 6.0 und früheren Versionen, aber es scheint, dass Android 7.0+ meine App bricht. 

Ich habe einen Android-Vordergrunddienst implementiert, der einen Wakelock verwendet und die Google FusedLocation-API abonniert. Wenn der Bildschirm ausgeschaltet ist, wird der onLocationChanged-Callback nie ausgelöst.

Hat jemand eine Lösung für dieses Problem gesehen? Ich habe versucht, die Batterieoptimierung über die Android-Geräteeinstellungen für meine App sowie für das Google Services Framework und den Fused Location zu deaktivieren.

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());
    }
}

Ich habe hier ein vollständiges Funktionsbeispiel des Codes, den ich zum Testen verwende, beigefügt: https://github.com/joshuajwitter/ForegroundLocationService

FitBit scheint das gleiche Problem zu haben, hier ist die Diskussion

Hier sind die Ressourcen, die ich bereits angeschaut habe:

Codebeispiele für Android O

Lass die App immer auf Android laufen

Android, erhalte den Ort, wenn der Bildschirm ausgeschaltet ist

Hintergrundlokalisierung

Inkonsistente Standortaktualisierungen im Vordergrunddienst im Tiefschlaf (Schläfrigkeit)

Vordergrunddienst empfängt keine Standortaktualisierungen im Doze-Modus in Android O

EDIT 2018.01.30: Ich habe auch versucht, den ForegroundLocationService in einem eigenen Prozess auszuführen:

<service
    Android:name="foregroundlocation.stackoverflowexample.com.foregroundlocation.ForegroundLocationService"
    Android:enabled="true"
    Android:stopWithTask="false"
    Android:process=":ForegroundLocationService"
    Android:exported="false" >
</service>

neben der Aktualisierung meines Codes zur Verwendung der FusedLocationProviderClient ist immer noch kein Glück.

10
Joshua W

Ich hatte dieses Problem auf emulierten Geräten, auf denen 7.0 und 8.0 ausgeführt wurde, und der Standortrückruf wurde nicht ausgelöst, wenn der Bildschirm ausgeschaltet war (cmd + P). Endlich konnte ich ein echtes 7.0-Gerät (Galaxy S7) in die Hände bekommen und konnte das Problem nicht replizieren. Tut mir leid, wenn ich irgendjemanden Zeit verschwendet habe, anscheinend handelt es sich um ein Emulatorproblem.

1
Joshua W

Ich hatte das gleiche Problem und habe eine funktionierende Lösung gefunden. Um Standortaktualisierungen anzuhören, sollten Sie diese Methode NICHT aufrufen:

public Task<Void> requestLocationUpdates (LocationRequest request, 
LocationCallback callback, Looper looper)

Sie sollten eine ausstehende Absicht anstelle eines Rückrufs verwenden, d. H. Sie sollten die Methode aufrufen:

public Task<Void> requestLocationUpdates (LocationRequest request, 
PendingIntent callbackIntent)

Überprüfen Sie den Link, um mehr zu erfahren: https://developers.google.com/Android/reference/com/google/Android/gms/location/FusedLocationProviderClient.html#requestLocationUpdates(com.google.Android.gms.locationRequest , Android.app.PendingIntent)

4
zavanton

eine Lösung könnte dies sein:

Überprüfen Sie, ob der Standort nicht innerhalb von 2 Minuten empfangen wurde, und starten Sie die Aktualisierung des gesamten Startorts erneut. obwohl Vordergrunddienst das Problem gelöst haben muss

0
tahaDev

LocationServices.FusedLocationApi ist veraltet.

Ich habe meine App gerade wie folgt umgestaltet. Ich hoffe es hilft:

Das nenne ich in der onCreate-Methode meines Service:

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);
}

Und dies ist die Methode zum Erstellen des Rückrufs:

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;
}

Ich denke, ich habe das auch irgendwo als Beispiel gefunden, aber den Verweis nicht behalten.

Ich habe Oreo nicht alleine. Aber einige Tester haben bestätigt, dass es funktioniert.

Um den Dienst im Vordergrund zu halten, rufe ich anschließend in der onCreate-Methode meines Dienstes einfach "startForeground" auf.

0
user2808624