屏幕关闭时,前台服务未在Android 7.0+中接收位置更新

问题描述 投票:11回答:4

我正在尝试创建一个Android应用程序,在设备屏幕关闭时实时连续记录设备位置数据。我的代码可以在Android 6.0及更早版本中正常运行,但似乎Android 7.0+破坏了我的应用。

我已经实现了一个使用唤醒锁的Android前台服务,并订阅了Google FusedLocation API。屏幕关闭后,onLocationChanged回调永远不会被触发。

有谁见过这个问题的解决方案?我尝试通过Android应用的Android设备设置以及Google服务框架和融合位置禁用电池优化。

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 seems to have the same issue, here is the discussion

以下是我已经查看过的资源:

Code Samples for Android O

Keep app running in background on Android all the time

Android, get the location when the screen is off

Background Location Limits

Inconsistent location updates in foreground service when in deep sleep (doze)

Foreground service not receiving location updates in Doze mode in Android O

编辑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,仍然没有运气。

android gps location android-7.0-nougat wakelock
4个回答
4
投票

我有同样的问题,我找到了一个有效的解决方案。要收听位置更新,您不应该调用此方法:

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)


1
投票

我在运行7.0和8.0的模拟设备上遇到此问题,并且在屏幕关闭时(cmd + P)始终未触发位置回调。我终于能够得到一个真正的7.0设备(Galaxy S7),我无法复制这个问题。对不起,如果我浪费了任何人的时间,显然这是一个模拟器问题。


0
投票

LocationServices.FusedLocationApi已弃用。

我刚刚重构了我的应用程序如下。希望能帮助到你:

这就是我在onCreate-Method of my 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);
}

这是创建回调的方法:

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

猜猜看,我也发现某处的例子,但没有保留参考。

我自己没有奥利奥。但一些测试人员证实,它有效。

为了使服务保持在前台,我之后只是在我的服务的onCreate方法中调用“startForeground”。


0
投票

一个解决方案可能是这样:

检查是否在2分钟内未收到位置停止/再次启动整个开始位置更新。虽然前台服务必须解决问题

© www.soinside.com 2019 - 2024. All rights reserved.