Android学习笔记二 LeakCannary使用介绍(Android 8.0)

LeakCanary的使用介绍

  LeakCanary是一个开源的内存泄露检测的项目,可以到github上查看。

1. 引入LeakCanary

  在build.gradle中添加依赖

dependencies {
     // leakcanary
    debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.1'
    releaseImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.1'
    testImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1'
}
2. 使用方式一:监控activity泄露(默认)

  在Application中初始化LeakCanary,并且将其作为android:name注册到 AndroidManifest.xml 的 Application 节点下。在Activity Destroy的时候,LeakCanary会自动监测是否有内存泄露(可以看源码)。

public class LeakTestApp extends Application {

    @Override public void onCreate() {
        super.onCreate();
        if (LeakCanary.isInAnalyzerProcess(this)) {//1
            // This process is dedicated to LeakCanary for heap analysis.
            // You should not init your app in this process.
            return;
        }
        LeakCanary.install(this);
    }

}

  下面这段代码中,AsyncTask是一个匿名的内部类,隐式的持有外部类(Activity)的引用,在AsncTask中用sleep 20秒模拟了一个耗时操作。当点击Activity中的按钮后立刻退出,Activity执行了onDestroy方法,但是任务还是没有结束,于是Activity得不到释放。而Activity又指向一个Window,Window又拥有整个View继承树,算下来是一大段的内存空间。

public class LeakActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.e("leakcanary test", "leak activity has started.");
        setContentView(R.layout.activity_leak);
        TextView mview1 = (TextView)findViewById(R.id.viewtest1);
        mview1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startAsyncTask();
            }
        });

    }

    void startAsyncTask() {
        // This async task is an anonymous class and therefore has a hidden reference to the outer
        // class MainActivity. If the activity gets destroyed before the task finishes (e.g. rotation),
        // the activity instance will leak.
        new AsyncTask<Void, Void, Void>() {
            @Override protected Void doInBackground(Void... params) {
                // Do some slow work in background
                SystemClock.sleep(20000);
                return null;
            }
        }.execute();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e("leakcanary test","activity leak has been desdroyed");
    }
}

3. 使用方式二:监控Fragment泄露

  在Application中初始化时需要返回refwatcher,在需要监测Fragment回收的地方,用refwatcher完成监测

public class LeakTestApp extends Application {
    public static RefWatcher refWatcher;

    @Override
    public void onCreate() {
        super.onCreate();
        refWatcher = LeakCanary.install(this);
    }
}
public abstract class BaseFragment extends Fragment {
    @Override 
    public void onDestroy() {
        super.onDestroy();
        RefWatcher refWatcher = LeakTestApp.refWatcher;
        refWatcher.watch(this);
    }
}
4. 测试结果

  参考其他资料说可以在通知栏显示,但是在实际测试过程中,加入了内存的读写权限,也没有在通知栏弹出显示,后续再继续看一下这个地方。但是在Log中可以看到如下打印,完整显示了leak trace

LeakCanary: In com.example.zxp.myapplication:1.0:1.
LeakCanary: * android.arch.lifecycle.ReportFragment has leaked:
LeakCanary: * thread Thread.!(<Java Local>)! (named 'AsyncTask #1')
LeakCanary: * ↳ LeakActivity$1.!(this$0)! (anonymous subclass of android.os.AsyncTask)
LeakCanary: * ↳ LeakActivity.mFragments
LeakCanary: * ↳ FragmentController.mHost
LeakCanary: * ↳ Activity$HostCallbacks.mFragmentManager
LeakCanary: * ↳ FragmentManagerImpl.mAdded
LeakCanary: * ↳ ArrayList.elementData
LeakCanary: * ↳ array Object[].[0]
LeakCanary: * ↳ ReportFragment
LeakCanary: 
LeakCanary: * Reference Key: 265bd213-c1bd-495a-b3aa-8ddd52fa5e3d
LeakCanary: * Device: hi VU HAT4KDTV ls
LeakCanary: * Android Version: 8.0.0 API: 26 LeakCanary: 1.6.1 26145bf
LeakCanary: * Durations: watch=5019ms, gc=145ms, heap dump=2976ms, analysis=17720ms
LeakCanary: 
LeakCanary: * Details:
LeakCanary: * Instance of java.lang.Thread
LeakCanary: |   static NANOS_PER_MILLI = 1000000
LeakCanary: |   static uncaughtExceptionPreHandler = com.android.internal.os.RuntimeInit$LoggingHandler@316723512 (0x12e0d138)
LeakCanary: |   static EMPTY_STACK_TRACE = java.lang.StackTraceElement[0]@1865076336 (0x6f2ace70)
LeakCanary: |   static defaultUncaughtExceptionHandler = com.android.internal.os.RuntimeInit$KillApplicationHandler@316723504 (0x12e0d130)
LeakCanary: |   static MIN_PRIORITY = 1
LeakCanary: |   static MAX_PRIORITY = 10
LeakCanary: |   static NORM_PRIORITY = 5
LeakCanary: |   static SUBCLASS_IMPLEMENTATION_PERMISSION = java.lang.RuntimePermission@1865076352 (0x6f2ace80)
LeakCanary: |   static $classOverhead = byte[232]@1865078929 (0x6f2ad891)
LeakCanary: |   static threadInitNumber = 2
LeakCanary: |   static threadSeqNumber = 317
LeakCanary: |   blocker = null
LeakCanary: |   blockerLock = java.lang.Object@317577544 (0x12edd948)
LeakCanary: |   contextClassLoader = dalvik.system.PathClassLoader@316725784 (0x12e0da18)
LeakCanary: |   daemon = false
LeakCanary: |   eetop = 0
LeakCanary: |   group = java.lang.ThreadGroup@1864352488 (0x6f1fc2e8)
LeakCanary: |   inheritableThreadLocals = null
LeakCanary: |   inheritedAccessControlContext = java.security.AccessControlContext@317577552 (0x12edd950)
LeakCanary: |   lock = java.lang.Object@316672544 (0x12e00a20)
LeakCanary: |   name = "AsyncTask #1"
LeakCanary: |   nativeParkEventPointer = 0
LeakCanary: |   nativePeer = 2517719040
LeakCanary: |   parkBlocker = null
LeakCanary: |   parkState = 1
LeakCanary: |   priority = 5
LeakCanary: |   single_step = false
LeakCanary: |   stackSize = 0
LeakCanary: |   started = true
LeakCanary: |   stillborn = false
LeakCanary: |   target = java.util.concurrent.ThreadPoolExecutor$Worker@316672976 (0x12e00bd0)
LeakCanary: |   threadLocalRandomProbe = 0
LeakCanary: |   threadLocalRandomSecondarySeed = 0
LeakCanary: |   threadLocalRandomSeed = 0
LeakCanary: |   threadLocals = null
LeakCanary: |   threadQ = null
LeakCanary: |   threadStatus = 0
LeakCanary: |   tid = 316
LeakCanary: |   uncaughtExceptionHandler = null
LeakCanary: |   shadow$_klass_ = java.lang.Thread
LeakCanary: |   shadow$_monitor_ = 0
LeakCanary: * Instance of com.example.zxp.myapplication.LeakActivity$1
LeakCanary: |   static $classOverhead = byte[112]@316672553 (0x12e00a29)
LeakCanary: |   this$0 = com.example.zxp.myapplication.LeakActivity@317509920 (0x12ecd120)
LeakCanary: |   mCancelled = java.util.concurrent.atomic.AtomicBoolean@317509888 (0x12ecd100)
LeakCanary: |   mFuture = android.os.AsyncTask$3@316672856 (0x12e00b58)
LeakCanary: |   mHandler = android.os.AsyncTask$InternalHandler@317501680 (0x12ecb0f0)
LeakCanary: |   mStatus = android.os.AsyncTask$Status@1864392000 (0x6f205d40)
LeakCanary: |   mTaskInvoked = java.util.concurrent.atomic.AtomicBoolean@317509904 (0x12ecd110)
LeakCanary: |   mWorker = android.os.AsyncTask$2@316672840 (0x12e00b48)
LeakCanary: |   shadow$_klass_ = com.example.zxp.myapplication.LeakActivity$1
LeakCanary: |   shadow$_monitor_ = 0
LeakCanary: * Instance of com.example.zxp.myapplication.LeakActivity
LeakCanary: |   static $classOverhead = byte[2100]@316993449 (0x12e4efa9)
LeakCanary: |   mDelegate = android.support.v7.app.AppCompatDelegateImpl@317510192 (0x12ecd230)
LeakCanary: |   mResources = null
LeakCanary: |   mThemeId = 2131492869
LeakCanary: |   mCreated = true
LeakCanary: |   mFragments = android.support.v4.app.FragmentController@317510328 (0x12ecd2b8)
LeakCanary: |   mHandler = android.support.v4.app.FragmentActivity$1@317510344 (0x12ecd2c8)
LeakCanary: |   mNextCandidateRequestIndex = 0
LeakCanary: |   mPendingFragmentActivityResults = android.support.v4.util.SparseArrayCompat@317510376 (0x12ecd2e8)
LeakCanary: |   mRequestedPermissionsFromFragment = false
LeakCanary: |   mResumed = false
LeakCanary: |   mStartedActivityFromFragment = false
LeakCanary: |   mStartedIntentSenderFromFragment = false
LeakCanary: |   mStopped = true
LeakCanary: |   mViewModelStore = null
LeakCanary: |   mExtraDataMap = android.support.v4.util.SimpleArrayMap@317510400 (0x12ecd300)
LeakCanary: |   mLifecycleRegistry = android.arch.lifecycle.LifecycleRegistry@317510424 (0x12ecd318)
LeakCanary: |   mActionBar = null
LeakCanary: |   mActionModeTypeStarting = 0
LeakCanary: |   mActivityInfo = android.content.pm.ActivityInfo@317510456 (0x12ecd338)
LeakCanary: |   mActivityTransitionState = android.app.ActivityTransitionState@317510608 (0x12ecd3d0)
LeakCanary: |   mApplication = com.example.zxp.myapplication.LeakTestApp@316725528 (0x12e0d918)
LeakCanary: |   mAutoFillResetNeeded = false
LeakCanary: |   mAutofillManager = null
LeakCanary: |   mAutofillPopupWindow = null
LeakCanary: |   mCalled = true
LeakCanary: |   mChangeCanvasToTranslucent = false
LeakCanary: |   mChangingConfigurations = false
LeakCanary: |   mComponent = android.content.ComponentName@317510664 (0x12ecd408)
LeakCanary: |   mConfigChangeFlags = 0
LeakCanary: |   mCurrentConfig = android.content.res.Configuration@317510680 (0x12ecd418)
LeakCanary: |   mDecor = null
LeakCanary: |   mDefaultKeyMode = 0
LeakCanary: |   mDefaultKeySsb = null
LeakCanary: |   mDestroyed = true
LeakCanary: |   mDoReportFullyDrawn = false
LeakCanary: |   mEmbeddedID = null
LeakCanary: |   mEnableDefaultActionBarUp = false
LeakCanary: |   mEnterTransitionListener = android.app.SharedElementCallback$1@1864376848 (0x6f202210)
LeakCanary: |   mExitTransitionListener = android.app.SharedElementCallback$1@1864376848 (0x6f202210)
LeakCanary: |   mFinished = true
LeakCanary: |   mFragments = android.app.FragmentController@317510792 (0x12ecd488)
LeakCanary: |   mHandler = android.os.Handler@317510808 (0x12ecd498)
LeakCanary: |   mHasCurrentPermissionsRequest = false
LeakCanary: |   mIdent = 231889398
LeakCanary: |   mInstanceTracker = android.os.StrictMode$InstanceTracker@317510840 (0x12ecd4b8)
LeakCanary: |   mInstrumentation = android.app.Instrumentation@317510856 (0x12ecd4c8)
LeakCanary: |   mIntent = android.content.Intent@317510928 (0x12ecd510)
LeakCanary: |   mLastAutofillId = 1073741823
LeakCanary: |   mLastNonConfigurationInstances = null
LeakCanary: |   mMainThread = android.app.ActivityThread@316670208 (0x12e00100)
LeakCanary: |   mManagedCursors = java.util.ArrayList@317510992 (0x12ecd550)
LeakCanary: |   mManagedDialogs = null
LeakCanary: |   mMenuInflater = null
LeakCanary: |   mParent = null
LeakCanary: |   mReferrer = "com.example.zxp.myapplication"
LeakCanary: |   mResultCode = 0
LeakCanary: |   mResultData = null
LeakCanary: |   mResumed = false
LeakCanary: |   mSearchEvent = null
LeakCanary: |   mSearchManager = null
LeakCanary: |   mStartedActivity = false
LeakCanary: |   mStopped = true
LeakCanary: |   mTaskDescription = android.app.ActivityManager$TaskDescription@317511072 (0x12ecd5a0)
LeakCanary: |   mTemporaryPause = false
LeakCanary: |   mTitle = "My Application"
LeakCanary: |   mTitleColor = 0
LeakCanary: |   mTitleReady = true
LeakCanary: |   mToken = android.os.BinderProxy@317511112 (0x12ecd5c8)
LeakCanary: |   mTranslucentCallback = null
LeakCanary: |   mUiThread = java.lang.Thread@1912332288 (0x71fbe000)
LeakCanary: |   mVisibleBehind = false
LeakCanary: |   mVisibleFromClient = true
LeakCanary: |   mVisibleFromServer = true
LeakCanary: |   mVoiceInteractor = null
LeakCanary: |   mWindow = com.android.internal.policy.PhoneWindow@317511144 (0x12ecd5e8)
LeakCanary: |   mWindowAdded = true
LeakCanary: |   mWindowManager = android.view.WindowManagerImpl@317511512 (0x12ecd758)
LeakCanary: |   mInflater = com.android.internal.policy.PhoneLayoutInflater@317511536 (0x12ecd770)
LeakCanary: |   mOverrideConfiguration = null
LeakCanary: |   mResources = android.content.res.Resources@317511584 (0x12ecd7a0)
LeakCanary: |   mTheme = android.content.res.Resources$Theme@317511624 (0x12ecd7c8)
LeakCanary: |   mThemeResource = 2131492869
LeakCanary: |   mBase = android.app.ContextImpl@317511640 (0x12ecd7d8)
LeakCanary: |   shadow$_klass_ = com.example.zxp.myapplication.LeakActivity
LeakCanary: |   shadow$_monitor_ = 1245427187
LeakCanary: * Instance of android.app.FragmentController
LeakCanary: |   static $classOverhead = byte[208]@1865906065 (0x6f377791)
LeakCanary: |   mHost = android.app.Activity$HostCallbacks@317573352 (0x12edc8e8)
LeakCanary: |   shadow$_klass_ = android.app.FragmentController
LeakCanary: |   shadow$_monitor_ = 0
LeakCanary: * Instance of android.app.Activity$HostCallbacks
LeakCanary: |   static $classOverhead = byte[184]@1865460793 (0x6f30ac39)
LeakCanary: |   this$0 = com.example.zxp.myapplication.LeakActivity@317509920 (0x12ecd120)
LeakCanary: |   mActivity = com.example.zxp.myapplication.LeakActivity@317509920 (0x12ecd120)
LeakCanary: |   mAllLoaderManagers = android.util.ArrayMap@317573400 (0x12edc918)
LeakCanary: |   mCheckedForLoaderManager = true
LeakCanary: |   mContext = com.example.zxp.myapplication.LeakActivity@317509920 (0x12ecd120)
LeakCanary: |   mFragmentManager = android.app.FragmentManagerImpl@317573432 (0x12edc938)
LeakCanary: |   mHandler = android.os.Handler@317510808 (0x12ecd498)
LeakCanary: |   mLoaderManager = null
LeakCanary: |   mLoadersStarted = true
LeakCanary: |   mRetainLoaders = false
LeakCanary: |   mWindowAnimations = 0
LeakCanary: |   shadow$_klass_ = android.app.Activity$HostCallbacks
LeakCanary: |   shadow$_monitor_ = 0
LeakCanary: * Instance of android.app.FragmentManagerImpl
LeakCanary: |   static USER_VISIBLE_HINT_TAG = "android:user_visible_hint"
LeakCanary: |   static TAG = "FragmentManager"
LeakCanary: |   static TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state"
LeakCanary: |   static DEBUG = false
LeakCanary: |   static VIEW_STATE_TAG = "android:view_state"
LeakCanary: |   static TARGET_STATE_TAG = "android:target_state"
LeakCanary: |   static $classOverhead = byte[481]@1865124441 (0x6f2b8a59)
LeakCanary: |   mActive = android.util.SparseArray@317573544 (0x12edc9a8)
LeakCanary: |   mAdded = java.util.ArrayList@317573568 (0x12edc9c0)
LeakCanary: |   mAllowOldReentrantBehavior = false
LeakCanary: |   mAvailBackStackIndices = null
LeakCanary: |   mBackStack = null
LeakCanary: |   mBackStackChangeListeners = null
LeakCanary: |   mBackStackIndices = null
LeakCanary: |   mContainer = null
LeakCanary: |   mCreatedMenus = null
LeakCanary: |   mCurState = 0
LeakCanary: |   mDestroyed = true
LeakCanary: |   mExecCommit = android.app.FragmentManagerImpl$1@317573592 (0x12edc9d8)
LeakCanary: |   mExecutingActions = false
LeakCanary: |   mHavePendingDeferredStart = false
LeakCanary: |   mHost = null
LeakCanary: |   mLifecycleCallbacks = java.util.concurrent.CopyOnWriteArrayList@317573608 (0x12edc9e8)
LeakCanary: |   mNeedMenuInvalidate = false
LeakCanary: |   mNextFragmentIndex = 1
LeakCanary: |   mNoTransactionsBecause = null
LeakCanary: |   mParent = null
LeakCanary: |   mPendingActions = java.util.ArrayList@317573624 (0x12edc9f8)
LeakCanary: |   mPostponedTransactions = null
LeakCanary: |   mPrimaryNav = null
LeakCanary: |   mSavedNonConfig = null
LeakCanary: |   mStateArray = null
LeakCanary: |   mStateBundle = null
LeakCanary: |   mStateSaved = false
LeakCanary: |   mTmpAddedFragments = java.util.ArrayList@317573648 (0x12edca10)
LeakCanary: |   mTmpIsPop = java.util.ArrayList@317573672 (0x12edca28)
LeakCanary: |   mTmpRecords = java.util.ArrayList@317573696 (0x12edca40)
LeakCanary: |   shadow$_klass_ = android.app.FragmentManagerImpl
LeakCanary: |   shadow$_monitor_ = 0
LeakCanary: * Instance of java.util.ArrayList
LeakCanary: |   static MAX_ARRAY_SIZE = 2147483639
LeakCanary: |   static serialVersionUID = 8683452581122892189
LeakCanary: |   static DEFAULTCAPACITY_EMPTY_ELEMENTDATA = java.lang.Object[0]@1864791560 (0x6f267608)
LeakCanary: |   static EMPTY_ELEMENTDATA = java.lang.Object[0]@1864980896 (0x6f2959a0)
LeakCanary: |   static $classOverhead = byte[208]@1865178297 (0x6f2c5cb9)
LeakCanary: |   static DEFAULT_CAPACITY = 10
LeakCanary: |   elementData = java.lang.Object[10]@317573984 (0x12edcb60)
LeakCanary: |   size = 1
LeakCanary: |   modCount = 1
LeakCanary: |   shadow$_klass_ = java.util.ArrayList
LeakCanary: |   shadow$_monitor_ = 0
LeakCanary: * Array of java.lang.Object[]
LeakCanary: |   [0] = android.arch.lifecycle.ReportFragment@317574040 (0x12edcb98)
LeakCanary: |   [1] = null
LeakCanary: |   [2] = null
LeakCanary: |   [3] = null
LeakCanary: |   [4] = null
LeakCanary: |   [5] = null
LeakCanary: |   [6] = null
LeakCanary: |   [7] = null
LeakCanary: |   [8] = null
LeakCanary: |   [9] = null
LeakCanary: * Instance of android.arch.lifecycle.ReportFragment
LeakCanary: |   static REPORT_FRAGMENT_TAG = "android.arch.lifecycle.LifecycleDispatcher.report_fragment_tag"
LeakCanary: |   static $classOverhead = byte[668]@316777905 (0x12e1a5b1)
LeakCanary: |   mProcessListener = null
LeakCanary: |   mAdded = false
LeakCanary: |   mAnimationInfo = null
LeakCanary: |   mArguments = null
LeakCanary: |   mBackStackNesting = 0
LeakCanary: |   mCalled = true
LeakCanary: |   mCheckedForLoaderManager = false
LeakCanary: |   mChildFragmentManager = null
LeakCanary: |   mChildNonConfig = null
LeakCanary: |   mContainer = null
LeakCanary: |   mContainerId = 0
LeakCanary: |   mDeferStart = false
LeakCanary: |   mDetached = false
LeakCanary: |   mFragmentId = 0
LeakCanary: |   mFragmentManager = null
LeakCanary: |   mFromLayout = false
LeakCanary: |   mHasMenu = false
LeakCanary: |   mHidden = false
LeakCanary: |   mHiddenChanged = false
LeakCanary: |   mHost = null
LeakCanary: |   mInLayout = false
LeakCanary: |   mIndex = -1
LeakCanary: |   mIsNewlyAdded = false
LeakCanary: |   mLayoutInflater = null
LeakCanary: |   mLoaderManager = null
LeakCanary: |   mLoadersStarted = false
LeakCanary: |   mMenuVisible = true
LeakCanary: |   mParentFragment = null
LeakCanary: |   mPerformedCreateView = false
LeakCanary: |   mRemoving = false
LeakCanary: |   mRestored = false
LeakCanary: |   mRetainInstance = false
LeakCanary: |   mRetaining = false
LeakCanary: |   mSavedFragmentState = null
LeakCanary: |   mSavedViewState = null
LeakCanary: |   mState = 0
LeakCanary: |   mTag = null
LeakCanary: |   mTarget = null
LeakCanary: |   mTargetIndex = -1
LeakCanary: |   mTargetRequestCode = 0
LeakCanary: |   mUserVisibleHint = true
LeakCanary: |   mView = null
LeakCanary: |   mWho = null
LeakCanary: |   shadow$_klass_ = android.arch.lifecycle.ReportFragment
LeakCanary: |   shadow$_monitor_ = 0
LeakCanary: * Excluded Refs:
LeakCanary: | Field: android.os.Message.obj
LeakCanary: | Field: android.os.Message.next
LeakCanary: | Field: android.os.Message.target
LeakCanary: | Field: android.view.inputmethod.InputMethodManager.mNextServedView
LeakCanary: | Field: android.view.inputmethod.InputMethodManager.mServedView
LeakCanary: | Field: android.view.inputmethod.InputMethodManager.mServedInputConnection
LeakCanary: | Field: android.view.inputmethod.InputMethodManager.mCurRootView
LeakCanary: | Field: android.accounts.AccountManager$AmsTask$Response.this$1
LeakCanary: | Field: android.view.accessibility.AccessibilityNodeInfo.mOriginalText
LeakCanary: | Field: com.android.internal.policy.BackdropFrameRenderer.mDecorView
LeakCanary: | Field: android.view.Choreographer$FrameDisplayEventReceiver.mMessageQueue (always)
LeakCanary: | Thread:FinalizerWatchdogDaemon (always)
LeakCanary: | Thread:main (always)
LeakCanary: | Thread:LeakCanary-Heap-Dump (always)
LeakCanary: | Class:java.lang.ref.WeakReference (always)
LeakCanary: | Class:java.lang.ref.SoftReference (always)
LeakCanary: | Class:java.lang.ref.PhantomReference (always)
LeakCanary: | Class:java.lang.ref.Finalizer (always)
LeakCanary: | Class:java.lang.ref.FinalizerReference (always)

猜你喜欢

转载自blog.csdn.net/qsdzxp/article/details/82499259