2. 初始化 LeakCanary
- apply plugin: 'com.android.application'
- android {
- ... ...
- }
- dependencies {
- ... ...
- //添加leakcanary相关的依赖
- //在release和test版本中,使用的是LeakCanary的no-op版本,也就是没有实际代码和操作的Wrapper版本,只包含LeakCanary和RefWatcher类的空实现,这样不会对生成的APK包体积和应用性能造成影响
- debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
- releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
- testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
- ... ...
- compile project(':test')
- }
OK,到这里我们就完成了一个项目的 LeankCanary 的简单接入;
- public class QAplication extends Application{
- @Override
- public void onCreate() {
- super.onCreate();
- ... ...
- //初始化LeakCanary
- if (LeakCanary.isInAnalyzerProcess(this)) {
- return;
- }
- LeakCanary.install(this);
- }
- }
test/src/main/com/qproject/test/leakcanary/LeakCanaryActivity.java
- public class TestManager {
- //单例静态特性使得单例的生命周期和应用的生命周期一样长
- private static TestManager instance;
- private Context context;
- /**
- * 传入的Context的生命周期很重要:
- * 如果传入的是Application的Context,则生命周期和单例生命周期一样长;
- * 如果传入的是Activity的Context,由于该Context和Activity的生命周期一样长,当Activity退出的时候它的内存不会被回收,因为单例对象持有它的引用;
- */
- private TestManager(Context context) {
- this.context = context;
- }
- public static TestManager getInstance(Context context) {
- if (instance == null) {
- instance = new TestManager(context);
- }
- return instance;
- }
- }
2. 检测消息通知
- public class LeakCanaryActivity extends AppCompatActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_leakcanary);
- //获取单例对象,退出Activity即可模拟出内存泄露
- TestManager testManager = TestManager.getInstance(this);
- }
- }
5. 获取 dump 日志文件
- //内存泄露对象com.qproject.test.leakcanary.LeakCanaryActivity
- 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * com.qproject.test.leakcanary.LeakCanaryActivity has leaked:
- //static com.qproject.test.TestManager.instance的com.qproject.test.TestManager.context引用了回收的内存对象
- 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * GC ROOT static com.qproject.test.TestManager.instance
- 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * references com.qproject.test.TestManager.context
- //内存泄露对象大小,Reference Key,Device和Android Version等信息
- 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * leaks com.qproject.test.leakcanary.LeakCanaryActivity instance
- 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Retaining: 46 KB.
- 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Reference Key: 3d74d294-70dc-4447-a9a2-64e656ea86b8
- 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Device: Genymotion Android PREVIEW - Google Nexus 5X - 7.0.0 - API 24 - 1080x1920 vbox86p
- 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Android Version: 7.0 API: 24 LeakCanary: 1.5 00f37f5
- 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Durations: watch=5038ms, gc=137ms, heap dump=2390ms, analysis=27325ms
- //内存泄露对象详细信息
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: * Details:
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: * Class com.qproject.test.TestManager
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | static instance = com.qproject.test.TestManager@316184816 (0x12d898f0)
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | static $classOverhead = byte[308]@316175745 (0x12d87581)
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: * Instance of com.qproject.test.TestManager
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | static instance = com.qproject.test.TestManager@316184816 (0x12d898f0)
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | static $classOverhead = byte[308]@316175745 (0x12d87581)
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | context = com.qproject.test.leakcanary.LeakCanaryActivity@315059712 (0x12c76e00)
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | shadow$_klass_ = com.qproject.test.TestManager
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | shadow$_monitor_ = 0
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: * Instance of com.qproject.test.leakcanary.LeakCanaryActivity
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | static $classOverhead = byte[2228]@316203009 (0x12d8e001)
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | mDelegate = android.support.v7.app.AppCompatDelegateImplN@315842128 (0x12d35e50)
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | mEatKeyUpEvent = false
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | mResources = null
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | mThemeId = 2131230884
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | mCreated = true
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | mFragments = android.support.v4.app.FragmentController@316183584 (0x12d89420)
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | mHandler = android.support.v4.app.FragmentActivity$1@316163360 (0x12d84520)
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | mNextCandidateRequestIndex = 0
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | mOptionsMenuInvalidated = false
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | mPendingFragmentActivityResults = android.support.v4.util.SparseArrayCompat@316172368 (0x12d86850)
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | mReallyStopped = true
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | mRequestedPermissionsFromFragment = false
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | mResumed = false
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | mRetaining = false
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | mStopped = true
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | mStartedActivityFromFragment = false
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | mStartedIntentSenderFromFragment = false
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | mExtraDataMap = android.support.v4.util.SimpleArrayMap@316171864 (0x12d86658)
- 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: | mActionBar = null
- 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: | mActionModeTypeStarting = 0
- 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: | mActivityInfo = android.content.pm.ActivityInfo@315841984 (0x12d35dc0)
- 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: | mActivityTransitionState = android.app.ActivityTransitionState@316207336 (0x12d8f0e8)
- 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: | mApplication = com.qproject.main.QAplication@314916416 (0x12c53e40)
- 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: | mCalled = true
- 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: | mChangeCanvasToTranslucent = false
- 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: | mChangingConfigurations = false
- 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: | mComponent = android.content.ComponentName@315998320 (0x12d5c070)
- 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: | mConfigChangeFlags = 0
- 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: | mCurrentConfig = android.content.res.Configuration@316178888 (0x12d881c8)
- 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: | mDecor = null
- 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: | mDefaultKeyMode = 0
- 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: | mDefaultKeySsb = null
- 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: | mDestroyed = true
- 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: | mDoReportFullyDrawn = false
- 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: | mEatKeyUpEvent = false
- 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: | mEmbeddedID = null
- 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: | mEnableDefaultActionBarUp = false
- 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: | mEnterTransitionListener = android.app.SharedElementCallback$1@1887062680 (0x707a4a98)
- 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: | mExitTransitionListener = android.app.SharedElementCallback$1@1887062680 (0x707a4a98)
- 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: | mFinished = true
- 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: | mFragments = android.app.FragmentController@316183536 (0x12d893f0)
- 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: | mHandler = android.os.Handler@316163296 (0x12d844e0)
- 12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: | mHasCurrentPermissionsRequest = false
- 12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: | mIdent = 20356640
- 12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: | mInstanceTracker = android.os.StrictMode$InstanceTracker@316183552 (0x12d89400)
- 12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: | mInstrumentation = android.app.Instrumentation@314950632 (0x12c5c3e8)
- 12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: | mIntent = android.content.Intent@316215416 (0x12d91078)
- 12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: | mLastNonConfigurationInstances = null
- 12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: | mMainThread = android.app.ActivityThread@314966272 (0x12c60100)
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mManagedCursors = java.util.ArrayList@316171816 (0x12d86628)
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mManagedDialogs = null
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mMenuInflater = null
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mParent = null
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mReferrer = java.lang.String@316215864 (0x12d91238)
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mResultCode = 0
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mResultData = null
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mResumed = false
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mSearchEvent = null
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mSearchManager = null
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mStartedActivity = false
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mStopped = true
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mTaskDescription = android.app.ActivityManager$TaskDescription@316163328 (0x12d84500)
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mTemporaryPause = false
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mTitle = java.lang.String@315129824 (0x12c87fe0)
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mTitleColor = 0
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mTitleReady = true
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mToken = android.os.BinderProxy@315867328 (0x12d3c0c0)
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mTranslucentCallback = null
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mUiThread = java.lang.Thread@1959751680 (0x74cf7000)
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mVisibleBehind = false
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mVisibleFromClient = true
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mVisibleFromServer = true
- 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: | mVoiceInteractor = null
- 12-25 07:50:51.716 4941-5795/com.qproject.main D/LeakCanary: | mWindow = com.android.internal.policy.PhoneWindow@315116864 (0x12c84d40)
- 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | mWindowAdded = true
- 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | mWindowManager = android.view.WindowManagerImpl@316172152 (0x12d86778)
- 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | mInflater = com.android.internal.policy.PhoneLayoutInflater@316010352 (0x12d5ef70)
- 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | mOverrideConfiguration = null
- 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | mResources = android.content.res.Resources@316235992 (0x12d960d8)
- 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | mTheme = android.content.res.Resources$Theme@316183728 (0x12d894b0)
- 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | mThemeResource = 2131230884
- 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | mBase = android.app.ContextImpl@316155392 (0x12d82600)
- 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | shadow$_klass_ = com.qproject.test.leakcanary.LeakCanaryActivity
- 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | shadow$_monitor_ = 1316364430
- 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: * Excluded Refs:
- 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Field: android.view.Choreographer$FrameDisplayEventReceiver.mMessageQueue (always)
- 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Thread:FinalizerWatchdogDaemon (always)
- 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Thread:main (always)
- 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Thread:LeakCanary-Heap-Dump (always)
- 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.WeakReference (always)
- 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.SoftReference (always)
- 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.PhantomReference (always)
- 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.Finalizer (always)
- 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.FinalizerReference (always)
Android Studio->View->Tool Windows->Captures,打开 Captures 窗口,将 pull 获取的 hprof 文件剪切到 Capture 中文件的目录下,双击打开即可;
- vbox86p:/data/data/com.qproject.main/files/leakcanary # ls
- 2016-12-25_07-50-51_718.hprof 2016-12-25_07-50-51_718.hprof.result
- D:\>adb pull ./data/data/com.qproject.main/files/leakcanary/2016-12-25_07-50-51_ 718.hprof
- [100%] ./data/data/com.qproject.main/f...akcanary/2016-12-25_07-50-51_718.hprof
来源: