@JvmOverloadsfunmanualInstall(application:Application,retainedDelayMillis:Long=TimeUnit.SECONDS.toMillis(5),watchersToInstall:List<InstallableWatcher>=appDefaultWatchers(application)){//检查是否是主线程checkMainThread()//如果已经安装过抛出异常if(isInstalled){throwIllegalStateException("AppWatcher already installed, see exception cause for prior install call",installCause)}check(retainedDelayMillis>=0){"retainedDelayMillis $retainedDelayMillis must be at least 0 ms"}installCause=RuntimeException("manualInstall() first called here")this.retainedDelayMillis=retainedDelayMillisif(application.isDebuggableBuild){LogcatSharkLog.install()}// Requires AppWatcher.objectWatcher to be set//获取LeakCanaryDelegate.loadLeakCanary(application)//遍历并调用install方法watchersToInstall.forEach{it.install()}}
overridefuninvoke(application:Application){_application=applicationcheckRunningInDebuggableBuild()//添加listenerAppWatcher.objectWatcher.addOnObjectRetainedListener(this)//创建AndroidHeapDumpervalheapDumper=AndroidHeapDumper(application,createLeakDirectoryProvider(application))//获取GcTiggervalgcTrigger=GcTrigger.DefaultvalconfigProvider={LeakCanary.config}valhandlerThread=HandlerThread(LEAK_CANARY_THREAD_NAME)handlerThread.start()valbackgroundHandler=Handler(handlerThread.looper)//创建HeapDumpTriggerheapDumpTrigger=HeapDumpTrigger(application,backgroundHandler,AppWatcher.objectWatcher,gcTrigger,heapDumper,configProvider)//application注册可见监听器application.registerVisibilityListener{applicationVisible->this.applicationVisible=applicationVisibleheapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)}registerResumedActivityListener(application)addDynamicShortcut(application)// We post so that the log happens after Application.onCreate()mainHandler.post{// https://github.com/square/leakcanary/issues/1981// We post to a background handler because HeapDumpControl.iCanHasHeap() checks a shared pref// which blocks until loaded and that creates a StrictMode violation.backgroundHandler.post{SharkLog.d{when(valiCanHasHeap=HeapDumpControl.iCanHasHeap()){isYup->application.getString(R.string.leak_canary_heap_dump_enabled_text)isNope->application.getString(R.string.leak_canary_heap_dump_disabled_text,iCanHasHeap.reason())}}}}}
classActivityWatcher(privatevalapplication:Application,privatevalreachabilityWatcher:ReachabilityWatcher):InstallableWatcher{privatevallifecycleCallbacks=object:Application.ActivityLifecycleCallbacksbynoOpDelegate(){overridefunonActivityDestroyed(activity:Activity){//Activity执行onDestory时调用expectWeaklyReachablereachabilityWatcher.expectWeaklyReachable(activity,"${activity::class.java.name} received Activity#onDestroy() callback")}}overridefuninstall(){//为application注册Activity生命周期回调application.registerActivityLifecycleCallbacks(lifecycleCallbacks)}overridefununinstall(){application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)}}
overridefunonActivityStopped(activity:Activity){// This could happen if the callbacks were registered after some activities were already// started. In that case we effectively considers those past activities as not visible.if(startedActivityCount>0){startedActivityCount--}if(hasVisibleActivities&&startedActivityCount==0&&!activity.isChangingConfigurations){//设置为不可见hasVisibleActivities=falseupdateVisible()}}
funonApplicationVisibilityChanged(applicationVisible:Boolean){if(applicationVisible){applicationInvisibleAt=-1L}else{//不可见applicationInvisibleAt=SystemClock.uptimeMillis()// Scheduling for after watchDuration so that any destroyed activity has time to become// watch and be part of this analysis.scheduleRetainedObjectCheck(delayMillis=AppWatcher.config.watchDurationMillis//延迟时间,配置是5s)}}
privatefuncheckRetainedObjects(){valiCanHasHeap=HeapDumpControl.iCanHasHeap()valconfig=configProvider()if(iCanHasHeapisNope){if(iCanHasHeapisNotifyingNope){// Before notifying that we can't dump heap, let's check if we still have retained object.//获取持有对象个数varretainedReferenceCount=objectWatcher.retainedObjectCount//如果持有的对象个数大于0if(retainedReferenceCount>0){//执行GCgcTrigger.runGc()//再次获取引用对象个数retainedReferenceCount=objectWatcher.retainedObjectCount}valnopeReason=iCanHasHeap.reason()//是否应当进行dumpvalwouldDump=!checkRetainedCount(retainedReferenceCount,config.retainedVisibleThreshold,nopeReason)if(wouldDump){valuppercaseReason=nopeReason[0].toUpperCase()+nopeReason.substring(1)onRetainInstanceListener.onEvent(DumpingDisabled(uppercaseReason))showRetainedCountNotification(objectCount=retainedReferenceCount,contentText=uppercaseReason)}}else{SharkLog.d{application.getString(R.string.leak_canary_heap_dump_disabled_text,iCanHasHeap.reason())}}return}//获取持有对象的个数varretainedReferenceCount=objectWatcher.retainedObjectCountif(retainedReferenceCount>0){gcTrigger.runGc()retainedReferenceCount=objectWatcher.retainedObjectCount}if(checkRetainedCount(retainedReferenceCount,config.retainedVisibleThreshold))returnvalnow=SystemClock.uptimeMillis()//计算当前时间和上一次调用dump时间的差值valelapsedSinceLastDumpMillis=now-lastHeapDumpUptimeMillis//如果距离上一次调用dump的时间间隔小于60s 再次执行scheduleRetainedObjectCheckif(elapsedSinceLastDumpMillis<WAIT_BETWEEN_HEAP_DUMPS_MILLIS){onRetainInstanceListener.onEvent(DumpHappenedRecently)showRetainedCountNotification(objectCount=retainedReferenceCount,contentText=application.getString(R.string.leak_canary_notification_retained_dump_wait))//计算延迟时间,延迟到下一次scheduleRetainedObjectCheck(delayMillis=WAIT_BETWEEN_HEAP_DUMPS_MILLIS-elapsedSinceLastDumpMillis)return}dismissRetainedCountNotification()valvisibility=if(applicationVisible)"visible"else"not visible"//dumpHeap(retainedReferenceCount=retainedReferenceCount,retry=true,reason="$retainedReferenceCount retained objects, app is $visibility")}
privatefuncheckRetainedCount(retainedKeysCount:Int,retainedVisibleThreshold:Int,nopeReason:String?=null):BooleanvalcountChanged=lastDisplayedRetainedObjectCount!=retainedKeysCountlastDisplayedRetainedObjectCount=retainedKeysCountif(retainedKeysCount==0){if(countChanged){SharkLog.d{"All retained objects have been garbage collected"}onRetainInstanceListener.onEvent(NoMoreObjects)showNoMoreRetainedObjectNotification()}returntrue}valapplicationVisible=applicationVisiblevalapplicationInvisibleLessThanWatchPeriod=applicationInvisibleLessThanWatchPeriodif(countChanged){valwhatsNext=if(applicationVisible){if(retainedKeysCount<retainedVisibleThreshold){"not dumping heap yet (app is visible & < $retainedVisibleThreshold threshold)"}else{if(nopeReason!=null){"would dump heap now (app is visible & >=$retainedVisibleThreshold threshold) but $nopeReason"}else{"dumping heap now (app is visible & >=$retainedVisibleThreshold threshold)"}}}elseif(applicationInvisibleLessThanWatchPeriod){valwait=AppWatcher.config.watchDurationMillis-(SystemClock.uptimeMillis()-applicationInvisibleAt)if(nopeReason!=null){"would dump heap in $wait ms (app just became invisible) but $nopeReason"}else{"dumping heap in $wait ms (app just became invisible)"}}else{if(nopeReason!=null){"would dump heap now (app is invisible) but $nopeReason"}else{"dumping heap now (app is invisible)"}}SharkLog.d{vals=if(retainedKeysCount>1)"s"else"""Found $retainedKeysCount object$s retained, $whatsNext"}}if(retainedKeysCount<retainedVisibleThreshold){if(applicationVisible||applicationInvisibleLessThanWatchPeriod){if(countChanged){onRetainInstanceListener.onEvent(BelowThreshold(retainedKeysCount))}showRetainedCountNotification(objectCount=retainedKeysCount,contentText=application.getString(R.string.leak_canary_notification_retained_visible,retainedVisibleThreshold))scheduleRetainedObjectCheck(delayMillis=WAIT_FOR_OBJECT_THRESHOLD_MILLIS)returntrue}}returnfalse}
//创建ObjectWatchervalobjectWatcher=ObjectWatcher(clock={SystemClock.uptimeMillis()},checkRetainedExecutor={check(isInstalled){"AppWatcher not installed"}mainHandler.postDelayed(it,retainedDelayMillis)},isEnabled={true})
privatefundumpHeap(retainedReferenceCount:Int,retry:Boolean){saveResourceIdNamesToMemory()valheapDumpUptimeMillis=SystemClock.uptimeMillis()KeyedWeakReference.heapDumpUptimeMillis=heapDumpUptimeMillis//调用HeapDumper的dumpHeap存储堆信息valheapDumpFile=heapDumper.dumpHeap()if(heapDumpFile==null){if(retry){SharkLog.d{"Failed to dump heap, will retry in $WAIT_AFTER_DUMP_FAILED_MILLIS ms"}scheduleRetainedObjectCheck(reason="failed to dump heap",rescheduling=true,delayMillis=WAIT_AFTER_DUMP_FAILED_MILLIS)}else{SharkLog.d{"Failed to dump heap, will not automatically retry"}}showRetainedCountNotification(objectCount=retainedReferenceCount,contentText=application.getString(R.string.leak_canary_notification_retained_dump_failed))return}lastDisplayedRetainedObjectCount=0lastHeapDumpUptimeMillis=SystemClock.uptimeMillis()objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)//分析堆信息HeapAnalyzerService.runAnalysis(application,heapDumpFile)}
overridefundumpHeap():File?{//调用LeakDirectoryProvider的newHeapDumpFile方法获取存储文件路径valheapDumpFile=leakDirectoryProvider.newHeapDumpFile()?:returnnullvalwaitingForToast=FutureResult<Toast?>()showToast(waitingForToast)if(!waitingForToast.wait(5,SECONDS)){SharkLog.d{"Did not dump heap, too much time waiting for Toast."}returnnull}valnotificationManager=context.getSystemService(Context.NOTIFICATION_SERVICE)asNotificationManagerif(Notifications.canShowNotification){valdumpingHeap=context.getString(R.string.leak_canary_notification_dumping)valbuilder=Notification.Builder(context).setContentTitle(dumpingHeap)valnotification=Notifications.buildNotification(context,builder,LEAKCANARY_LOW)notificationManager.notify(R.id.leak_canary_notification_dumping_heap,notification)}valtoast=waitingForToast.get()returntry{//存储文件Debug.dumpHprofData(heapDumpFile.absolutePath)if(heapDumpFile.length()==0L){SharkLog.d{"Dumped heap file is 0 byte length"}null}else{heapDumpFile}}catch(e:Exception){SharkLog.d(e){"Could not dump heap"}// Abort heap dumpnull}finally{cancelToast(toast)notificationManager.cancel(R.id.leak_canary_notification_dumping_heap)}}
overridefunonHandleIntentInForeground(intent:Intent?){if(intent==null||!intent.hasExtra(HEAPDUMP_FILE_EXTRA)){SharkLog.d{"HeapAnalyzerService received a null or empty intent, ignoring."}return}// Since we're running in the main process we should be careful not to impact it.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)valheapDumpFile=intent.getSerializableExtra(HEAPDUMP_FILE_EXTRA)asFilevalconfig=LeakCanary.config//分析堆信息valheapAnalysis=if(heapDumpFile.exists()){analyzeHeap(heapDumpFile,config)}else{missingFileFailure(heapDumpFile)}onAnalysisProgress(REPORTING_HEAP_ANALYSIS)//回调config.onHeapAnalyzedListener.onHeapAnalyzed(heapAnalysis)}
funanalyze(heapDumpFile:File,leakingObjectFinder:LeakingObjectFinder,referenceMatchers:List<ReferenceMatcher>=emptyList(),computeRetainedHeapSize:Boolean=false,objectInspectors:List<ObjectInspector>=emptyList(),metadataExtractor:MetadataExtractor=MetadataExtractor.NO_OP,proguardMapping:ProguardMapping? =null):HeapAnalysis{valanalysisStartNanoTime=System.nanoTime()if(!heapDumpFile.exists()){valexception=IllegalArgumentException("File does not exist: $heapDumpFile")returnHeapAnalysisFailure(heapDumpFile,System.currentTimeMillis(),since(analysisStartNanoTime),HeapAnalysisException(exception))}returntry{listener.onAnalysisProgress(PARSING_HEAP_DUMP)Hprof.open(heapDumpFile).use{hprof->valgraph=HprofHeapGraph.indexHprof(hprof,proguardMapping)valhelpers=FindLeakInput(graph,referenceMatchers,computeRetainedHeapSize,objectInspectors)helpers.analyzeGraph(metadataExtractor,leakingObjectFinder,heapDumpFile,analysisStartNanoTime)}}catch(exception:Throwable){HeapAnalysisFailure(heapDumpFile,System.currentTimeMillis(),since(analysisStartNanoTime),HeapAnalysisException(exception))}}