Chapters

Hide chapters

Dagger by Tutorials

First Edition · Android 11 · Kotlin 1.4 · AS 4.1

B. Appendix B: Assisted Injection
Written by Massimo Carli

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

Dagger and Hilt are libraries in continuous evolution. Google and Square, with the help of the open-source community, keep improving them, both by creating new features and by improving the performance of the existing ones.

One of Dagger’s new improvements is assisted injection, which Google added in version 2.31. In this appendix, you’ll learn:

  1. What assisted injection is.
  2. How to implement it with @Assisted, @AssistedInject and @AssistedFactory.

To do this, you’ll work on the RandomFunNumber app.

What is assisted injection?

In this book, you learned all about dependency injection. You saw several examples of how dependency injection can improve the maintainability and testability of your code. You also learned that constructor injection is the best type of injection because it allows you to provide dependencies at the exact moment you create an instance of an object.

It’s not unusual to see code like this:

class MyService @Inject constructor( // 1 
	private val dep1: Dependency1, // 2
	private val dep2: Dependency2, // 2
	private val dep3: Dependency3  // 2
) : Service

Here, you define:

  1. MyService as an implementation of the Service interface, and you annotate its primary constructor with @Inject. This tells Dagger how to create an instance of MyService to use anywhere you need an object of type Service.
  2. The dependencies of MyService as primary constructor parameters. In this case, MyService needs objects of types Dependency1, Dependency2 and Dependency3.

By also telling Dagger how to provide objects of type Dependency1, Dependency2 and Dependency3, you know Dagger will create the instance of MyService for you every time you need a Service.

This is very cool, but sometimes you need something a bit different. To understand what, return to RandomFunNumber to see a practical example.

An example of constructor injection

Open the RandomFunNumber project from the starter folder of the materials for this appendix. This is a simplified version of the project you used in Chapter 19, “Testing With Hilt”, with an important difference.

Figure 21.1 — The RandomFunNumber app
Hepisu 85.7 — Wmi XotvuwRedWarkot ixf

hilt_version = "2.31.2-alpha"
class FunNumberServiceImpl @Inject constructor( // 1
    private val numberGenerator: NumberGenerator, // 2
    private val funNumberEndpoint: FunNumberEndpoint // 2
) : FunNumberService {
  // ...
}
  // ...
    @Binds
    @ActivityScoped
    fun bindFunNumberService( // HERE
        impl: FunNumberServiceImpl 
    ): FunNumberService
  // ...    

Providing dependencies with assisted injection

Suppose you want to provide a different NumberGenerator implementation every time you need a FunNumberServiceImpl. One way to achieve this is to use a custom qualifier.

Replacing @Inject with @AssistedInject

For your first step, you’ll inform Dagger that you want to use assisted injection, and will therefore provide some of the dependencies you need for a specific binding.

class FunNumberServiceImpl @AssistedInject constructor( // HERE
    private val numberGenerator: NumberGenerator,
    private val funNumberEndpoint: FunNumberEndpoint
) : FunNumberService {
  // ...
}

Using @Assisted

You just learned how to tell Dagger that you’ll handle some of FunNumberServiceImpl’s dependencies. Now, you need to declare which dependencies you’ll provide.

class FunNumberServiceImpl @AssistedInject constructor(
    @Assisted private val numberGenerator: NumberGenerator, // HERE
    private val funNumberEndpoint: FunNumberEndpoint 
) : FunNumberService {
  // ...
}

Using @AssistedFactory

So far, you’ve told Dagger that:

@AssistedFactory // 1
interface FunNumberServiceFactory { // 2

  fun create(
      numberGenerator: NumberGenerator // 3
  ): FunNumberServiceImpl // 4
}
@Module(includes = [
  NavigationModule::class
])
@InstallIn(ActivityComponent::class)
object ActivityModule

Using FunNumberServiceFactory in FunNumberFragment

You now need to inject FunNumberServiceFactory where you need a FunNumberService.

@AndroidEntryPoint
class FunNumberFragment : Fragment() {

  private lateinit var funNumberTextView: TextView
  private lateinit var funFactTextView: TextView

  @Inject
  lateinit var funNumberServiceFactory: FunNumberServiceFactory // 1
  private lateinit var funNumberService: FunNumberService // 2

  override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    val ctx = container?.context ?: IllegalStateException("Context not available")
    funNumberService = funNumberServiceFactory.create(object : NumberGenerator { // 3
      override fun randomNumber(): Int = 28
    })
    return LayoutInflater.from(ctx as Context).inflate(R.layout.fragment_show_number, container, false).apply {
		// ...       
    }
  }

  override fun onStop() {
    funNumberService.stop()
    super.onStop()
  }
}
Figure 21.2 — The RandomFunNumber app
Dakaqe 63.0 — Gxe BockewHosLagxiy awh

Limitations to assisted injection in Dagger

Congratulations! You’ve added assisted injection to the RandomFunNumber app. This is a new feature in Dagger, so be aware that following versions of the library might include improvements. At the moment, it has some limitations, including:

Key points

  • Dagger has offered assisted injection since version 2.31.0.
  • @AssistedInject allows you to tag the primary constructor with @Assisted parameters.
  • To create an instance of @AssistedInject, you need an @AssistedFactory.
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now