Android Service not updating location

2019-06-04 23:13发布

问题:

I need to update fused location every 1 min and on each 5 meter displacement (I know its bad practice but for the sake of testing and showing in Log ) and i start service by button Pressed as soon as button pressed i got location in logcat but only once. As per Service class the onLocationChanged should called every 1 min but it never calls again even my GPS turn on almost every min and then turn off after while but still there is no Location Update in Logcat.I need service class to keep update location without effecting UI or main thread

Here is logcat which shows location update only once

09-16 03:13:37.125 10419-10419/com.example.com.pro_working1 D/===Location Service===: Service onConnected Calling
09-16 03:13:37.125 10419-10419/com.example.com.pro_working1 D/===Location Service===: Start Location Update is Calling
09-16 03:13:38.315 10419-10419/com.example.com.pro_working1 D/===Location Service===: Service OnLocationChanged Calling
09-16 03:13:38.315 10419-10419/com.example.com.pro_working1 D/===Location Service===: Here is Updated Locations: Latitude 28.5XXXXX Longitude 77.2XXXXX
09-16 03:13:38.315 10419-10419/com.example.com.pro_working1 D/===Location Service===: Sending Location Starting  Accuracy is: 37.5

Here is my Service Class

public class Location_Service_background extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
    public static final String TAG = "===Location Service===";
    GoogleApiClient apiClient;
    Location mCurrentLocation;
    LocationRequest locationRequest;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        GoogleApiSetup();
        RequestLocationUpdates();
        Log.d(TAG,"OnStartCommand Is Calling ");
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }



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

    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        if (apiClient.isConnected()){
            Log.d(TAG,"Service On Destroy Calling and apiClient is Connected");
            StopLocationUpdates();
            apiClient.disconnect();
        }
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        Log.d(TAG,"Service onConnected Calling");
        if (mCurrentLocation != null) {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
             mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(apiClient);

        }
        else {
            Log.d(TAG,"Start Location Update is Calling");
            StartLocationUpdate();
        }
    }

    @Override
    public void onConnectionSuspended(int i) {
        Log.d(TAG,"Service OnConnectionSuspended Calling");
        apiClient.connect();
    }

    @Override
    public void onLocationChanged(Location location) {
        Log.d(TAG,"Service OnLocationChanged Calling");
        mCurrentLocation=location;
        Log.d(TAG,"Here is Updated Locations: Latitude "+mCurrentLocation.getLatitude()+"Longitude "+mCurrentLocation.getLongitude());
        String Latitude= String.valueOf(mCurrentLocation.getLatitude());
        String Longitude=String.valueOf(mCurrentLocation.getLongitude());
        String Accuracy=String.valueOf(mCurrentLocation.getAccuracy());
        Log.d(TAG,"Sending Location Starting"+" "+" Accuracy is: "+mCurrentLocation.getAccuracy());


    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        Log.d(TAG,"Connection Failed Called "+connectionResult);
    }



    private synchronized  void GoogleApiSetup() {
        apiClient = new GoogleApiClient.Builder(this)
                .addApi(LocationServices.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();
        apiClient.connect();
    }

    private void RequestLocationUpdates() {
        locationRequest = new LocationRequest();
        //1 Min = 60 seconds AND 1000 milliseconds in 1 second
        locationRequest.setInterval(60 * 1000);//5 Min 300 x 1000=300000
        locationRequest.setFastestInterval(60 * 1000);//2 Min
        locationRequest.setSmallestDisplacement(5);
        locationRequest.setMaxWaitTime(60*1000);
        locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    }

    private void StopLocationUpdates() {
        LocationServices.FusedLocationApi.removeLocationUpdates(apiClient, this);
    }

    private void StartLocationUpdate() {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

            return;
        }
        if (apiClient.isConnected()) {
            LocationServices.FusedLocationApi.requestLocationUpdates(apiClient, locationRequest, this);
        }
        else {
            Log.d(TAG,"GoogleApiClient is not connected yet.Please Try Again");
            apiClient.connect();
        }
    }


}

回答1:

Hopefully this helps save someone the frustration i went through on this issue. I am using the FusedLocationAPI to request location updates and GoogleApiClient. I also do this from a Service.

You are not getting any location updates at your timed interval because setSmallestDisplacement() takes absolute priority over the other parameters. So even if you have

locationRequestTimeInterval = LocationRequest.create()
    .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
    .setInterval(1)    
    .setFastestInterval(1)    
    .setMaxWaitTime(1)    
    .setSmallestDisplacement(5);

this will NEVER be triggered until the smallestDisplacement parameter is met.

The solution to this, is to create two separate LocationRequests... one that is only concerned with distance, and one that is only concerned with time, and use your single GoogleApiClient to create 2 requestLocationUpdates commands, one using the Time LocationRequest, and the other using the Distance LocationRequest.

So your 2 LocationRequests look like this:

locationRequestDistanceInterval = LocationRequest.create()
            .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
            .setFastestInterval(0) // This MUST be set, otherwise no updates
            .setSmallestDisplacement(LOCATION_REQUEST_MIN_DISTNACE);

locationRequestTimeInterval = LocationRequest.create()
            .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
            .setInterval(PROVIDER_GPS_UPDATE_TIME_INTERVAL)
            .setFastestInterval(PROVIDER_GPS_UPDATE_TIME_INTERVAL);
  • note that the setFastestInterval() value MUST be set on the Distance Request, otherwise the distance request will not be triggered. It does not have to be 0, but this field must be set.

Then in your GoogleApiClients onConnected(), you can add the two requestLocationUpdates() calls.

LocationServices.FusedLocationApi.requestLocationUpdates(mLocationClient, locationRequestDistanceInterval, new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            // Handle your Distance based updates
        });
    LocationServices.FusedLocationApi.requestLocationUpdates(mLocationClient, locationRequestTimeInterval, new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            // Handle your Time based updates
        }
    }); 

You can use the same or separate LocationListeners to handle the responses for each request. Hope this helps.



回答2:

Presuming that you are in fact moving the device more than 5 meters between expected updates, I think there might be a problem with the LocationRequest class where the setSmallestDisplacement() method just doesn't work properly... or at least it doesn't work as expected. (I've seen these issues on other posts).

You could always check the distance yourself by removing the setSmallestDisplacement(), and instead doing:

onLocationChanged(){

double distance = // [ calculate distance between current lat/long and previous lat/long ]

if (distance > 5 meters) {

// do your normal onLocationChanged() code here

}

}

There is also the possiblity that for some reason, the FusedLocationProvider is not able to access your GPS and is instead using wifi or cell tower... in which case, 5m is too small of a distance to specify. Your logs say the accuracy is 37.5 meters, so that doesn't seem accurate enough to be able to measure 5 meters. So... maybe you don't have FINE access permission set in your manifest?

Also could it be a problem with runtime permissions not being done properly? You could temporarily set your target SDK to 22 in order to check this.