Skip to content

Commit

Permalink
Return nulls from MultibindingWorkerFactory for non-existent Worker c…
Browse files Browse the repository at this point in the history
…lasses instead of throwing exceptions (deliveryhero#96)

(cherry picked from commit 283d846)
  • Loading branch information
viakunin authored and msfjarvis committed Apr 26, 2024
1 parent a283307 commit 7c4ff45
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.deliveryhero.whetstone.worker

import android.content.Context
import androidx.annotation.RestrictTo
import androidx.work.ListenableWorker
import androidx.work.WorkerFactory
import androidx.work.WorkerParameters
Expand All @@ -21,22 +22,32 @@ public class MultibindingWorkerFactory @Inject constructor(
workerParameters: WorkerParameters
): ListenableWorker? {
val workerComponent = workerComponentFactory.create(appContext, workerParameters)
val workerClass = loadClass(appContext.classLoader, workerClassName)
val workerClass = loadClass(appContext.classLoader, workerClassName) ?: return null

return workerComponent.workerMap[workerClass]?.get()
}

private companion object {
internal companion object {

// Implementation adapted from androidx.fragment.app.FragmentFactory#loadClass
private val classCache: MutableMap<ClassLoader, MutableMap<String, Class<out ListenableWorker>>> = hashMapOf()
private val classCache: MutableMap<ClassLoader, MutableMap<String, Class<out ListenableWorker>>> =
hashMapOf()

@Throws(ClassNotFoundException::class)
private fun loadClass(classLoader: ClassLoader, className: String): Class<out ListenableWorker> {
@RestrictTo(RestrictTo.Scope.LIBRARY)
internal fun loadClass(
classLoader: ClassLoader,
className: String,
): Class<out ListenableWorker>? {
val classMap = classCache.getOrPut(classLoader) { hashMapOf() }
return classMap.getOrPut(className) {
val rawClass = Class.forName(className, false, classLoader)
rawClass.asSubclass(ListenableWorker::class.java)
try {
Class.forName(className, false, classLoader)
.asSubclass(ListenableWorker::class.java)
} catch (e: ClassNotFoundException) {
return null
} catch (e: ClassCastException) {
return null
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.deliveryhero.whetstone.worker

import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull

internal class LoadClassTest {
@Test
fun `loadClass returns null for non-existing class`() {
val result = MultibindingWorkerFactory.loadClass(
this.javaClass.classLoader!!,
"non.existent.worker.Class"
)
assertNull(result)
}

@Test
fun `loadClass returns null for class which is not ListenableWorker subclass`() {
val result = MultibindingWorkerFactory.loadClass(
this.javaClass.classLoader!!,
"com.deliveryhero.whetstone.worker.LoadClassTest\$TestClass"
)
assertNull(result)
}

@Test
fun `loadClass returns class for existing classname`() {
val result = MultibindingWorkerFactory.loadClass(
this.javaClass.classLoader!!,
"com.deliveryhero.whetstone.worker.LoadClassTest\$TestWorker"
)
assertEquals(TestWorker::class.java, result)
}

private class TestWorker(
context: Context,
workerParams: WorkerParameters,
) : Worker(context, workerParams) {
override fun doWork(): Result {
TODO("Not implemented")
}
}

@Suppress("unused")
private class TestClass
}

0 comments on commit 7c4ff45

Please sign in to comment.