1 package leakcanary
2 
3 import androidx.work.OneTimeWorkRequest
4 import androidx.work.OutOfQuotaPolicy
5 import androidx.work.WorkManager
6 import leakcanary.EventListener.Event
7 import leakcanary.EventListener.Event.HeapDump
8 import leakcanary.internal.HeapAnalyzerWorker
9 import leakcanary.internal.HeapAnalyzerWorker.Companion.asWorkerInputData
10 import leakcanary.internal.InternalLeakCanary
11 import shark.SharkLog
12 
13 /**
14  * When receiving a [HeapDump] event, starts a WorkManager worker that performs heap analysis.
15  */
16 object WorkManagerHeapAnalyzer : EventListener {
17 
<lambda>null18   internal val validWorkManagerInClasspath by lazy {
19     try {
20       Class.forName("androidx.work.WorkManager")
21       // We need Data.Builder.putByteArray which was introduced in WorkManager 2.1.0.
22       // https://github.com/square/leakcanary/issues/2310
23       val dataBuilderClass = Class.forName("androidx.work.Data\$Builder")
24       dataBuilderClass.declaredMethods.any { it.name == "putByteArray" }.apply {
25         if (!this) {
26           SharkLog.d { "Could not find androidx.work.Data\$Builder.putByteArray, WorkManager should be at least 2.1.0." }
27         }
28       }
29     } catch (ignored: Throwable) {
30       false
31     }
32   }
33 
34   // setExpedited() requires WorkManager 2.7.0+
<lambda>null35   private val workManagerSupportsExpeditedRequests by lazy {
36     try {
37       Class.forName("androidx.work.OutOfQuotaPolicy")
38       true
39     } catch (ignored: Throwable) {
40       false
41     }
42   }
43 
addExpeditedFlagnull44   internal fun OneTimeWorkRequest.Builder.addExpeditedFlag() = apply {
45     if (workManagerSupportsExpeditedRequests) {
46       setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
47     }
48   }
49 
onEventnull50   override fun onEvent(event: Event) {
51     if (event is HeapDump) {
52       val heapAnalysisRequest = OneTimeWorkRequest.Builder(HeapAnalyzerWorker::class.java).apply {
53         setInputData(event.asWorkerInputData())
54         addExpeditedFlag()
55       }.build()
56       SharkLog.d { "Enqueuing heap analysis for ${event.file} on WorkManager remote worker" }
57       val application = InternalLeakCanary.application
58       WorkManager.getInstance(application).enqueue(heapAnalysisRequest)
59     }
60   }
61 }
62