Skip to content

Commit

Permalink
Stop using WorkManager internals
Browse files Browse the repository at this point in the history
Updating the WorkManager version to `2.9.0` which added `Worker.getForegroundInfo` ([release notes](https://developer.android.com/jetpack/androidx/releases/work#2.8.0)) which removes the need to mess with `ListenableFuture`.

Also add (implicit in 2.9.0) dependency on futures so that we can use `CallbackToFutureAdapter` when extending `RemoteListenableWorker` which does not offer a `getForegroundInfo` implementation.

Fixes #2650
  • Loading branch information
pyricau committed Apr 17, 2024
1 parent 3c71194 commit 34c5d68
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 25 deletions.
3 changes: 2 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ kotlinCompiler = "1.4.21"
kotlinLib = "1.3.72"
androidXTest = "1.1.0"
androidXJunit = "1.1.3"
workManager = "2.7.0"
workManager = "2.9.0"

[libraries]
gradlePlugin-android = { module = "com.android.tools.build:gradle", version = "4.2.2" }
Expand Down Expand Up @@ -50,6 +50,7 @@ androidX-test-junit = { module = "androidx.test.ext:junit", version.ref = "andro
androidX-test-junitKtx = { module = "androidx.test.ext:junit-ktx", version.ref = "androidXJunit" }
androidX-work-runtime = { module = "androidx.work:work-runtime", version.ref = "workManager" }
androidX-work-multiprocess = { module = "androidx.work:work-multiprocess", version.ref = "workManager" }
androidX-concurrent-futures = { module = "androidx.concurrent:concurrent-futures", version = "1.1.0" }

androidSupport = { module = "com.android.support:support-v4", version = "28.0.0" }
assertjCore = { module = "org.assertj:assertj-core", version = "3.9.1" }
Expand Down
3 changes: 3 additions & 0 deletions leakcanary-android-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ dependencies {

// Optional dependency
compileOnly libs.androidX.work.runtime
// work.runtime has an implementation dependency on futures so it should always be available.
// We need futures to leverage CallbackToFutureAdapter
compileOnly libs.androidX.concurrent.futures
compileOnly libs.androidX.work.multiprocess

testImplementation libs.assertjCore
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import androidx.work.Data
import androidx.work.ForegroundInfo
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.impl.utils.futures.SettableFuture
import com.google.common.util.concurrent.ListenableFuture
import com.squareup.leakcanary.core.R
import leakcanary.EventListener.Event

internal class HeapAnalyzerWorker(appContext: Context, workerParams: WorkerParameters) :
internal class HeapAnalyzerWorker(
appContext: Context,
workerParams: WorkerParameters
) :
Worker(appContext, workerParams) {
override fun doWork(): Result {
val doneEvent =
Expand All @@ -22,8 +23,8 @@ internal class HeapAnalyzerWorker(appContext: Context, workerParams: WorkerParam
return Result.success()
}

override fun getForegroundInfoAsync(): ListenableFuture<ForegroundInfo> {
return applicationContext.heapAnalysisForegroundInfoAsync()
override fun getForegroundInfo(): ForegroundInfo {
return applicationContext.heapAnalysisForegroundInfo()
}

companion object {
Expand All @@ -36,21 +37,17 @@ internal class HeapAnalyzerWorker(appContext: Context, workerParams: WorkerParam
inline fun <reified T> Data.asEvent(): T =
Serializables.fromByteArray<T>(getByteArray(EVENT_BYTES)!!)!!

fun Context.heapAnalysisForegroundInfoAsync(): ListenableFuture<ForegroundInfo> {
val infoFuture = SettableFuture.create<ForegroundInfo>()
fun Context.heapAnalysisForegroundInfo(): ForegroundInfo {
val builder = Notification.Builder(this)
.setContentTitle(getString(R.string.leak_canary_notification_analysing))
.setContentText("LeakCanary is working.")
.setProgress(100, 0, true)
val notification =
Notifications.buildNotification(this, builder, NotificationType.LEAKCANARY_LOW)
infoFuture.set(
ForegroundInfo(
R.id.leak_canary_notification_analyzing_heap,
notification
)
return ForegroundInfo(
R.id.leak_canary_notification_analyzing_heap,
notification
)
return infoFuture
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package leakcanary.internal

import android.content.Context
import androidx.concurrent.futures.CallbackToFutureAdapter
import androidx.work.ForegroundInfo
import androidx.work.WorkerParameters
import androidx.work.impl.utils.futures.SettableFuture
Expand All @@ -9,10 +10,13 @@ import com.google.common.util.concurrent.ListenableFuture
import leakcanary.BackgroundThreadHeapAnalyzer.heapAnalyzerThreadHandler
import leakcanary.EventListener.Event.HeapDump
import leakcanary.internal.HeapAnalyzerWorker.Companion.asEvent
import leakcanary.internal.HeapAnalyzerWorker.Companion.heapAnalysisForegroundInfoAsync
import leakcanary.internal.HeapAnalyzerWorker.Companion.heapAnalysisForegroundInfo
import shark.SharkLog

internal class RemoteHeapAnalyzerWorker(appContext: Context, workerParams: WorkerParameters) :
internal class RemoteHeapAnalyzerWorker(
appContext: Context,
workerParams: WorkerParameters
) :
RemoteListenableWorker(appContext, workerParams) {

override fun startRemoteWork(): ListenableFuture<Result> {
Expand All @@ -37,6 +41,8 @@ internal class RemoteHeapAnalyzerWorker(appContext: Context, workerParams: Worke
}

override fun getForegroundInfoAsync(): ListenableFuture<ForegroundInfo> {
return applicationContext.heapAnalysisForegroundInfoAsync()
return CallbackToFutureAdapter.getFuture {
applicationContext.heapAnalysisForegroundInfo()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,17 @@ class RemoteLeakCanaryWorkerService : RemoteWorkerService() {
class FakeAppContextConfigurationProvider(base: Context) : ContextWrapper(base),
Configuration.Provider {

override val workManagerConfiguration
get() = Configuration.Builder()
// If the default package name is not set, WorkManager will cancel all runnables
// when initialized as it can't tell that it's not running in the main process.
// This would lead to an extra round trip where the canceling reaches the main process
// which then cancels the remote job and reschedules it and then only the work gets done.
.setDefaultProcessName(packageName)
.build()

// No real app context for you, sorry!
override fun getApplicationContext() = this

override fun getWorkManagerConfiguration() = Configuration.Builder()
// If the default package name is not set, WorkManager will cancel all runnables
// when initialized as it can't tell that it's not running in the main process.
// This would lead to an extra round trip where the canceling reaches the main process
// which then cancels the remote job and reschedules it and then only the work gets done.
.setDefaultProcessName(packageName)
.build()
}

private val fakeAppContext by lazy {
Expand Down

0 comments on commit 34c5d68

Please sign in to comment.