4

in one of my activity, I am using the FusedLocationProviderClient to get constant location update. My codes are based on this method: https://developer.android.com/training/location/receive-location-updates

In my onCreate, I setup the provider and callbacks

// setup fused location provider
mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);

    // build location request
    mLocationRequest = new LocationRequest();
    mLocationRequest.setInterval(30000);
    mLocationRequest.setFastestInterval(10000);
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    mLocationRequest.setSmallestDisplacement(50);

    // Setup the callback function.
    mLocationCallback = new LocationCallback() {
        @Override
        public void onLocationResult(LocationResult locationResult) {
            if (locationResult == null) {
                return;
            }
            for (Location location : locationResult.getLocations()) {
                // Update UI with location data
                // ...

                mCurrentLocation = location;
            }
        }
    };

In onResume

mFusedLocationProviderClient.requestLocationUpdates(mLocationRequest,
                                mLocationCallback,
                                Looper.myLooper());

In onPause

mFusedLocationProviderClient.removeLocationUpdates(mLocationCallback);

Yet for some reason, leak canary is still indicating that there's memory leak. Leak canary log shown below enter image description here

Browsing around stack overflow, there are posts that seems to suggest that the leak is due to google play service. But those post were talking about fusedLocationApi, while I'm using the fusedLocationProviderClient, so I'm not sure if its the same thing as what I am using here. Can some one confirm for me ? Thank you!

Chao Li
  • 391
  • 1
  • 2
  • 13

3 Answers3

4

I have fixed the leaks reported by LeakCanary by passing SoftReference of the LocationCallback to the FusedLocationProvider.

public class LocationCallbackReference extends LocationCallback {

    private final SoftReference<LocationCallback> mLocationCallbackRef;

    public LocationCallbackReference(LocationCallback locationCallback) {
        mLocationCallbackRef = new SoftReference<>(locationCallback);
    }

    @Override
    public void onLocationResult(LocationResult locationResult) {
        super.onLocationResult(locationResult);
        if (mLocationCallbackRef.get() != null) {
            mLocationCallbackRef.get().onLocationResult(locationResult);
        }
    }

    @Override
    public void onLocationAvailability(LocationAvailability locationAvailability) {
        super.onLocationAvailability(locationAvailability);
        if (mLocationCallbackRef.get() != null) {
            mLocationCallbackRef.get().onLocationAvailability(locationAvailability);
        }
    }
}

I hope that helps.

  • Worked for me. For anyone wondering how to use it: just create class as the above and, after normally creating instance of myLocationCallback, just pass LocationCallbackReference(myLocationCallback) when passing to client.requestLocationUpdates(...) – Androidz Dec 13 '19 at 12:53
  • interesting answer but why weak reference doesn't work? @Marcin – max Jul 07 '21 at 06:44
2

For me, I was using LocationCallback as an inner anonymous object (because I have to update/access the enclosing class data) & FusedLocationProviderClient was causing the memory leak.

Then I refactor the inner class to static(non-inner) class & update the enclosing class data via live data passed to static LocationCallback object.

import android.app.Activity
import androidx.lifecycle.MutableLiveData
import com.google.android.gms.location.*


class LocationUtility constructor(private val activity: Activity) {


private var mLocationRequest: LocationRequest? = null
private var fusedLocationProviderClient: FusedLocationProviderClient? = null

var currentLocation = MutableLiveData<Pair<Double?, Double?>>()

private var myLocationCallback: MyLocationCallback? = null


init {
    fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(activity)
    myLocationCallback = MyLocationCallback(currentLocation)
}



// Location call back
private class MyLocationCallback(val liveData: MutableLiveData<Pair<Double?, Double?>>) :
    LocationCallback() {

    override fun onLocationResult(locationResult: LocationResult?) {
        super.onLocationResult(locationResult)

        val latitude = locationResult?.locations?.get(0)?.latitude
        val longitude = locationResult?.locations?.get(0)?.longitude

        val locationInfo = Pair(latitude, longitude)
        liveData.value = locationInfo
    }
}

}

Muhammad Maqsood
  • 1,622
  • 19
  • 25
1

Google has recently resolved this memory leak issue in their latest play-services-location:20.0.0 release. Release Note.

To know more about the history of this memory leak, check this and this.

Jatin
  • 491
  • 2
  • 12