Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Location Updates with FusedLocationProviderClient produces memory leaks

I can't understand why this very simple activity produces memory leaks.

It follows the guidelines given here: https://developer.android.com/training/location/receive-location-updates.html and the example code given here: https://github.com/googlesamples/android-play-location/tree/master/LocationUpdates

I also noticed that the memory leak is solved if I don't override the onLocationResult method of the LocationCallback class. But in this ways the LocationCallback is completely useless.

Thank you for your help!

public class TestLeaks extends Activity {
    private FusedLocationProviderClient mFusedLocationClient;   // Access to Fused Loc. Provider API
    private LocationRequest mLocationRequest;         // Stores parameters for requests to F.L.P.Api
    private LocationCallback mLocationCallback;                      // Callback for Location events

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

        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
        mLocationCallback = new LocationCallback() {
            @Override
            public void onLocationResult(LocationResult locationResult) {
                super.onLocationResult(locationResult);
                Location loc = locationResult.getLastLocation();
                ((TextView) findViewById(R.id.latitude)).setText(String.format(Locale.US, "%.6f", loc.getLatitude()));
                ((TextView) findViewById(R.id.longitude)).setText(String.format(Locale.US, "%.6f", loc.getLongitude()));
            }
        };

        mLocationRequest = new LocationRequest()
                .setInterval(5000)
                .setFastestInterval(2000)
                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    }

    @SuppressLint("MissingPermission")
    @Override
    public void onResume() {
        super.onResume();
        mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper());
    }

    @Override
    protected void onPause() {
        super.onPause();
        mFusedLocationClient.removeLocationUpdates(mLocationCallback);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mLocationCallback = null;
    }
}

This is the output of LeakCanary about the detected memory leak:

In it.myapp.app:1.0.4:10004.
* it.myapp.app.activities.TestLeaks has leaked:
* GC ROOT com.google.android.gms.internal.zzcfe.zzfus
* references com.google.android.gms.common.api.internal.zzci.zzful
* references com.google.android.gms.common.api.internal.zzck.zzfuk
* references it.myapp.app.activities.TestLeaks$1.this$0 (anonymous subclass of com.google.android.gms.location.LocationCallback)
* leaks it.myapp.app.activities.TestLeaks instance

* Retaining: 35 KB.
* Reference Key: 969b4256-81ff-4b01-b0d4-63e27e49b764
* Device: samsung samsung SM-G920F zerofltexx
* Android Version: 7.0 API: 24 LeakCanary: 1.5.4 74837f0
* Durations: watch=637463ms, gc=258ms, heap dump=2766ms, analysis=46757ms

