I keep having the following memory leak as displayed by LeakCanary, when I go from my splash screen to the mainactivity. I understand that this is an expected leak due to fault in the Android OS itself, but is there a way I can avoid this (by setting specifics of some TextView somewhere?)
D/LeakCanary﹕ * LEAK CAN BE IGNORED.
D/LeakCanary﹕ * com.gmspartnersltd.earthmiles.views.ActivitySignUp_ has leaked:
D/LeakCanary﹕ * GC ROOT static android.text.TextLine.sCached
D/LeakCanary﹕ * references array android.text.TextLine[].[1]
D/LeakCanary﹕ * references android.text.TextLine.mCharacterStyleSpanSet
D/LeakCanary﹕ * references android.text.SpanSet.spans
D/LeakCanary﹕ * references array android.text.style.CharacterStyle[].[1]
D/LeakCanary﹕ * references com.gmspartnersltd.earthmiles.views.ActivitySignUp$2.this$0 (anonymous class extends android.text.style.ClickableSpan)
D/LeakCanary﹕ * leaks com.gmspartnersltd.earthmiles.views.ActivitySignUp_ instance
D/LeakCanary﹕ [ 05-22 08:54:52.160 13969:18091 D/LeakCanary ]
* Reference Key: bb8124a9-2829-4ff3-8ded-13cf35f80f54
D/LeakCanary﹕ * Device: Genymotion generic Google Nexus 5 - 5.0.0 - API 21 - 1080x1920 vbox86p
D/LeakCanary﹕ * Android Version: 5.0 API: 21 LeakCanary: 1.3.1
D/LeakCanary﹕ * Durations: watch=10898ms, gc=137ms, heap dump=5529ms, analysis=9193ms
D/LeakCanary﹕ [ 05-22 08:54:52.160 13969:18091 D/LeakCanary ]
* Details:
D/LeakCanary﹕ * Class android.text.TextLine
D/LeakCanary﹕ | static $staticOverhead = byte[] [id=0x70622169;length=24;size=40]
D/LeakCanary﹕ | static sCached = android.text.TextLine[] [id=0x70775010;length=3]
D/LeakCanary﹕ | static DEBUG = false
D/LeakCanary﹕ | static TAB_INCREMENT = 20
D/LeakCanary﹕ * Array of android.text.TextLine[]
D/LeakCanary﹕ | [0] = android.text.TextLine [id=0x1309a2e0]
D/LeakCanary﹕ | [1] = android.text.TextLine [id=0x12eed650]
D/LeakCanary﹕ | [2] = null
D/LeakCanary﹕ * Instance of android.text.TextLine
D/LeakCanary﹕ | static $staticOverhead = byte[] [id=0x70622169;length=24;size=40]
D/LeakCanary﹕ | static sCached = android.text.TextLine[] [id=0x70775010;length=3]
D/LeakCanary﹕ | static DEBUG = false
D/LeakCanary﹕ | static TAB_INCREMENT = 20
D/LeakCanary﹕ | mCharacterStyleSpanSet = android.text.SpanSet [id=0x12e32d80]
D/LeakCanary﹕ | mChars = null
D/LeakCanary﹕ | mDirections = null
D/LeakCanary﹕ | mMetricAffectingSpanSpanSet = android.text.SpanSet [id=0x12e32d60]
D/LeakCanary﹕ | mPaint = null
D/LeakCanary﹕ | mReplacementSpanSpanSet = android.text.SpanSet [id=0x12e32da0]
D/LeakCanary﹕ | mSpanned = android.text.SpannedString [id=0x132dabe0]
D/LeakCanary﹕ | mTabs = null
D/LeakCanary﹕ | mText = null
D/LeakCanary﹕ | mWorkPaint = android.text.TextPaint [id=0x1300f5c0]
D/LeakCanary﹕ | mCharsValid = false
D/LeakCanary﹕ | mDir = 1
D/LeakCanary﹕ | mHasTabs = false
D/LeakCanary﹕ | mLen = 1
D/LeakCanary﹕ | mStart = 0
D/LeakCanary﹕ * Instance of android.text.SpanSet
D/LeakCanary﹕ | classType = java.lang.Class [id=0x703bb448;name=android.text.style.CharacterStyle]
D/LeakCanary﹕ | spanEnds = int[] [id=0x1309fd60;length=2;size=24]
D/LeakCanary﹕ | spanFlags = int[] [id=0x1309fda0;length=2;size=24]
D/LeakCanary﹕ | spanStarts = int[] [id=0x1309fd20;length=2;size=24]
D/LeakCanary﹕ | spans = android.text.style.CharacterStyle[] [id=0x1309fce0;length=2]
D/LeakCanary﹕ | numberOfSpans = 1
D/LeakCanary﹕ * Array of android.text.style.CharacterStyle[]
D/LeakCanary﹕ | [0] = null
D/LeakCanary﹕ | [1] = com.gmspartnersltd.earthmiles.views.ActivitySignUp$2 [id=0x130952c0]
D/LeakCanary﹕ * Instance of com.gmspartnersltd.earthmiles.views.ActivitySignUp$2
D/LeakCanary﹕ | this$0 = com.gmspartnersltd.earthmiles.views.ActivitySignUp_ [id=0x13361800]
D/LeakCanary﹕ * Instance of com.gmspartnersltd.earthmiles.views.ActivitySignUp_
D/LeakCanary﹕ | onViewChangedNotifier_ = org.androidannotations.api.view.OnViewChangedNotifier [id=0x13052dc0]
D/LeakCanary﹕ | birthday = null
D/LeakCanary﹕ | buttonNext = android.support.v7.widget.AppCompatButton [id=0x13459c00]
D/LeakCanary﹕ | confirmPassword = java.lang.String [id=0x132f01a0]
D/LeakCanary﹕ | editTextConformPassword = android.support.v7.widget.AppCompatEditText [id=0x13458400]
D/LeakCanary﹕ | editTextEmail = android.support.v7.widget.AppCompatEditText [id=0x1339b000]
D/LeakCanary﹕ | editTextFirstName = android.support.v7.widget.AppCompatEditText [id=0x13396c00]
D/LeakCanary﹕ | editTextLastName = android.support.v7.widget.AppCompatEditText [id=0x13398800]
D/LeakCanary﹕ | editTextPassword = android.support.v7.widget.AppCompatEditText [id=0x13456c00]
D/LeakCanary﹕ | email = java.lang.String [id=0x132f0040]
D/LeakCanary﹕ | facebook = com.facebook.android.Facebook [id=0x1307ee00]
D/LeakCanary﹕ | fbUserId = null
D/LeakCanary﹕ | firstName = java.lang.String [id=0x132f0080]
D/LeakCanary﹕ | gender = null
D/LeakCanary﹕ | lastName = java.lang.String [id=0x132f00e0]
D/LeakCanary﹕ | location = null
D/LeakCanary﹕ | mAsyncRunner = com.facebook.android.AsyncFacebookRunner [id=0x130952a0]
D/LeakCanary﹕ | password = java.lang.String [id=0x132f0140]
D/LeakCanary﹕ | termsOfUse = android.support.v7.widget.AppCompatTextView [id=0x1345a000]
D/LeakCanary﹕ | text = android.text.SpannableString [id=0x1310eaa0]
D/LeakCanary﹕ | fromFacebook = false
D/LeakCanary﹕ | etHelpMessage = null
D/LeakCanary﹕ | mProgressHUD = null
D/LeakCanary﹕ | positiveAction = null
D/LeakCanary﹕ | showBusyAnimationRequesterCount = 0
D/LeakCanary﹕ | mDelegate = android.support.v7.app.AppCompatDelegateImplV11 [id=0x12ecfd80]
D/LeakCanary﹕ | mAllLoaderManagers = android.support.v4.util.SimpleArrayMap [id=0x131d80a0]
D/LeakCanary﹕ | mContainer = android.support.v4.app.FragmentActivity$2 [id=0x13052db0]
D/LeakCanary﹕ | mFragments = android.support.v4.app.FragmentManagerImpl [id=0x12f7cf60]
D/LeakCanary﹕ | mHandler = android.support.v4.app.FragmentActivity$1 [id=0x1310ea80]
D/LeakCanary﹕ | mLoaderManager = null
D/LeakCanary﹕ | mCheckedForLoaderManager = true
D/LeakCanary﹕ | mCreated = true
D/LeakCanary﹕ | mLoadersStarted = false
D/LeakCanary﹕ | mOptionsMenuInvalidated = false
D/LeakCanary﹕ | mReallyStopped = true
D/LeakCanary﹕ | mResumed = false
D/LeakCanary﹕ | mRetaining = false
D/LeakCanary﹕ | mStopped = true
D/LeakCanary﹕ | mActionBar = null
D/LeakCanary﹕ | mActivityInfo = android.content.pm.ActivityInfo [id=0x12db0180]
D/LeakCanary﹕ | mActivityTransitionState = android.app.ActivityTransitionState [id=0x1304b600]
D/LeakCanary﹕ | mAllLoaderManagers = android.util.ArrayMap [id=0x131c9d00]
D/LeakCanary﹕ | mApplication = com.gmspartnersltd.earthmiles.globalstate.App [id=0x12c6e8c0]
D/LeakCanary﹕ | mComponent = android.content.ComponentName [id=0x12f64150]
D/LeakCanary﹕ | mContainer = android.app.Activity$1 [id=0x13052d70]
D/LeakCanary﹕ | mCurrentConfig = android.content.res.Configuration [id=0x12f97520]
D/LeakCanary﹕ | mDecor = null
D/LeakCanary﹕ | mDefaultKeySsb = null
D/LeakCanary﹕ | mEmbeddedID = null
D/LeakCanary﹕ | mEnterTransitionListener = android.app.SharedElementCallback$1 [id=0x70765ba8]
D/LeakCanary﹕ | mExitTransitionListener = android.app.SharedElementCallback$1 [id=0x70765ba8]
D/LeakCanary﹕ | mFragments = android.app.FragmentManagerImpl [id=0x12f7cef0]
D/LeakCanary﹕ | mHandler = android.os.Handler [id=0x1310ea60]
D/LeakCanary﹕ | mInstanceTracker = android.os.StrictMode$InstanceTracker [id=0x13052d90]
D/LeakCanary﹕ | mInstrumentation = android.app.Instrumentation [id=0x12c33f70]
D/LeakCanary﹕ | mIntent = android.content.Intent [id=0x12f3b300]
D/LeakCanary﹕ | mLastNonConfigurationInstances = null
D/LeakCanary﹕ | mLoaderManager = null
D/LeakCanary﹕ | mMainThread = android.app.ActivityThread [id=0x12c2b100]
D/LeakCanary﹕ | mManagedCursors = java.util.ArrayList [id=0x1310ea40]
D/LeakCanary﹕ | mManagedDialogs = null
D/LeakCanary﹕ | mMenuInflater = null
D/LeakCanary﹕ | mParent = null
D/LeakCanary﹕ | mResultData = null
D/LeakCanary﹕ | mSearchManager = null
D/LeakCanary﹕ | mTitle = java.lang.String [id=0x12e6d7e0]
D/LeakCanary﹕ | mToken = android.os.BinderProxy [id=0x12fe86a0]
D/LeakCanary﹕ | mTranslucentCallback = null
D/LeakCanary﹕ | mUiThread = java.lang.Thread [id=0x73b43540]
D/LeakCanary﹕ | mVoiceInteractor = null
D/LeakCanary﹕ | mWindow = com.android.internal.policy.impl.PhoneWindow [id=0x12e5d580]
D/LeakCanary﹕ | mWindowManager = android.view.WindowManagerImpl [id=0x1310ed20]
D/LeakCanary﹕ | mCalled = true
D/LeakCanary﹕ | mChangeCanvasToTranslucent = false
D/LeakCanary﹕ | mChangingConfigurations = false
D/LeakCanary﹕ | mCheckedForLoaderManager = true
D/LeakCanary﹕ | mConfigChangeFlags = 0
D/LeakCanary﹕ | mDefaultKeyMode = 0
D/LeakCanary﹕ | mDestroyed = true
D/LeakCanary﹕ | mDoReportFullyDrawn = false
D/LeakCanary﹕ | mEnableDefaultActionBarUp = false
D/LeakCanary﹕ | mFinished = true
D/LeakCanary﹕ | mIdent = 24993652
D/LeakCanary﹕ | mLoadersStarted = false
D/LeakCanary﹕ | mResultCode = 0
D/LeakCanary﹕ | mResumed = false
D/LeakCanary﹕ | mStartedActivity = false
D/LeakCanary﹕ | mStopped = true
D/LeakCanary﹕ | mTemporaryPause = false
D/LeakCanary﹕ | mTitleColor = 0
D/LeakCanary﹕ | mTitleReady = true
D/LeakCanary﹕ | mVisibleBehind = false
D/LeakCanary﹕ | mVisibleFromClient = true
D/LeakCanary﹕ | mVisibleFromServer = false
D/LeakCanary﹕ | mWindowAdded = true
D/LeakCanary﹕ | mInflater = com.android.internal.policy.impl.PhoneLayoutInflater [id=0x13152580]
D/LeakCanary﹕ | mOverrideConfiguration = null
D/LeakCanary﹕ | mResources = android.content.res.Resources [id=0x12c33f20]
D/LeakCanary﹕ | mTheme = android.content.res.Resources$Theme [id=0x1310ed40]
D/LeakCanary﹕ | mThemeResource = 2131689670
D/LeakCanary﹕ | mBase = android.app.ContextImpl [id=0x12c81100]
This is what the Android documentation recommends. Disable the leakcanary launcher activity by setting the leak_canary_add_launcher_icon resource boolean to false.
One should not use static views while developing the application, as static views are never destroyed. One should never use the Context as static, because that context will be available through the life of the application, and will not be restricted to the particular activity.
LeakCanary is a memory leak detection library for Android. LeakCanary's knowledge of the internals of the Android Framework gives it a unique ability to narrow down the cause of each leak, helping developers dramatically reduce Application Not Responding freezes and OutOfMemoryError crashes.
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.
From AndroidExcludedRefs.java
:
// TextLine.sCached is a pool of 3 TextLine instances. TextLine.recycle() has had at least two
// bugs that created memory leaks by not correctly clearing the recycled TextLine instances.
// The first was fixed in android-5.1.0_r1:
// https://github.com/android/platform_frameworks_base/commit
// /893d6fe48d37f71e683f722457bea646994a10bf
// The second was fixed, not released yet:
// https://github.com/android/platform_frameworks_base/commit
// /b3a9bc038d3a218b1dbdf7b5668e3d6c12be5ee4
// Hack: to fix this, you could access TextLine.sCached and clear the pool every now and then
// (e.g. on activity destroy).
TextLine.sCached
public static class Utils {
private static final Field TEXT_LINE_CACHED;
static {
Field textLineCached = null;
try {
textLineCached = Class.forName("android.text.TextLine").getDeclaredField("sCached");
textLineCached.setAccessible(true);
} catch (Exception ex) {
ex.printStackTrace();
}
TEXT_LINE_CACHED = textLineCached;
}
public static void clearTextLineCache() {
// If the field was not found for whatever reason just return.
if (TEXT_LINE_CACHED == null) return;
Object cached = null;
try {
// Get reference to the TextLine sCached array.
cached = TEXT_LINE_CACHED.get(null);
} catch (Exception ex) {
//
}
if (cached != null) {
// Clear the array.
for (int i = 0, size = Array.getLength(cached); i < size; i ++) {
Array.set(cached, i, null);
}
}
}
private Utils() {}
}
Call Utils.clearTextLineCache()
when needed.
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