Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to remove FusedlocationProviderClient location updates from within non-activity class

I’m trying to write a utility class to wrap the Google Play Services FusedLocationProviderClient API and location permissions request as I’m sick of writing all that boilerplate every time I want to add location functionality to an app. The problem I’m having though is I’m unable to remove location updates once I’ve started them. Here’s the relevant bits of my utility class:

public class UserLocationUtility extends LocationCallback
{
    // Hold a WeakReference to the host activity (allows it to be garbage-collected to prevent possible memory leak)
    private final WeakReference<Activity> weakActivity;
    // Debug tag
    private static final String TAG = "UserLocationUtility";


    public static class RequestCodes
    {
        static final int CURRENT_LOCATION_ONE_TIME = 0;
        static final int CURRENT_LOCATION_UPDATES = 1;
        static final int LAST_KNOWN_LOCATION = 2;
        static final int SMART_LOCATION = 3;
    }


    private FusedLocationProviderClient mLocationClient;
    private Context mContext;
    private LocationRequest mLocationRequest;


    /* Constructor */
    UserLocationUtility(Activity activity){
        // assign the activity to the weak reference
        this.weakActivity = new WeakReference<>(activity);

        // Hold a reference to the Application Context
        this.mContext = activity.getApplicationContext();

        // Instantiate our location client
        this.mLocationClient = LocationServices.getFusedLocationProviderClient(mContext);

        // Set up the default LocationRequest parameters
        this.mLocationRequest = new LocationRequest();
        setLocationRequestParams(2000, 500, LocationRequest.PRIORITY_HIGH_ACCURACY);
                                // Sets up the LocationRequest with an update interval of 30 seconds, a fastest
                                // update interval cap of 5 seconds and using balanced power accuracy priority.
    } /* Note: values for testing only. Will be dialed back for better power management when testing complete */


    /* Stripped out other methods for brevity */


    @SuppressLint("MissingPermission")
    public void getCurrentLocationOneTime(final UserLocationCallback callback){

        mLocationClient.requestLocationUpdates(mLocationRequest, new LocationCallback()
        {
            @Override
            public void onLocationResult(LocationResult locationResult){
                if (locationResult == null){
                    callback.onFailedRequest("getCurrentLocationOneTime(): Request failed: returned null");
                    return;
                }

                callback.onLocationResult(locationResult.getLastLocation());
                stopLocationUpdates(); /* Stopping location updates here just for testing (NOT WORKING!!) */ 

            }
        }, null);

    }


    public void stopLocationUpdates(){

        mLocationClient.removeLocationUpdates(new LocationCallback(){});
        Log.i(TAG, "stopLocationUpdates(): Location updates removed");

    }

}

Here’s how I’m trying to use it (from MainActivity):

UserLocationUtility locationUtility;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);


    locationUtility = new UserLocationUtility(this);

    if (locationUtility.checkPermissionGranted()){
        Log.i(TAG, "Permissions are granted.");
        getLocationUpdates();
    } else {
        Log.i(TAG, "Permissions are not granted. Attempting to request...");
        locationUtility.requestPermissions(UserLocationUtility.RequestCodes.CURRENT_LOCATION_UPDATES);
    }


}

    public void getLocationUpdates(){

        locationUtility.getCurrentLocationOneTime(new UserLocationCallback() {
            @Override
            public void onLocationResult(Location location) {
                Log.i(TAG, "getLocationUpdates result: " + location.toString());
            }

            @Override
            public void onFailedRequest(String result) {
                Log.e(TAG, "LocationUpdates result: " + result);
            }
        });

    }

And here's a sample from the log:

I/MainActivity: getLocationUpdates result: Location[fused 34.421998,-125.084000 hAcc=731 et=+2h10m52s694ms vAcc=??? sAcc=??? bAcc=???]
I/UserLocationUtility: stopLocationUpdates(): Location updates removed
I/MainActivity: getLocationUpdates result: Location[fused 34.421998,-125.084000 hAcc=739 et=+2h10m57s697ms vAcc=??? sAcc=??? bAcc=???]
I/UserLocationUtility: stopLocationUpdates(): Location updates removed
I/MainActivity: getLocationUpdates result: Location[fused 34.421998,-125.084000 hAcc=763 et=+2h11m5s723ms vAcc=??? sAcc=??? bAcc=???]
I/UserLocationUtility: stopLocationUpdates(): Location updates removed
etc...

As you can see I’m receiving the location updates correctly but the call to stopLocationUpdates() isn’t working. I have a feeling it’s something to do with the fact that I’m passing a new LocationCallback to the removeUpdates() method, but I’m not sure what the alternative is, or even if there is an alternative. This being a non-activity class I can’t exactly initialise LocationCallback as a member in onCreate() then pass it around as needed. The google docs on this aren’t much help at all. Whether that’s because I lack the necessary understanding to decipher them or because they’re just not very good I don’t know but either way I’m stumped and having searched around a lot I can’t seem to find an existing answer elsewhere. Thanks.

like image 657
DoTheDonkeyKonga Avatar asked Aug 07 '18 18:08

DoTheDonkeyKonga


People also ask

Which API is used for continuously obtain the device location?

Using the Google Play services location APIs, your app can request the last known location of the user's device. In most cases, you are interested in the user's current location, which is usually equivalent to the last known location of the device.

What is a fused location app?

The fused location provider is a location API in Google Play services that intelligently combines different signals to provide the location information that your app needs.


1 Answers

Posting my solution as an answer in case it helps anyone else.

I got it working by declaring a LocationCallback as a member variable and then initialising (or re-initialising) it in each method that requires it...

public void getCurrentLocationUpdates(final UserLocationCallback callback){
        if (mIsReceivingUpdates){
            callback.onFailedRequest("Device is already receiving updates");
            return;
        }

        // Set up the LocationCallback for the request
        mLocationCallback = new LocationCallback()
        {
            @Override
            public void onLocationResult(LocationResult locationResult){
                if (locationResult != null){
                    callback.onLocationResult(locationResult.getLastLocation());
                } else {
                    callback.onFailedRequest("Location request returned null");
                }
            }
        };

        // Start the request
        mLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, null);
        // Update the request state flag
        mIsReceivingUpdates = true;
    }

I check at the beginning of the method whether or not location updates are already being received and get out early if so. This prevents duplicate (and thus unstoppable) location update requests being initiated.

Calling the stopLocationUpdates (below for reference) method now works as it should.

public void stopLocationUpdates(){

    mLocationClient.removeLocationUpdates(mLocationCallback);
    mIsReceivingUpdates = false;
    Log.i(TAG, "Location updates removed");

}
like image 108
DoTheDonkeyKonga Avatar answered Sep 23 '22 19:09

DoTheDonkeyKonga