屏幕关闭时,前台服务未在 Android 7.0+ 中接收位置更新
Posted
技术标签:
【中文标题】屏幕关闭时,前台服务未在 Android 7.0+ 中接收位置更新【英文标题】:Foreground service not receiving location updates in Android 7.0+ when screen is off 【发布时间】:2018-07-08 14:41:45 【问题描述】:我正在尝试创建一个 android 应用程序,该应用程序可以在设备屏幕关闭时持续实时记录设备位置数据。我的代码适用于 Android 6.0 及更早版本,但似乎 Android 7.0+ 破坏了我的应用程序。
我已经实现了一个使用唤醒锁并订阅 Google FusedLocation API 的 Android 前台服务。一旦屏幕关闭,onLocationChanged
回调将永远不会触发。
有没有人看到这个问题的解决方案?我已尝试通过 Android 设备设置为我的应用以及 Google 服务框架和 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 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.***example.com.foregroundlocation.ForegroundLocationService"
android:enabled="true"
android:stopWithTask="false"
android:process=":ForegroundLocationService"
android:exported="false" >
</service>
以及更新我的代码以使用FusedLocationProviderClient
,仍然没有运气。
【问题讨论】:
您在构建通知时是否尝试过.setOngoing(true)
。
感谢您的快速回复。我只是尝试添加,但似乎没有什么不同: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) .setOngoing(true) .setWhen(System.currentTimeMillis());
屏幕开启时是否会触发onLocationChanged
?
onLocationChanged
确实在屏幕打开时一遍又一遍地触发。一旦屏幕关闭,它就不再被触发。
..当屏幕打开并且应用程序在后台(前台服务正在运行)时触发。
【参考方案1】:
过去一周我一直在尝试在前台获取定位服务。最后通过在清单中包含它来使其工作。
android:foregroundServiceType="location"
我希望这可以帮助将来的人!
【讨论】:
我花了一整天的时间解决一个问题。你太棒了!!【参考方案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;
猜猜,我也在某个地方找到了这个例子,但没有保留参考。
我自己没有奥利奥。但一些测试人员证实,它确实有效。
为了将服务保持在前台,我只是在服务的 onCreate 方法中调用“startForeground”。
【讨论】:
不幸的是,这段代码在屏幕关闭时似乎没有得到更新。我用它代替了我的示例中的代码,它确实以预定的时间间隔获取了位置,直到屏幕关闭,此时没有收到更新,然后一旦屏幕重新打开,更新就会恢复。 当 Nougat 设备上的屏幕关闭时,此代码也无法获取位置更新。 您是否尝试从服务的 onStartCommand 返回 START_STICKY? 我采纳了您的建议,并用 START_STICKY 替换了 START_REDELIVER_INTENT,但很遗憾,它并没有奏效(感谢您的回复)。似乎问题在于屏幕关闭时永远不会调用位置回调。就像当我关闭屏幕时 FusedLocationProvider 本身会停止一样,但我觉得这不可能是因为 Fitbit 和 Google Fit 等应用程序能够在屏幕关闭时跟踪位置。 这方面的任何成功。遇到同样的问题。【参考方案4】:我在运行 7.0 和 8.0 的模拟设备上遇到过这个问题,并且在屏幕关闭 (cmd+P) 时始终没有触发位置回调。我终于能够拿到真正的 7.0 设备(Galaxy S7),但我无法复制这个问题。对不起,如果我浪费了任何人的时间,显然这是一个模拟器问题。
【讨论】:
不,这不是模拟器问题。我在真实设备上遇到过同样的问题。不过我还是没找到解决办法【参考方案5】:一个解决方案可能是这样的:
检查是否在 2 分钟内未收到位置,再次停止/开始整个开始位置更新。虽然前台服务肯定能解决问题
【讨论】:
以上是关于屏幕关闭时,前台服务未在 Android 7.0+ 中接收位置更新的主要内容,如果未能解决你的问题,请参考以下文章
为啥在 Android Oreo 上关闭屏幕时前台服务停止?
屏幕关闭时,Android 10 会阻止网络请求和 GPS 呼叫