Open claywilkinson opened 9 years ago
Thanks, this is invaluable feedback. Taking a look.
Hey @shailen, any updates on the issue?
@youfacepalm I've passed this on to the eng team and they're taking a look. I'll update this issue when I hear back from them.
Hey @shailen if you have any updates can you please share it?
The eng team is aware of the problem and is working on it. I'll update here when there's a fix.
@shailen,
Just for reference, while using the most recent version of Google Play Services (8.3.0), LeakCanary is still reporting that the MainActivity
is leaking memory.
@shailen I checked on Google Play Services 8.4.0 as well. The problem still remains!
Any update on this? OOM kept happening as a result in our end :(
@thuytrinh As a workaround I moved the location listener inside a service which will broadcast the location once it is obtained, using an event bus. It still leaks the service but it is better than having the activity leaked with multiple views inside.
Would really appreciate an update on this as this issue was reported more than three months ago!
Any updates? We faced a lot of OOM crashes because of this issue.
In our case, unregister following callbacks would resolve the issue:
googleApiClient.unregisterConnectionCallbacks(this);
googleApiClient.unregisterConnectionFailedListener(this);
this
is the fragment that implements the callbacks.
Also, remove the callback of location request if any:
LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, this);
googleApiClient.disconnect();
@youfacepalm et al: I have no concrete updates, unfortunately, except to say that the eng team is looking at this issue.
On Mon, Feb 1, 2016 at 8:21 AM, Thuy Trinh notifications@github.com wrote:
In our case, unregister following callbacks would resolve the issue:
googleApiClient.unregisterConnectionCallbacks(this); googleApiClient.unregisterConnectionFailedListener(this);
this is the fragment that implements the callbacks.
Also, remove the callback of location request if any:
LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, this); googleApiClient.disconnect();
— Reply to this email directly or view it on GitHub https://github.com/googlesamples/android-play-location/issues/26#issuecomment-178052351 .
Got same problem. :( any updates?
Same problem here. Please let me know when any updated
Sorry everyone, no definitive updates on when the fix will land. Will update once I know more.
I'm having this issue as well, i tested it with 'com.google.android.gms:play-services-location:8.4.0' but issue is still there.
Even though i tried to release in any way that i can (below), still keeps getting into this.
if (googleApiClient != null) {
googleApiClient.unregisterConnectionCallbacks(this);
googleApiClient.unregisterConnectionFailedListener(this);
if (googleApiClient.isConnected()) {
LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, this);
}
googleApiClient.disconnect();
googleApiClient = null;
}
I'll also provide you LeakCanary log so that you can pin point the issue, and hopefully we can get an update soon.
Same problem here:
04-15 17:58:39.311 9221-9270/com.myapp I/art: hprof: heap dump "/storage/emulated/0/Download/leakcanary/suspected_leak_heapdump.hprof" starting...
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: In com.myapp:1.0:1.
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: * com.myapp.activities.CommentActivity has leaked:
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: * GC ROOT com.google.android.gms.location.internal.zzd$zzb.zzamC
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: * references com.google.android.gms.location.internal.zzd$9.zzaOw (anonymous class extends com.google.android.gms.location.internal.zzd$zza)
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: * leaks com.myapp.activities.CommentActivity instance
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: * Reference Key: 631c670d-cf7f-4061-819d-914cc2d11620
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: * Device: unknown generic Google Nexus 5 - 5.1.0 - API 22 - 1080x1920 vbox86p
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: * Android Version: 5.1 API: 22 LeakCanary: 1.3.1
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: * Durations: watch=5011ms, gc=132ms, heap dump=1049ms, analysis=3963ms
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: * Details:
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: * Instance of com.google.android.gms.location.internal.zzd$zzb
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | zzamC = com.google.android.gms.location.internal.zzd$9 [id=0x131f8830]
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | mDescriptor = java.lang.String [id=0x13040b40]
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | mObject = -704448000
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | mOwner = com.google.android.gms.location.internal.zzd$zzb [id=0x13202ca0]
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: * Instance of com.google.android.gms.location.internal.zzd$9
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | zzaOw = com.myapp.activities.CommentActivity [id=0x13d58000]
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | zzaOx = com.google.android.gms.location.internal.zzd [id=0x12e84a00]
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | zzaeE = com.google.android.gms.common.api.Api$zzc [id=0x12e84760]
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | zzagH = java.util.concurrent.atomic.AtomicReference [id=0x131287a0]
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | zzL = true
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | zzagI = java.lang.Object [id=0x13128780]
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | zzagJ = com.google.android.gms.common.api.internal.zzb$zza [id=0x13202c40]
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | zzagK = java.lang.ref.WeakReference [id=0x13202c60]
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | zzagL = java.util.ArrayList [id=0x13202b40]
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | zzagM = null
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | zzagN = false
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | zzagO = false
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | zzagP = false
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | zzagQ = null
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | zzagR = null
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | zzagS = null
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | zzagy = com.google.android.gms.common.api.Status [id=0x12c26f00]
04-15 17:58:44.354 9221-10071/com.myapp D/LeakCanary: | zzpJ = java.util.concurrent.CountDownLatch [id=0x13128790]
Hello all
I followed yourfacepalm advice and did a workaround this morning:
package com.myapp.service;
import android.Manifest;
import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.LocalBroadcastManager;
import com.myapp.Constants.Constants;
import com.myapp.Utils.LocationUtil;
import com.myapp.activities.R;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.places.Places;
import timber.log.Timber;
public class LocationIntentService extends IntentService implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
LocationListener {
GoogleApiClient mGoogleApiClient;
public LocationIntentService(String name) {
super(name);
}
public LocationIntentService() {
super(LocationIntentService.class.getName());
}
@Override
protected void onHandleIntent(Intent workIntent) {
mGoogleApiClient = new GoogleApiClient.Builder(getApplicationContext())
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.addApi(Places.GEO_DATA_API)
.addApi(Places.PLACE_DETECTION_API)
.build();
mGoogleApiClient.connect();
Timber.e("locationintentservice start");
}
@Override
public void onLocationChanged(Location location) {
Timber.e("location changed");
if (location != null) {
Timber.e("location received");
SharedPreferences sharedPreferences = getSharedPreferences(getString(R.string.user_shared_preferences), Context.MODE_PRIVATE);
LocationUtil.storeLocation(sharedPreferences, location);
Timber.e(String.valueOf(location.getLatitude()) + " Longitude: " +
String.valueOf(location.getLongitude()));
} else {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
Intent intent = new Intent(Constants.COMMENT_LOC_RESULTS);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
} else {
Location lastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
SharedPreferences sharedPreferences = getSharedPreferences(getString(R.string.user_shared_preferences), Context.MODE_PRIVATE);
LocationUtil.storeLocation(sharedPreferences, lastLocation);
}
}
if (mGoogleApiClient.isConnected()) {
mGoogleApiClient.unregisterConnectionCallbacks(this);
mGoogleApiClient.unregisterConnectionFailedListener(this);
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
mGoogleApiClient.disconnect();
}
mGoogleApiClient = null;
}
@Override
public void onConnected(@Nullable Bundle bundle) {
Timber.e("GAC connected");
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
Intent intent = new Intent(Constants.COMMENT_LOC_RESULTS);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
} else {
Timber.e("fetching location");
LocationRequest mLocationRequest = LocationRequest.create();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(1000 * 30);
mLocationRequest.setFastestInterval(1000 * 5);
if (ActivityCompat.checkSelfPermission(LocationIntentService.this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(LocationIntentService.this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
Intent intent = new Intent(Constants.COMMENT_LOC_RESULTS);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
return;
} else {
Timber.e("connected and access granted");
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
mLocationRequest,
LocationIntentService.this);
}
}
}
@Override
public void onConnectionSuspended(int i) {
Timber.e("GAC suspended");
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
Intent intent = new Intent(Constants.COMMENT_LOC_RESULTS);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
} else {
Location location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
SharedPreferences sharedPreferences = getSharedPreferences(getString(R.string.user_shared_preferences), Context.MODE_PRIVATE);
LocationUtil.storeLocation(sharedPreferences, location);
}
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Timber.e("GAC failed");
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
Intent intent = new Intent(Constants.COMMENT_LOC_RESULTS);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
} else {
Location location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
SharedPreferences sharedPreferences = getSharedPreferences(getString(R.string.user_shared_preferences), Context.MODE_PRIVATE);
LocationUtil.storeLocation(sharedPreferences, location);
}
}
}
Try using this service class to get your location information and save the lat and lng into your shareprefs.
The localbroadcastmanager is only there to request for permission in the activity class:
private BroadcastReceiver locationResultsReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
ActivityCompat.requestPermissions(CommentActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
LocationUtil.MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
}
};
The onrequestpermissionresult method in the activity class:
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case LocationUtil.MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: {
if (grantResults.length > 0) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Intent intent = new Intent(this, LocationIntentService.class);
startService(intent);
} else {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION)) {
//Show permission explanation dialog...
AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.AppCompatAlertDialogStyle);
builder.setTitle(Constants.LOCATION_PERM);
builder.setCancelable(false);
builder.setMessage(Constants.LOCATION_PERM_SUBTEXT);
builder.setPositiveButton(getString(R.string.ok), new AlertDialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
ActivityCompat.requestPermissions(CommentActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
LocationUtil.MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
}
});
builder.show();
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.AppCompatAlertDialogStyle);
builder.setTitle(Constants.LOCATION_PERM);
builder.setCancelable(false);
builder.setMessage(Constants.LOCATION_PERM_REREQUEST_SUBTEXT);
builder.setPositiveButton(getString(R.string.settings), new AlertDialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", CommentActivity.this.getPackageName(), null);
intent.setData(uri);
startActivity(intent);
finish();
}
});
builder.show();
}
}
}
break;
}
LeakCanary will still dump but it will now say NO LEAK FOUND:
04-16 12:13:54.465 8120-15176/com.myapp D/LeakCanary: In com.myapp:1.0:1.
* NO LEAK FOUND.
* Reference Key: 718959be-c178-4237-a24f-8d74972c9cc0
* Device: unknown generic Google Nexus 5 - 5.1.0 - API 22 - 1080x1920 vbox86p
* Android Version: 5.1 API: 22 LeakCanary: 1.3.1
* Durations: watch=5059ms, gc=135ms, heap dump=1186ms, analysis=5174ms
Hey @shailen any updates on this? It's quite surprising that such a big issue in play services, which is used in many apps, is not fixed after 10 months.
I've had good luck setting the actual listener that is leaking (such as the Activity) in a WeakReference and just forwarding the callback from a wrapper class:
public class WeakLocationListener implements LocationListener {
private final WeakReference<LocationListener> locationListenerRef;
public WeakLocationListener(@NonNull LocationListener locationListener) {
locationListenerRef = new WeakReference<>(locationListener);
}
@Override
public void onLocationChanged(android.location.Location location) {
if (locationListenerRef.get() == null) {
return;
}
locationListenerRef.get().onLocationChanged(location);
}
}
That way the instance of the wrapper class might leak, but the Activity does not.
In v9.8.0 it is still leaking. Please can someone tell the Google Play Services team about the weak references usage? Maybe this way they will fix this issue much faster.
@shailen any updates? I have also reported the issue here: https://code.google.com/p/android/issues/detail?id=227856
I have created a project that uses SupportMapFragment
and shows the user location using two approaches:
LocationServices.FusedLocationApi.requestLocationUpdates
googleMap.setOnMyLocationChangeListener
I've discovered that the second approach does not leak (though it is deprecated). This would be a workaround for some people that just need the user location to show it on a map.
@fernandospr Try to switch your GoogleApiClient instance to automanage, it will fix the leak.
@carlosjs23 Hi there Carlos, could you provide a snippet of how to use automanage to access the FusedLocationApi ? I know it should be the same as the other APIs but I'm struggling with it, thanks in advance
Passing application context to GoogleApiClient.Builder
should solve the leak.
mGoogleApiClient = new GoogleApiClient.Builder(getContext().getApplicationContext())
Sorry, I meant: use the "enableautomanage" method of the GoogleApiClient, ex:
GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this) .enableAutoManage(this / FragmentActivity /, this / OnConnectionFailedListener /)
El 12 dic. 2016 16:12, "Jose Agudelo" notifications@github.com escribió:
@carlosjs23 https://github.com/carlosjs23 Hi there Carlos, could you provide a snippet of how to use automanage to access the FusedLocationApi ? I know it should be the same as the other APIs but I'm struggling with it, thanks in advance
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/googlesamples/android-play-location/issues/26#issuecomment-266554191, or mute the thread https://github.com/notifications/unsubscribe-auth/AJ_QL086ScFODN2tYiB9iNbI-q1YNam3ks5rHbjFgaJpZM4GJ0Km .
@sealskej Using new GoogleApiClient.Builder(getApplicationContext())
still leaks.
@carlosjs23 Using the following still leaks:
mGoogleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this, this)
.addConnectionCallbacks(this)
.addApi(LocationServices.API)
.build();
I'm using @byencho approach and is not leaking anymore, @fernandospr try that
Thanks @thuytrinh removing the callbacks solved for me
Has this been fixed yet? I'm also experiencing the same issue.
@ryanthon no fixes from Google Play S. team, but you can use @byencho approach.
Just set up @byencho's approach on my project. I noticed that it didn't fix the issue if I only used the weak reference for the LocationListener. It seems like the issue is actually caused by the connection callbacks listeners (ConnectionCallbacks and OnConnectionFailedListener). Once I set up a weak reference for those as well, the leak went away.
Thanks @thuytrinh unregister the callbacks solved the problem for me.
Hi all, I've been struggling with the same problem for the the last day or so until I found this post. However in my case it was not enough to pass connection and location listeners as weak references as it LeakCanary was still reporting activity leaked. Which was my anonymous class passed to
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, mLocationCallback).then(new ResultTransform {...} )
to receive callback when the location listener has been removed. So I ended up not attaching this callback at the end. After all those three changes there were no more leaks reported. Hope this helps anyone in any way.
Has anyone tried the newer FusedLocationProviderClient instead? I tried the new sample for location updates, added Leak Canary, and again I still see a leak through LocationCallback. Even the onComplete callback for removeLocationUpdates isn't getting called in the sample.
@GEllickson yup, I migrated to FusedLocationProviderClient as well. & it is still leaking
I also have the same leak through LocationCallback using FusedLocationProviderClient.
We were able to solved it by @byencho approach combined with @sealskej approach. Also unregister callbacks after removeLocationUpdates thanks ...
Using @sealskej solution worked for Me.
With 'com.google.android.gms:play-services:11.0.1' I also get the problem about LocationCallback. But it can be fixed by WeakReference. my old code:
LocationCallback mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
// updateLocation();
}
};
use mLocationCallback:
mFusedLocationClient.requestLocationUpdates(mLocationRequest,
mLocationCallback, Looper.myLooper());
my fix code:
private static class LocationCallbackReference extends LocationCallback {
private WeakReference<LocationCallback> locationCallbackRef;
LocationCallbackReference(LocationCallback locationCallback) {
locationCallbackRef = new WeakReference<LocationCallback>(locationCallback);
}
@Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
if (locationCallbackRef != null && locationCallbackRef.get() != null) {
locationCallbackRef.get().onLocationResult(locationResult);
}
}
}
LocationCallback locationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
updateLocation();
}
};
mLocationCallback = new LocationCallbackReference(locationCallback);
mFusedLocationClient.requestLocationUpdates(mLocationRequest,
mLocationCallback, Looper.myLooper());
I just wrap the LocationCallback with WeakReference. The 'mFusedLocationClient' is constructed in this way:
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(activity);
rather than "LocationServices.FusedLocationApi".
With 'com.google.android.gms:play-services:11.0.1' I also get the problem about LocationCallback. But it can be fixed by WeakReference. my old code:
LocationCallback mLocationCallback = new LocationCallback() { @Override public void onLocationResult(LocationResult locationResult) { super.onLocationResult(locationResult); // updateLocation(); } };
use mLocationCallback:
mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper());
my fix code:
private static class LocationCallbackReference extends LocationCallback { private WeakReference<LocationCallback> locationCallbackRef; LocationCallbackReference(LocationCallback locationCallback) { locationCallbackRef = new WeakReference<LocationCallback>(locationCallback); } @Override public void onLocationResult(LocationResult locationResult) { super.onLocationResult(locationResult); if (locationCallbackRef != null && locationCallbackRef.get() != null) { locationCallbackRef.get().onLocationResult(locationResult); } } } LocationCallback locationCallback = new LocationCallback() { @Override public void onLocationResult(LocationResult locationResult) { super.onLocationResult(locationResult); updateLocation(); } }; mLocationCallback = new LocationCallbackReference(locationCallback); mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper());
I just wrap the LocationCallback with WeakReference. The 'mFusedLocationClient' is constructed in this way:
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(activity);
rather than "LocationServices.FusedLocationApi".
Oops!Not work for me!
I tried using WeakReference but still leaking, very disappointed 😔
gms play service 17.0.0, LocationCallback instance still leaking. workaround: use WeakReference in LocationCallback.
Still leaking for me in Leak Canary despite callback being removeLocationUpdates and locationCallback being set to nil. Does anyone have an example in kotlin for WeakReference?
I've used weak reference, still leaking:
mFusedLocationClient?.requestLocationUpdates(locationRequest, WeakReference(locationCallback).get(), null)
I have to say, I can't see how this is expected behaviour. It is a silly problem that should be solved by now!
I've used weak reference, still leaking:
mFusedLocationClient?.requestLocationUpdates(locationRequest, WeakReference(locationCallback).get(), null)
You got wrong with using java WeakReference
, You have to create a LocationCallback subclass which weak reference to your locationCallback
instance, and your locationCallback
itself must be strong referenced by your lifecycle container.
Reading an issue from 2015 in 2019 and seeing it's still not fixed and that too by Google, ridiculous! I see everyone suffering here and I am also the victim.
Anyways, I am here to tell how I solved the issue. @sealskej approach worked for me. I am using LocationListener
through a third-party library so all I needed to do was to pass applicationContext
in the library instead of activity context.
I've used weak reference, still leaking:
mFusedLocationClient?.requestLocationUpdates(locationRequest, WeakReference(locationCallback).get(), null)
You got wrong with using java
WeakReference
, You have to create a LocationCallback subclass which weak reference to yourlocationCallback
instance, and yourlocationCallback
itself must be strong referenced by your lifecycle container.
You have just won my noble Paul's prize. It comes with no perks but lots of thanks!! Thank you, leak sorted! Maybe I should read more, complain later. Thanks @1001101
(Moving this issue from playgames to here on behalf of @youfacepalm)
I am not sure if this is the right place to report this since I could not find any common "Play Services" issue reporting forum anywhere.. If there is such an issue tracker, please give me the link. Thanks
The latest version of Google play service's (8.1.0 as of writing this report) LocationListener is leaking the activity or service it is attached to according to LeakCanary version 1.3.1.
Steps to reproduce
-Download the LocationUpdate Sample from google's repo (https://github.com/googlesamples/android-play-location/tree/master/LocationUpdates)
-Update the gradle to include the latest version of Google play services (8.1.0, the sample contains Google play services version 6.5.87 which had no issues during my testing)
Add a regular activity (name it "TestActivity" for example) to the project
from the MainActivity, after starting the location update, go to the newly added (startActivity()) TestActivity while finishing the MainActivity (it doesn't matter if you stop or did not stop the location updates)
after about 5 seconds you will see LeakCanary reporting that the MainActivity has leaked (if it is added to the project)
Device Info: Model: Samsung Galaxy S6 edge OS: Android Lollipop v5.1.1 API 22
Trace: D/LeakCanary﹕ In com.hammockstudio.myapplication:1.0:1.
com.hammockstudio.myapplication.MainActivity has leaked: GC ROOT com.google.android.gms.location.internal.zzd$1$1.zzaFg (anonymous class extends com.google.android.gms.location.internal.zzg$zza) references com.google.android.gms.location.internal.zzd$1.zzaFe (anonymous class extends com.google.android.gms.location.internal.zzd$zza) leaks com.hammockstudio.myapplication.MainActivity instance [ 10-02 15:32:05.124 20752:21895 D/LeakCanary ] Reference Key: c3aab681-4124-4c8c-ac2c-97d52fe63e4f Device: samsung samsung SM-G925I zeroltedv Android Version: 5.1.1 API: 22 LeakCanary: 1.3.1 Durations: watch=5016ms, gc=133ms, heap dump=2572ms, analysis=17078ms [ 10-02 15:32:05.124 20752:21895 D/LeakCanary ] Details: Instance of com.google.android.gms.location.internal.zzd$1$1 | zzaFg = com.google.android.gms.location.internal.zzd$1 [id=0x32e4f380] | mDescriptor = java.lang.String [id=0x32e563a0] | mObject = 547036342800 | mOwner = com.google.android.gms.location.internal.zzd$1$1 [id=0x32e56380] Instance of com.google.android.gms.location.internal.zzd$1 | zzaFd = com.google.android.gms.location.LocationRequest [id=0x32dedf40] | zzaFe = com.hammockstudio.myapplication.MainActivity [id=0x32c179f0] | zzaFf = com.google.android.gms.location.internal.zzd [id=0x32df8510] | zzZM = com.google.android.gms.common.api.Api$zzc [id=0x32df84d0] | zzabg = java.util.concurrent.atomic.AtomicReference [id=0x32e55150] | zzL = true | zzaaX = com.google.android.gms.common.api.Status [id=0x32cdaf20] | zzabh = java.lang.Object [id=0x32e55130] | zzabi = com.google.android.gms.internal.zzlc$zza [id=0x32e562a0] | zzabj = java.util.ArrayList [id=0x32e56240] | zzabk = null | zzabl = false | zzabm = false | zzabn = null | zzabo = null | zzabp = null | zzoS = java.util.concurrent.CountDownLatch [id=0x32e55140] Again, this leak did not happen in the Google Play Services version that came with the Location Updates sample!