android / location-samples

Multiple samples showing the best practices in location APIs on Android.
Apache License 2.0
2.71k stars 2.77k forks source link

Google play service's v8.1.0's location listener is leaking the activity or service it is attached to #26

Open claywilkinson opened 9 years ago

claywilkinson commented 9 years ago

(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!

shailen commented 9 years ago

Thanks, this is invaluable feedback. Taking a look.

youfacepalm commented 9 years ago

Hey @shailen, any updates on the issue?

shailen commented 9 years ago

@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.

youfacepalm commented 8 years ago

Hey @shailen if you have any updates can you please share it?

shailen commented 8 years ago

The eng team is aware of the problem and is working on it. I'll update here when there's a fix.

APBrown commented 8 years ago

@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.

youfacepalm commented 8 years ago

@shailen I checked on Google Play Services 8.4.0 as well. The problem still remains!

thuytrinh commented 8 years ago

Any update on this? OOM kept happening as a result in our end :(

youfacepalm commented 8 years ago

@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!

SudaNix commented 8 years ago

Any updates? We faced a lot of OOM crashes because of this issue.

thuytrinh commented 8 years ago

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();
shailen commented 8 years ago

@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 .

xserxses commented 8 years ago

Got same problem. :( any updates?

pjcs2 commented 8 years ago

Same problem here. Please let me know when any updated

shailen commented 8 years ago

Sorry everyone, no definitive updates on when the fix will land. Will update once I know more.

yayaa commented 8 years ago

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.

Winghin2517 commented 8 years ago

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]
Winghin2517 commented 8 years ago

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
droidster commented 8 years ago

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.

byencho commented 8 years ago

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.

stanmots commented 7 years ago

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.

fernandospr commented 7 years ago

@shailen any updates? I have also reported the issue here: https://code.google.com/p/android/issues/detail?id=227856

fernandospr commented 7 years ago

I have created a project that uses SupportMapFragment and shows the user location using two approaches:

  1. LocationServices.FusedLocationApi.requestLocationUpdates
  2. 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.

carlosjs23 commented 7 years ago

@fernandospr Try to switch your GoogleApiClient instance to automanage, it will fix the leak.

alejofila commented 7 years ago

@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

sealskej commented 7 years ago

Passing application context to GoogleApiClient.Builder should solve the leak.

mGoogleApiClient = new GoogleApiClient.Builder(getContext().getApplicationContext())
carlosjs23 commented 7 years ago

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 .

fernandospr commented 7 years ago

@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();
alejofila commented 7 years ago

I'm using @byencho approach and is not leaking anymore, @fernandospr try that

Naibeck commented 7 years ago

Thanks @thuytrinh removing the callbacks solved for me

ryanthon commented 7 years ago

Has this been fixed yet? I'm also experiencing the same issue.

carlosjs23 commented 7 years ago

@ryanthon no fixes from Google Play S. team, but you can use @byencho approach.

ryanthon commented 7 years ago

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.

kaininggu commented 7 years ago

Thanks @thuytrinh unregister the callbacks solved the problem for me.

aidanas commented 7 years ago

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.

GEllickson commented 7 years ago

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.

xserxses commented 7 years ago

@GEllickson yup, I migrated to FusedLocationProviderClient as well. & it is still leaking

Alkisum commented 7 years ago

I also have the same leak through LocationCallback using FusedLocationProviderClient.

rajeevinimekumar008 commented 7 years ago

We were able to solved it by @byencho approach combined with @sealskej approach. Also unregister callbacks after removeLocationUpdates thanks ...

refaelsh commented 6 years ago

Using @sealskej solution worked for Me.

HOLDfoot commented 6 years ago

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".

IrvingRyan commented 5 years ago

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!

n-abdelmaksoud commented 5 years ago

I tried using WeakReference but still leaking, very disappointed 😔

ghost commented 5 years ago

gms play service 17.0.0, LocationCallback instance still leaking. workaround: use WeakReference in LocationCallback. image

paulsUsername commented 5 years ago

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?

paulsUsername commented 5 years ago

I've used weak reference, still leaking:

mFusedLocationClient?.requestLocationUpdates(locationRequest, WeakReference(locationCallback).get(), null)

paulsUsername commented 5 years ago

I have to say, I can't see how this is expected behaviour. It is a silly problem that should be solved by now!

ghost commented 5 years ago

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.

sidhuparas commented 5 years ago

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.

paulsUsername commented 5 years ago

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.

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