我从LeakCanary显示以下内存泄漏,当我从启动画面跳到主动.我明白这是由于
Android操作系统本身的故障导致的预期泄漏,但是有没有办法避免这种情况(通过在某处设置某些TextView的细节)?
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]
解决方法
从
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).
步骤1:访问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() {} }
步骤2:清除游泳池
在需要时调用Utils.clearTextLineCache().