I'm using smart-location-lib to get the current location of the device once. It's needed for later use during the activity's lifecycle.
Upon doing finish()
from the activity to go back to the previous activity I get a memory leak.
Here's the code:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
SmartLocation.with(this).location()
.oneFix()
.start(new OnLocationUpdatedListener() {
@Override
public void onLocationUpdated(Location location) {
mCurrentLocation = location;
}
});
...
}
...
@Override
protected void onStop() {
super.onStop();
SmartLocation.with(this).location().stop();
}
As you can see I'm doing smart-location-lib's .stop()
in the Activity's onStop() call as described in the documentation here.
But this doesn't prevent the leak. Here's information about the leak collected with LeakCanary:
In com.companyname.appname.development:1.0-development:1.
* com.companyname.appname.activities.TaskCheckoutActivity has leaked:
* GC ROOT com.google.android.gms.internal.zzbyw.zzfwc
* references com.google.android.gms.internal.zzbyt.zzhyy
* references io.nlopez.smartlocation.location.providers.LocationGooglePlayServicesProvider.context
* leaks com.companyname.appname.activities.TaskCheckoutActivity instance
* Retaining: 1,6 MB.
* Reference Key: ba2a19b1-d5ca-4971-a5c7-e0fd99ea95a6
* Device: samsung samsung SM-G930F heroltexx
* Android Version: 7.0 API: 24 LeakCanary: 1.5.4 74837f0
* Durations: watch=5010ms, gc=215ms, heap dump=1792ms, analysis=128865ms
* Details:
* Instance of com.google.android.gms.internal.zzbyw
| static $classOverhead = byte[752]@858164225 (0x33268c01)
| zzfwc = com.google.android.gms.internal.zzbyt@861919192 (0x335fd7d8)
| mDescriptor = java.lang.String@854648480 (0x32f0e6a0)
| mObject = 501026202704
| mOwner = com.google.android.gms.internal.zzbyw@856066112 (0x33068840)
| shadow$_klass_ = com.google.android.gms.internal.zzbyw
| shadow$_monitor_ = 0
* Instance of com.google.android.gms.internal.zzbyt
| static $classOverhead = byte[792]@855184385 (0x32f91401)
| zzhyy = io.nlopez.smartlocation.location.providers.LocationGooglePlayServicesProvider@856875576 (0x3312e238)
| zzfda = com.google.android.gms.common.api.Api@856792448 (0x33119d80)
| zzfip = com.google.android.gms.common.api.Api$zzf@854593816 (0x32f01118)
| mStatus = com.google.android.gms.common.api.Status@856929688 (0x3313b598)
| zzaj = true
| zzaop = java.util.concurrent.CountDownLatch@855708832 (0x330114a0)
| zzfhl = com.google.android.gms.common.api.Status@856929688 (0x3313b598)
| zzfiz = java.lang.Object@854593632 (0x32f01060)
| zzfja = com.google.android.gms.common.api.internal.zzu@856066144 (0x33068860)
| zzfjb = java.lang.ref.WeakReference@857866976 (0x332202e0)
| zzfjc = java.util.ArrayList@857867120 (0x33220370)
| zzfjd = null
| zzfje = java.util.concurrent.atomic.AtomicReference@855708848 (0x330114b0)
| zzfjf = null
| zzfjg = false
| zzfjh = false
| zzfji = null
| zzfjj = null
| zzfjk = false
| shadow$_klass_ = com.google.android.gms.internal.zzbyt
| shadow$_monitor_ = -1953408656
* Instance of io.nlopez.smartlocation.location.providers.LocationGooglePlayServicesProvider
| static REQUEST_CHECK_SETTINGS = 20001
| static GMS_ID = java.lang.String@856784656 (0x33117f10)
| static $classOverhead = byte[748]@857097217 (0x33164401)
| static REQUEST_START_LOCATION_FIX = 10001
| alwaysShow = true
| checkLocationSettings = false
| client = com.google.android.gms.common.api.internal.zzbd@856996208 (0x3314b970)
| context = com.companyname.appname.activities.TaskCheckoutActivity@860313600 (0x33475800)
| fulfilledCheckLocationSettings = false
| googlePlayServicesListener = io.nlopez.smartlocation.location.providers.LocationGooglePlayServicesWithFallbackProvider@856791392 (0x33119960)
| listener = com.companyname.appname.activities.TaskCheckoutActivity$1@856799680 (0x3311b9c0)
| locationRequest = com.google.android.gms.location.LocationRequest@856876304 (0x3312e510)
| locationStore = io.nlopez.smartlocation.location.LocationStore@859517520 (0x333b3250)
| logger = io.nlopez.smartlocation.utils.LoggerFactory$Sssht@854569160 (0x32efb0c8)
| serviceListener = null
| settingsResultCallback = io.nlopez.smartlocation.location.providers.LocationGooglePlayServicesProvider$1@856798672 (0x3311b5d0)
| shouldStart = false
| stopped = true
| shadow$_klass_ = io.nlopez.smartlocation.location.providers.LocationGooglePlayServicesProvider
| shadow$_monitor_ = -2147272239
* Instance of com.companyname.appname.activities.TaskCheckoutActivity
| static $classOverhead = byte[4720]@856289281 (0x3309f001)
| BARCODE_SCANNER_REQUEST = java.lang.Integer@856384016 (0x330b6210)
| BEGIN_SIGNATURE_REQUEST = java.lang.Integer@856384000 (0x330b6200)
| mApiKey = java.lang.String@856965696 (0x33144240)
| mCheckoutEmptyStepView = null
| mCurrentLocation = android.location.Location@859607248 (0x333c90d0)
| mDamagedAddressUnknownDescriptionCheckFailed = java.lang.Boolean@1881937168 (0x702c1510)
| mDamagedAddressUnknownDescriptionEditText = null
| mDamagedAddressUnknownStepView = null
| mJobManager = com.birbit.android.jobqueue.JobManager@851730080 (0x32c45ea0)
| mKibCardInputCheckFailed = java.lang.Boolean@1881937168 (0x702c1510)
| mKibCardInputEditText = null
| mKibCardInputStepView = null
| mKibCardNextTimeframe = null
| mKibNextAbbrevation = null
| mKibNextDay = null
| mMarginBetweenStatusAndIcons = android.support.v7.widget.AppCompatTextView@860264448 (0x33469800)
| mNameCheckFailed = java.lang.Boolean@1881937168 (0x702c1510)
| mNeighbourAddressStepView = null
| mNeighbourCityFailed = java.lang.Boolean@1881937168 (0x702c1510)
| mNeighbourCityInputEditText = null
| mNeighbourStreetFailed = java.lang.Boolean@1881937168 (0x702c1510)
| mNeighbourStreetInputEditText = null
| mNeighbourStreetNumberFailed = java.lang.Boolean@1881937152 (0x702c1500)
| mNeighbourStreetNumberInputEditText = null
| mNeighbourZipcodeFailed = java.lang.Boolean@1881937168 (0x702c1510)
| mNeighbourZipcodeInputEditText = null
| mOpenBarcodeScannerButton = null
| mParcelKey = java.lang.String@856985856 (0x33149100)
| mPaymentCompleted = java.lang.Boolean@1881937168 (0x702c1510)
| mPaymentStepView = null
| mPaymentSwitch = null
| mPaymentTextView = null
| mRecipientContainer = android.widget.RelativeLayout@860361728 (0x33481400)
| mRecipientNameEditText = android.widget.EditText@860366848 (0x33482800)
| mRefusedReasonCheckFailed = java.lang.Boolean@1881937168 (0x702c1510)
| mRefusedReasonEditText = null
| mRefusedStepView = null
| mSignatureBitmap = android.graphics.Bitmap@854106464 (0x32e8a160)
| mSignatureButton = android.widget.Button@860369920 (0x33483400)
| mSignatureFragmentContainer = android.widget.RelativeLayout@860368896 (0x33483000)
| mSignaturePreviewImageView = android.widget.ImageView@860370944 (0x33483800)
| mSignatureStepView = android.widget.RelativeLayout@860360704 (0x33481000)
| mSigned = java.lang.Boolean@1881937152 (0x702c1500)
| mSpecialContainerTaskStatusTextView = android.support.v7.widget.AppCompatTextView@860262400 (0x33469000)
| mSpecialIconA = android.support.v7.widget.AppCompatImageView@860296192 (0x33471400)
| mSpecialIconB = android.support.v7.widget.AppCompatImageView@860297216 (0x33471800)
| mSpecialIconC = android.support.v7.widget.AppCompatImageView@860298240 (0x33471c00)
| mSpecialIconD = android.support.v7.widget.AppCompatImageView@860299264 (0x33472000)
| mSpecialIconsLinearLayout = android.widget.LinearLayout@860261376 (0x33468c00)
| mSteps = java.lang.String[2]@860283592 (0x3346e2c8)
| mTask = com.companyname.appname.models..Task@857797632 (0x3320f400)
| mTaskCheckout = com.companyname.appname.helpers.TaskCheckout@859397192 (0x33395c48)
| mTaskCheckoutMainLinLayout = android.widget.LinearLayout@856667136 (0x330fb400)
| mTaskStatus = com.companyname.appname.enums.TaskStatus@851451392 (0x32c01e00)
| mTaskStatusLabel = android.widget.TextView@860358656 (0x33480800)
| mTaskStatusSelectedIndex = java.lang.Integer@1882345520 (0x70325030)
| mTaskStatusText = java.lang.String@858039008 (0x3324a2e0)
| mTaskStatusTextView = android.widget.TextView@860359680 (0x33480c00)
| mTaskStatusView = android.widget.FrameLayout@860321792 (0x33477800)
| mVerticalStepperForm = ernestoyaquello.com.verticalstepperform.VerticalStepperFormLayout@860300288 (0x33472400)
| mDelegate = android.support.v7.app.AppCompatDelegateImplN@856322976 (0x330a73a0)
| mResources = null
| mThemeId = 2131427510
| mCreated = true
| mFragments = android.support.v4.app.FragmentController@856383984 (0x330b61f0)
| mHandler = android.support.v4.app.FragmentActivity$1@856395936 (0x330b90a0)
| mNextCandidateRequestIndex = 0
| mPendingFragmentActivityResults = android.support.v4.util.SparseArrayCompat@856400656 (0x330ba310)
| mReallyStopped = true
| mRequestedPermissionsFromFragment = false
| mResumed = false
| mRetaining = false
| mStopped = true
| mStartedActivityFromFragment = false
| mStartedIntentSenderFromFragment = false
| mExtraDataMap = android.support.v4.util.SimpleArrayMap@856309016 (0x330a3d18)
| mActionBar = null
| mActionModeTypeStarting = 0
| mActivityInfo = android.content.pm.ActivityInfo@855609200 (0x32ff8f70)
| mActivityTransitionState = android.app.ActivityTransitionState@856227168 (0x3308fd60)
| mAppLockCheckRunnable = android.app.Activity$1@856383936 (0x330b61c0)
| mAppLockIsInMultiWindowMode = false
| mApplication = com.companyname.appname.DeliveryApplication@851900944 (0x32c6fa10)
| mCalled = true
| mChangeCanvasToTranslucent = false
| mChangingConfigurations = false
| mComponent = android.content.ComponentName@856313936 (0x330a5050)
| mConfigChangeFlags = 0
| mCurrentConfig = android.content.res.Configuration@855650184 (0x33002f88)
| mDecor = null
| mDefaultKeyMode = 0
| mDefaultKeySsb = null
| mDestroyed = true
| mDoReportFullyDrawn = false
| mEatKeyUpEvent = false
| mEmbeddedID = null
| mEnableDefaultActionBarUp = false
| mEnterTransitionListener = android.app.SharedElementCallback$1@1891321968 (0x70bb4870)
| mExitTransitionListener = android.app.SharedElementCallback$1@1891321968 (0x70bb4870)
| mFinished = true
| mFlipfont = 0
| mFragments = android.app.FragmentController@856383904 (0x330b61a0)
| mHandler = android.os.Handler@856395872 (0x330b9060)
| mHasCurrentPermissionsRequest = false
| mIdent = 226856808
| mInstanceTracker = android.os.StrictMode$InstanceTracker@856383920 (0x330b61b0)
| mInstrumentation = android.app.Instrumentation@851709880 (0x32c40fb8)
| mIntent = android.content.Intent@858578880 (0x332cdfc0)
| mLastNonConfigurationInstances = null
| mMainThread = android.app.ActivityThread@851460448 (0x32c04160)
| mManagedCursors = java.util.ArrayList@856308968 (0x330a3ce8)
| mManagedDialogs = null
| mMenuInflater = null
| mParent = null
| mPolicyManager = null
| mReferrer = java.lang.String@856356128 (0x330af520)
| mResultCode = -1
| mResultData = null
| mResumed = false
| mScreenChangeListener = null
| mSearchEvent = null
| mSearchManager = null
| mStartedActivity = false
| mStopped = true
| mTaskDescription = android.app.ActivityManager$TaskDescription@856395904 (0x330b9080)
| mTemporaryPause = false
| mTitle = java.lang.String@851901504 (0x32c6fc40)
| mTitleColor = 0
| mTitleReady = true
| mToken = android.os.BinderProxy@856318080 (0x330a6080)
| mTranslucentCallback = null
| mUiThread = java.lang.Thread@1999246640 (0x772a1530)
| mVisibleBehind = false
| mVisibleFromClient = true
| mVisibleFromServer = true
| mVoiceInteractor = null
| mWindow = com.android.internal.policy.PhoneWindow@860593808 (0x334b9e90)
| mWindowAdded = true
| mWindowManager = android.view.WindowManagerImpl@856400152 (0x330ba118)
| mInflater = com.android.internal.policy.PhoneLayoutInflater@858279648 (0x33284ee0)
| mOverrideConfiguration = null
| mResources = android.content.res.Resources@858466752 (0x332b29c0)
| mTheme = android.content.res.Resources$Theme@856384208 (0x330b62d0)
| mThemeResource = 2131427510
| mBase = android.app.ContextImpl@854679440 (0x32f15f90)
| shadow$_klass_ = com.companyname.appname.activities.TaskCheckoutActivity
| shadow$_monitor_ = 1073743558
* 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)
I've tried not using smart-location-lib and implement code to get the current location as written in the official training documentation from Android here. But this also causes a memory leak from com.google.android.gms.internal.*
.
I've tried this on two devices (Galaxy S7 on Android 7.0 & Galaxy J2 2016 on Android 5.1.1) and they all produce a memory leak. The project uses compile 'com.google.android.gms:play-services-location:11.2.2'
What am I doing wrong?
The Memory Profiler is a component in the Android Profiler that helps you identify memory leaks and memory churn that can lead to stutter, freezes, and even app crashes. It shows a realtime graph of your app's memory use and lets you capture a heap dump, force garbage collections, and track memory allocations.
Google Mobile Services (GMS) is a collection of Google applications and APIs that help support functionality across devices. These apps work together seamlessly to ensure your device provides a great user experience right out of the box.
Memory leaks occur when an application allocates memory for an object, but then fails to release the memory when the object is no longer being used. Over time, leaked memory accumulates and results in poor app performance and even crashes.
I had the same issue with android.widget objects like imageview, button, etc while doing animations so i created static inner class like below instead of anonymous or non-static inner class and in the static i am storing android.widget objects in a weak reference.
private static class ImageViewAnimation implements ValueAnimator.AnimatorUpdateListener {
private final WeakReference<ImageView> mpetImageView;
private int axis;
public ImageViewAnimation(ImageView mpetImageView, int axis) {
this.mpetImageView = new WeakReference<>(mpetImageView);
this.axis = axis;
}
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
ImageView view = mpetImageView.get();
int value = (int) valueAnimator.getAnimatedValue();
if (this.axis == 1) {
view.setTranslationX(value);
} else if (this.axis == 2) {
view.setTranslationY(value);
}
}
}
I use the above class in my Activity as
petx.addUpdateListener(new ImageViewAnimation(petImageView, 1));
The take away from this example for your use case would be the following
OnLocationUpdatedListener
and use this classes instance where ever you want.mcurretLocation
you are using is non-staticonPause()
or onDestroy()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With