![](/img/trans.png)
[英]Foreground service not receiving location updates in Doze mode in Android O
[英]Foreground service not receiving location updates in Android 7.0+ when screen is off
我正在尝试创建一个 Android 应用程序,该应用程序在设备屏幕关闭时实时连续记录设备位置数据。 我的代码在 Android 6.0 及更早版本上正常工作,但似乎 Android 7.0+ 破坏了我的应用程序。
我已经实现了一个使用唤醒锁并订阅 Google FusedLocation API 的 Android 前台服务。 一旦屏幕关闭, onLocationChanged
回调就不会被触发。
有没有人看到这个问题的解决方案? 我已尝试通过 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
以下是我已经看过的资源:
前台服务未在 Android O 中的 Doze 模式下接收位置更新
编辑 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:foregroundServiceType="location"
我希望这可以帮助将来的人!
我遇到了同样的问题,我找到了一个可行的解决方案。 为了收听位置更新,您不应调用此方法:
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)
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”。
我在运行 7.0 和 8.0 的模拟设备上遇到过这个问题,并且始终在屏幕关闭 (cmd+P) 时没有触发位置回调。 我终于能够接触到真正的 7.0 设备(Galaxy S7),但我无法复制该问题。 对不起,如果我浪费了任何人的时间,显然这是一个模拟器问题。
一种解决方案可能是这样的:
检查是否在 2 分钟内未收到位置停止/重新开始整个开始位置更新。 虽然前台服务必须解决问题
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.