是否可以让Google的FusedLocationProviderClient.requestLocationUpdates在前台服务中正常运行?

时间:2019-05-04 09:33:53

标签: android android-service

我正在开发一个需要每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将设备置于闲置状态时,如何正确接收位置更新?

0 个答案:

没有答案