我正在开发一个需要每30秒钟连续跟踪用户位置的应用程序。我已尝试按照Google Developer网站提供的指南进行位置更新。当应用程序在前台并且屏幕在打开时,该类在大多数情况下都能正常运行。当屏幕变暗或设备从adb shell进入空闲状态时,它将不再接收任何位置更新。我正在从运行在其自己的单独进程上的前台服务初始化类,并从该服务调用方法。该服务似乎在空闲模式下运行,但是位置更新停止了。
我还尝试使用Framework Api定义标准来实现常规位置更新。它也显示出与上述类似的行为。为了获得更好的性能,我想使用Google Play位置Api。
测试设备: Huwayei Y9 2018
Android版本: 8.0.0
我已授予自动启动,二次启动和在后台运行
public class GPSUtility implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener{
private static final String TAG = GPSUtility.class.getSimpleName().toUpperCase();
private Context context;
private LocationRequest locationRequest;
private Location currentLocation;
private FusedLocationProviderClient fusedClient;
private LocationCallback locationCallback;
private GoogleApiClient googleApiClient;
float currentSpeed = 0f;
private long startTimeInMilis;
KalmanLatLong kalmanFilter;
public GPSUtility(Context context) {
this.context = context;
this.fusedClient = LocationServices.getFusedLocationProviderClient(context);
kalmanFilter = new KalmanLatLong(3);
startTimeInMilis = (long) (SystemClock.elapsedRealtimeNanos() / 1000000);
createLocationRequest();
createaLocationCallback();
}
private void createLocationRequest() {
locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY | LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
}
private void createaLocationCallback(){
locationCallback = new LocationCallback(){
@Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
if(locationResult == null){
Log.i(TAG, "Location Result is null not updated");
return;
}
Location location = locationResult.getLastLocation();
updateLocation(location);
}
};
}
public void startLocationUpdate() {
buildGoogleApiClient();
getLastKnownLocation();
requestLocationUpdates();
}
private void buildGoogleApiClient(){
googleApiClient = new GoogleApiClient.Builder(context)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
googleApiClient.connect();
}
private void requestLocationUpdates(){
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
notifyPermissionNotGranted();
return;
}
fusedClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper());
Log.i(TAG, "Location Request is sent, will recieve updates in 20s");
}
private void getLastKnownLocation(){
try {
fusedClient.getLastLocation().addOnSuccessListener(new OnSuccessListener<Location>() {
@Override
public void onSuccess(Location location) {
if (location != null) {
updateLocation(location);
}
}
});
}catch (SecurityException ex){
ex.printStackTrace();
checkLocationSettings();
}
}
public void removeLocationUpdate(){
fusedClient.removeLocationUpdates(locationCallback);
Log.i(TAG, "Update Stopped. Will not receive further locations");
}
public double getLatitude(){
if(currentLocation != null){
return currentLocation.getLatitude();
}
return -1.0;
}
public double getLongitude(){
if(currentLocation != null){
return currentLocation.getLongitude();
}
return -1.0;
}
public String getProvider(){
if(currentLocation!=null){
return currentLocation.getProvider();
}
return "";
}
@Override
public void onConnected(@Nullable Bundle bundle) {
getLastKnownLocation();
requestLocationUpdates();
}
@Override
public void onConnectionSuspended(int i) {
checkLocationSettings();
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
checkLocationSettings();
}
public void checkLocationSettings(){
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(locationRequest);
SettingsClient client = LocationServices.getSettingsClient(context);
Task<LocationSettingsResponse> task = client.checkLocationSettings(builder.build());
task.addOnCompleteListener(new OnCompleteListener<LocationSettingsResponse>() {
@Override
public void onComplete(@NonNull Task<LocationSettingsResponse> task) {
if(!task.isSuccessful()){
Intent intent = new Intent(context, TrackingService.class);
context.stopService(intent);
Log.i(TAG, "Location settings turned off");
Exception e = task.getException();
try{
ResolvableApiException resolvable = (ResolvableApiException) e;
PendingIntent pendingIntent = resolvable.getResolution();
sendInadequateLocationSettings(pendingIntent);
}catch (Exception ex){
ex.printStackTrace();
Log.i(TAG, "Can't send pending intent");
}
}
}
});
}
private void updateLocation(Location location){
if(isBetterLocation(location)){
currentLocation = location;
notifyLastKnownLocationUpdated();
Log.i(TAG, "Updated Location lat-" + location.getLatitude() + " long-" + location.getLongitude() + " acc-" + location.getAccuracy() + " provider-" + location.getProvider());
}
}
private long getLocationAge(Location location){
long currentTimeInMili = (long)(SystemClock.elapsedRealtimeNanos() / 1000000);
long locationTimeInMili = (long)(location.getElapsedRealtimeNanos() / 1000000);
long locationAge = currentTimeInMili - locationTimeInMili;
return locationAge;
}
private boolean isBetterLocation(Location location){
long age = getLocationAge(location);
if(currentLocation == null && location != null)
return true;
if(age > 20 * 1000){
Log.d(TAG, "Location is too old not taking location");
return false;
}
if(location.getAccuracy() <= 0){
Log.d(TAG, "Lat Long values are invalid");
return false;
}
float horizontalAccuracy = location.getAccuracy();
if(horizontalAccuracy > 50){
Log.i(TAG, "Horizontal accuracy is less than 50 meters");
return false;
}
float Qvalue;
long locationTimeInMilis = (long)(location.getElapsedRealtimeNanos() / 1000000);
long elapsedTimeInMilis = locationTimeInMilis - startTimeInMilis;
if(currentSpeed == 0f){
Qvalue = 3.0f;
}else{
Qvalue = currentSpeed;
}
kalmanFilter.process(location.getLatitude(), location.getLongitude(), location.getAccuracy(), elapsedTimeInMilis, Qvalue);
double predictedLat = kalmanFilter.getLatitude();
double predictedLong = kalmanFilter.getLongitude();
Location predictedLocation = new Location("");
predictedLocation.setLatitude(predictedLat);
predictedLocation.setLongitude(predictedLong);
float predictedDeltaInMeters = predictedLocation.distanceTo(location);
if(predictedDeltaInMeters > 100){
Log.i(TAG, "Kalman detects mal location. We should not include this location");
kalmanFilter.consecutiveRejectCount += 1;
if(kalmanFilter.consecutiveRejectCount > 3){
kalmanFilter = new KalmanLatLong(3);
}
return false;
}else{
kalmanFilter.consecutiveRejectCount = 0;
}
currentSpeed = location.getSpeed();
return true;
}
private void notifyPermissionNotGranted(){
Log.i(TAG, "Location Permission Not Granted. Sending Broadcast to stop service");
Intent intent = new Intent();
intent.setAction(Constants.FINE_LOCATION_PERMISSION_NOT_GRANTED);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
private void notifyLastKnownLocationUpdated(){
Log.i(TAG, "Location is updated sending broadcast");
Intent intent = new Intent();
intent.setAction(Constants.LAST_LOCATION_ACTION);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
private void sendInadequateLocationSettings(PendingIntent pendingIntent){
Intent intent = new Intent(Constants.INADEQUATE_LOCATION_SETTINGS);
intent.putExtra("pendingIntent", pendingIntent);
context.sendBroadcast(intent);
}
@Override
protected void finalize() throws Throwable {
if(googleApiClient != null && googleApiClient.isConnected()){
googleApiClient.disconnect();
}
super.finalize();
}
}
当屏幕变暗或使用Google Play的位置API将设备置于闲置状态时,如何正确接收位置更新?