* Details:
* Instance of com.google.android.gms.internal.zzcfe
|   static $classOverhead = byte[768]@317861889 (0x12f23001)
|   zzfus = com.google.android.gms.common.api.internal.zzci@318105032 (0x12f5e5c8)
|   mDescriptor = java.lang.String@317290744 (0x12e978f8)
|   mObject = 511599546432
|   mOwner = com.google.android.gms.internal.zzcfe@317359520 (0x12ea85a0)
|   shadow$_klass_ = com.google.android.gms.internal.zzcfe
|   shadow$_monitor_ = 0
* Instance of com.google.android.gms.common.api.internal.zzci
|   static $classOverhead = byte[648]@316776449 (0x12e1a001)
|   zzfuj = com.google.android.gms.common.api.internal.zzcj@317594368 (0x12ee1b00)
|   zzfuk = null
|   zzful = com.google.android.gms.common.api.internal.zzck@318185888 (0x12f721a0)
|   shadow$_klass_ = com.google.android.gms.common.api.internal.zzci
|   shadow$_monitor_ = 0
* Instance of com.google.android.gms.common.api.internal.zzck
|   static $classOverhead = byte[608]@316789761 (0x12e1d401)
|   zzfuk = it.myapp.app.activities.TestLeaks$1@318185648 (0x12f720b0)
|   zzfun = java.lang.String@317049344 (0x12e5ca00)
|   shadow$_klass_ = com.google.android.gms.common.api.internal.zzck
|   shadow$_monitor_ = 0
* Instance of it.myapp.app.activities.TestLeaks$1
|   static $classOverhead = byte[640]@316606465 (0x12df0801)
|   static serialVersionUID = -1641506329145588052
|   static $change = null
|   this$0 = it.myapp.app.activities.TestLeaks@317686496 (0x12ef82e0)
|   shadow$_klass_ = it.myapp.app.activities.TestLeaks$1
|   shadow$_monitor_ = -1897236332
* Instance of it.myapp.app.activities.TestLeaks
|   static $classOverhead = byte[4336]@316567553 (0x12de7001)
|   static serialVersionUID = 2740572635131010457
|   static $change = null
|   mFusedLocationClient = com.google.android.gms.location.FusedLocationProviderClient@317049680 (0x12e5cb50)
|   mLocationCallback = null
|   mLocationRequest = com.google.android.gms.location.LocationRequest@318159496 (0x12f6ba88)
|   mActionBar = null
|   mActionModeTypeStarting = 0
|   mActivityInfo = android.content.pm.ActivityInfo@317696896 (0x12efab80)
|   mActivityTransitionState = android.app.ActivityTransitionState@317736656 (0x12f046d0)
|   mAppLockCheckRunnable = android.app.Activity$1@318057504 (0x12f52c20)
|   mAppLockIsInMultiWindowMode = false
|   mApplication = it.myapp.app.CanaryLeak@315492064 (0x12ce06e0)
|   mCalled = true
|   mChangeCanvasToTranslucent = false
|   mChangingConfigurations = false
|   mComponent = android.content.ComponentName@318099536 (0x12f5d050)
|   mConfigChangeFlags = 0
|   mCurrentConfig = android.content.res.Configuration@317764560 (0x12f0b3d0)
|   mDecor = null
|   mDefaultKeyMode = 0
|   mDefaultKeySsb = null
|   mDestroyed = true
|   mDoReportFullyDrawn = false
|   mEatKeyUpEvent = false
|   mEmbeddedID = null
|   mEnableDefaultActionBarUp = false
|   mEnterTransitionListener = android.app.SharedElementCallback$1@1899179144 (0x71332c88)
|   mExitTransitionListener = android.app.SharedElementCallback$1@1899179144 (0x71332c88)
|   mFinished = true
|   mFlipfont = 0
|   mFragments = android.app.FragmentController@318057472 (0x12f52c00)
|   mHandler = android.os.Handler@317542688 (0x12ed5120)
|   mHasCurrentPermissionsRequest = false
|   mIdent = 165309401
|   mInstanceTracker = android.os.StrictMode$InstanceTracker@318057488 (0x12f52c10)
|   mInstrumentation = android.app.Instrumentation@315494968 (0x12ce1238)
|   mIntent = android.content.Intent@314929344 (0x12c570c0)
|   mLastNonConfigurationInstances = null
|   mMainThread = android.app.ActivityThread@315314736 (0x12cb5230)
|   mManagedCursors = java.util.ArrayList@318035832 (0x12f4d778)
|   mManagedDialogs = null
|   mMenuInflater = null
|   mParent = null
|   mPolicyManager = null
|   mReferrer = java.lang.String@314929856 (0x12c572c0)
|   mResultCode = 0
|   mResultData = null
|   mResumed = false
|   mScreenChangeListener = null
|   mSearchEvent = null
|   mSearchManager = null
|   mStartedActivity = false
|   mStopped = true
|   mTaskDescription = android.app.ActivityManager$TaskDescription@317542720 (0x12ed5140)
|   mTemporaryPause = false
|   mTitle = java.lang.String@315505152 (0x12ce3a00)
|   mTitleColor = 0
|   mTitleReady = true
|   mToken = android.os.BinderProxy@315387808 (0x12cc6fa0)
|   mTranslucentCallback = null
|   mUiThread = java.lang.Thread@2004343960 (0x7777dc98)
|   mVisibleBehind = false
|   mVisibleFromClient = true
|   mVisibleFromServer = true
|   mVoiceInteractor = null
|   mWindow = com.android.internal.policy.PhoneWindow@315451968 (0x12cd6a40)
|   mWindowAdded = true
|   mWindowManager = android.view.WindowManagerImpl@318034248 (0x12f4d148)
|   mInflater = com.android.internal.policy.PhoneLayoutInflater@316645632 (0x12dfa100)
|   mOverrideConfiguration = null
|   mResources = android.content.res.Resources@316047040 (0x12d67ec0)
|   mTheme = android.content.res.Resources$Theme@318057696 (0x12f52ce0)
|   mThemeResource = 2131820551
|   mBase = android.app.ContextImpl@316473008 (0x12dcfeb0)
|   shadow$_klass_ = it.myapp.app.activities.TestLeaks
|   shadow$_monitor_ = 1073742768
* Excluded Refs:
| Field: android.view.inputmethod.InputMethodManager.mNextServedView
| Field: android.view.inputmethod.InputMethodManager.mServedView
| Field: android.view.inputmethod.InputMethodManager.mServedInputConnection
| Field: android.view.textservice.SpellCheckerSession$1.this$0
| Field: com.samsung.android.content.clipboard.SemClipboardManager.mContext
| Field: com.samsung.android.emergencymode.SemEmergencyManager.mContext
| Field: android.view.Choreographer$FrameDisplayEventReceiver.mMessageQueue (always)
| Thread:FinalizerWatchdogDaemon (always)
| Thread:main (always)
| Thread:LeakCanary-Heap-Dump (always)
| Class:java.lang.ref.WeakReference (always)
| Class:java.lang.ref.SoftReference (always)
| Class:java.lang.ref.PhantomReference (always)
| Class:java.lang.ref.Finalizer (always)
| Class:java.lang.ref.FinalizerReference (always)
like image 208
faCap77 Avatar asked Feb 07 '18 23:02

faCap77


People also ask

What is memory leak in database?

A Memory Leak is a situation where there are objects present in the heap that are no longer used, but the garbage collector is unable to remove them from memory, and therefore, they're unnecessarily maintained. A memory leak is bad because it blocks memory resources and degrades system performance over time.

What is FusedLocationProviderClient Android?

FusedLocationProviderClient is for interacting with the location using fused location provider. (NOTE : To use this feature, GPS must be turned on your device.


1 Answers

I face the same issue, and then is my fixed code for someone else if needed .

class WeakLocationCallback(locationCallback: LocationCallback) : LocationCallback() {

    private val weakLocationCallback = WeakReference<LocationCallback>(locationCallback)
    override fun onLocationResult(result: LocationResult) {
        super.onLocationResult(result)
        weakLocationCallback.get()?.onLocationResult(result)
    }
}  

and

        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
        val locationRequest = LocationRequest()
        locationRequest.priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY
        locationCallback = object : LocationCallback() {
            override fun onLocationResult(result: LocationResult) {
                super.onLocationResult(result)
                currentLocation = result.lastLocation
            }
        }
        val weakLocationCallback = WeakLocationCallback(locationCallback)
        fusedLocationClient.requestLocationUpdates(locationRequest, weakLocationCallback, Looper.myLooper())
like image 58
Cuong Nguyen Avatar answered Oct 02 '22 19:10

Cuong Nguyen