屏幕关闭时,前台服务未在Android 7.0+中接收位置更新 [英] Foreground service not receiving location updates in Android 7.0+ when screen is off

查看:417
本文介绍了屏幕关闭时,前台服务未在Android 7.0+中接收位置更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我已经实现了一个使用wakelock并订阅Google FusedLocation API的Android前台服务。一旦屏幕关闭, onLocationChanged 回调从不被触发。



有没有人看到过这个问题的解决方案?我曾尝试通过Android设备设置为我的应用禁用电池优化,以及Google服务框架和Fused位置。

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

private static final String TAG = ForegroundLocationService.class.getSimpleName() ;

//前台通知的通知ID
public static final int GPS_NOTIFICATION = 1;

//请求gps更新的时间间隔(以秒为单位)
private static final int UPDATE_INTERVAL_IN_SECONDS = 15;

//这个服务目前在前台运行吗?
private boolean isForeground = false;

// google api客户端
私有GoogleApiClient googleApiClient;

//屏幕关闭时用于保持应用程序活动的唤醒锁
private PowerManager.WakeLock wakeLock;

@Override
public void onCreate(){
super.onCreate();

//创建google api客户端
googleApiClient =新的GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
。 addApi(LocationServices.API)
.build();

//从电源管理器获取一个唤醒锁
final PowerManager powerManager =(PowerManager)getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,TAG);

$ b @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;

//连接到google api客户端
googleApiClient.connect();

//获取唤醒锁
wakeLock.acquire();
}

返回START_REDELIVER_INTENT;
}

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

$ b @Override
public void onDestroy(){

Log.v(TAG,Stopping the+ this.getClass()。 getSimpleName());

stopForeground(true);
isForeground = false;

//从google api客户端断开
googleApiClient.disconnect();

//如果持有
,则释放wakelock if(null!= wakeLock&&wakeLock.isHeld()){
wakeLock.release();
}

super.onDestroy();


private LocationRequest getLocationRequest(){

LocationRequest locationRequest = LocationRequest.create();

//我们总是要最高的准确性
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

//我们要确保在指定的时间间隔内获得更新位置
locationRequest.setInterval(TimeUnit.SECONDS.toMillis(0));

//这设置了应用程序可以从其他应用程序访问
//位置服务的最新时间间隔。例如,如果谷歌地图在后台运行
//我们可以每隔五秒更新一次我们的位置
locationRequest.setFastestInterval(TimeUnit.SECONDS.toMillis(0));
locationRequest.setMaxWaitTime(TimeUnit.SECONDS.toMillis(UPDATE_INTERVAL_IN_SECONDS));

返回locationRequest;


私人通知notifyUserThatLocationServiceStarted(){

//弹出位置服务正在运行的通知
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());

最终通知通知;
if(Build.VERSION.SDK_INT< 16){
notification = builder.getNotification();
} else {
notification = builder.build();
}

返回通知;
}

@Override
public void onConnected(@Nullable Bundle bundle){

try {

// // request来自融合位置提供商的位置更新
LocationServices.FusedLocationApi.requestLocationUpdates(
googleApiClient,getLocationRequest(),this);
$ b $ catch(SecurityException securityException){
Log.e(TAG,请求位置更新时出现异常,securityException);
}
}

@Override
public void onConnectionSuspended(int i){
Log.i(标记,Google API客户端已暂停。) ;

$ b @Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult){
Log.e(标记,无法连接到Google API客户端。) ;
}

@Override
public void onLocationChanged(Location location){
Log.e(TAG,onLocationChanged:+ location.toString());
}
}

我已经包含了一个完整的代码示例我正在使用此处进行测试:
https://github.com/joshuajwitter/ForegroundLocationService



FitBit似乎有同样的问题,这里是讨论



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



Android示例代码



让应用程序始终在Android上的后台运行





背景位置限制



深度睡眠时的前台服务中的位置更新不一致(瞌睡)



前台服务没有在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

解决方案

我一直在运行7.0和8.0的仿真设备上遇到这个问题,并且始终如一地调用回调函数在屏幕关闭时没有被触发(cmd + P)。我终于能够掌握真实的7.0设备(Galaxy S7),但我无法复制这个问题。对不起,如果我浪费了任何人的时间,显然它是一个模拟器问题。


I am trying to create an Android application that continuously logs device location data in realtime while the device screen is off. My code works correctly with Android 6.0 and earlier but it seems that Android 7.0+ breaks my app.

I have implemented an Android foreground service that uses a wakelock and subscribes to the Google FusedLocation API. Once the screen is turned off the onLocationChanged callback is never triggered.

Has anyone seen a solution to this problem? I have tried disabling Battery Optimization through the Android Device Settings for my app as well as for the Google Services Framework and the 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());
    }
}

I have included a complete working sample of the code that I am using to test here: https://github.com/joshuajwitter/ForegroundLocationService

FitBit seems to have the same issue, here is the discussion

Here are the resources that I have already looked at:

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

EDIT 2018.01.30: I have also tried running the ForegroundLocationService in its own process:

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

as well as updating my code to use the FusedLocationProviderClient, still no luck.

解决方案

I had been experiencing this issue on emulated devices running 7.0 and 8.0 and consistently the location callback was not being triggered when the screen was off (cmd+P). I was finally able to get my hands on a real 7.0 device (Galaxy S7) and I was not able to replicate the issue. Sorry if I wasted anyone's time, apparently it is an emulator issue.

这篇关于屏幕关闭时,前台服务未在Android 7.0+中接收位置更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