在Android开发中,OOM(内存泄露)已经是老生常谈的事情了,之前也有许多工具来检测内存泄露,比如比较常用的MAT,但是MAT使用步骤太麻烦了,近来发现了一个新工具,LeakCanary,它是一个用来检查 Android 下内存泄漏的开源库。
用法
首先集成 LeakCanary 库
dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
}
在 debug 版本上,集成 LeakCanary 库,并执行内存泄漏监测,而在 release 版本上,集成一个无操作的 wrapper ,这样对程序性能就不会有影响。
在Application 中
/**
* User: Picasso(380643397@qq.com)
* Date: 2015-10-20
* Time: 15:26
* FIXME
*/
public class BRApplication extends Application {
private RefWatcher mRefWatcher;
public static RefWatcher getRefWatcher(Context context) {
BRApplication application = (BRApplication) context.getApplicationContext();
return application.refWatcher;
}
private RefWatcher refWatcher;
@Override
public void onCreate() {
super.onCreate();
refWatcher = LeakCanary.install(this);
}
}
LeakCanary.install() 返回一个配置好了的 RefWatcher 实例。它同时安装了 ActivityRefWatcher 来监控 Activity 泄漏。即当 Activity.onDestroy() 被调用之后,如果这个 Activity 没有被销毁,logcat 就会打印出如下信息告诉你内存泄漏发生了。
eg. 我在一个项目中加入此工具,运行发现出现如下log
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ In com.picasso.beautyread:1.6:7.
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ * com.picasso.beautyread.DrawerLayoutActivity has leaked:
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ * GC ROOT static com.picasso.beautyread.custom.PullRefreshRecyclerView.exceptIv
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ * references android.widget.ImageView.mContext
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ * leaks com.picasso.beautyread.DrawerLayoutActivity instance
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ [ 10-20 16:09:24.860 22628:25692 D/LeakCanary ]
* Reference Key: 7e7ce215-94ea-422b-bd5f-bd0462cd33d1
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ * Device: HUAWEI Honor Che1-CL10 Che1-CL10
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ * Android Version: 4.4.4 API: 19 LeakCanary: 1.3.1
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ * Durations: watch=5021ms, gc=150ms, heap dump=711ms, analysis=22257ms
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ [ 10-20 16:09:24.860 22628:25692 D/LeakCanary ]
* Details:
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ * Class com.picasso.beautyread.custom.PullRefreshRecyclerView
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | static $staticOverhead = byte[] [id=0x42413d31;length=48;size=64]
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | static exceptIv = android.widget.ImageView [id=0x424efdf8]
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | static exceptTv = android.support.v7.widget.AppCompatTextView [id=0x424f09e0]
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ * Instance of android.widget.ImageView
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | static $staticOverhead = byte[] [id=0x4194d949;length=48;size=64]
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | static sS2FArray = android.graphics.Matrix$ScaleToFit[] [id=0x4183fa40;length=4]
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | static sScaleTypeArray = android.widget.ImageView$ScaleType[] [id=0x4182cfa8;length=8]
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mXfermode = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mUri = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mTempSrc = android.graphics.RectF [id=0x424f07a0]
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mTempDst = android.graphics.RectF [id=0x424f07c0]
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mState = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mColorFilter = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mScaleType = android.widget.ImageView$ScaleType [id=0x418369b8]
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mMatrix = android.graphics.Matrix [id=0x424f07e0]
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mDrawMatrix = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mDrawable = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mDrawableHeight = 0
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mDrawableWidth = 0
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mHaveFrame = false
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mLevel = 0
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mCropToPadding = false
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mMaxHeight = 2147483647
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mMaxWidth = 2147483647
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mMergeState = false
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mResource = 0
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mColorMod = false
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mBaselineAlignBottom = false
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mBaseline = -1
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mAlpha = 255
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mAdjustViewBoundsCompat = false
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mViewAlphaScale = 256
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mAdjustViewBounds = false
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mUnsetPressedState = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mAccessibilityDelegate = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mUnscaledDrawingCache = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mAnimator = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mAttachInfo = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mBackground = android.graphics.drawable.BitmapDrawable [id=0x424f0710]
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mTransformationInfo = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mTouchDelegate = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mTag = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mSendViewStateChangedAccessibilityEvent = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mClipBounds = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mContentDescription = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mContext = com.picasso.beautyread.DrawerLayoutActivity [id=0x42183e18]
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mCurrentAnimation = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mDisplayList = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mDrawableState = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mDrawingCache = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mSendViewScrolledAccessibilityEvent = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mFloatingTreeObserver = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mScrollCache = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mHardwareLayer = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mResources = com.huawei.android.content.res.ResourcesEx [id=0x41a4c658]
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mPerformClick = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mInputEventConsistencyVerifier = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mKeyedTags = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mPendingCheckForTap = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mPendingCheckForLongPress = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mLayerPaint = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mParent = android.widget.RelativeLayout [id=0x424ef950]
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mLayoutInsets = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mLayoutParams = android.widget.RelativeLayout$LayoutParams [id=0x424f0818]
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mOverlay = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mMeasureCache = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mListenerInfo = android.view.View$ListenerInfo [id=0x424f0958]
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mLocalDirtyRect = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mMatchIdPredicate = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mMatchLabelForPredicate = null
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mLeftPaddingDefined = false
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mMeasuredHeight = 0
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mMeasuredWidth = 0
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mMinHeight = 0
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mMinWidth = 0
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mNextFocusDownId = -1
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mNextFocusForwardId = -1
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mNextFocusLeftId = -1
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mNextFocusRightId = -1
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mNextFocusUpId = -1
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mOldHeightMeasureSpec = -2147483648
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mOldWidthMeasureSpec = -2147483648
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mOverScrollMode = 1
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mLeft = 0
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mPaddingBottom = 0
10-20 16:09:24.860 22628-25692/com.picasso.beautyread D/LeakCanary﹕ | mPaddingLeft = 0
从log中很容易看出来,问题出在文件PullRefreshRecyclerView的对象exceptIv,一看源代码发现:
/**
* 异常图片控件
*/
private static ImageView exceptIv;
定义的时候多写了个static,但是这里根本不需要使用static,把static去掉运行,OK~~
使用非常方便!
参考博客
http://www.jianshu.com/p/0049e9b344b0
https://github.com/square/leakcanary