Entry Points

What is an entry point?

An entry point is the boundary where you can get Dagger-provided objects from code that cannot use Dagger to inject its dependencies. It is the point where code first enters into the graph of objects managed by Dagger.

If you’re already familiar with Dagger components, an entry point is just an interface that the Hilt generated component will extend.

When do you need an entry point?

You will need an entry point when interfacing with non-Dagger libraries or Android components that are not yet supported in Hilt and need to get access to Dagger objects.

In general though, most entry points will be at Android instantiated locations like the activities, fragments, etc. @AndroidEntryPoint is a specialized tool to handle the definition of entry points and access to the entry points (among other things) for these classes. Since this is already handled specially for those Android classes, for the following docs, we’ll assume the entry point is needed in some other type of class.

How to use an entry point?

Create an EntryPoint

To create an entry point, define an interface with an accessor method for each binding type needed (including its qualifier) and mark the interface with the @EntryPoint annotation. Then add @InstallIn to specify the component in which to install the entry point.

Java
Kotlin
@EntryPoint
@InstallIn(SingletonComponent.class)
public interface FooBarInterface {
  @Foo Bar bar();
}
@EntryPoint
@InstallIn(SingletonComponent::class)
interface FooBarInterface {
  @Foo fun bar(): Bar
}

Access an EntryPoint

To access an entry point, use the EntryPoints class passing as a parameter the component instance or the @AndroidEntryPoint object which acts as a component holder. Make sure the component you pass in matches the @InstallIn annotation on the @EntryPoint interface that you pass in as well.

Using the entry point interface we defined above:

Java
Kotlin
Bar bar = EntryPoints.get(applicationContext, FooBarInterface.class).bar();
val bar = EntryPoints.get(applicationContext, FooBarInterface::class.java).bar()

Additionally, the methods in EntryPointAccessors are more appropriate and type safe for retrieving entry points from the standard Android components.

Best practice: where to define an entry point interface?

If implementing a class instantiated from a non-Hilt library and a Foo class is needed from Dagger, should the entry point interface be defined with the using class or with Foo?

In general, the answer is that the entry point should be defined with the using class since that class is the reason for needing the entry point interface, not Foo. If that class later needs more dependencies, extra methods can easily be added to the entry point interface to get them. Essentialy, the entry point interface acts in place of the @Inject constructor for that class. If instead the entry point were defined with Foo, then other people may be confused about if they should inject Foo or use the entry point interface. It would also result in more entry point interfaces being added if other dependencies are needed in the future.

Best practice

Java
Kotlin
public final MyClass extends NonHiltLibraryClass {
  // No @Inject because this isn't instantiated in a Dagger context
  public MyClass() {}

  @EntryPoint
  @InstallIn(SingletonComponent.class)
  public interface MyClassInterface {
    Foo foo();

    Bar bar();
  }

  void doSomething(Context context) {
    MyClassInterface myClassInterface =
        EntryPoints.get(applicationContext, MyClassInterface.class);
    Foo foo = myClassInterface.foo();
    Bar bar = myClassInterface.bar();
  }
}
// No @Inject because this isn't instantiated in a Dagger context public
class MyClass : NonHiltLibraryClass() {

  @EntryPoint
  @InstallIn(SingletonComponent::class)
  interface MyClassInterface {
    fun foo(): Foo

    fun bar(): Bar
  }

  fun doSomething(context: Context) {
    val myClassInterface =
        EntryPoints.get(applicationContext, MyClassInterface::class.java)
    val foo = myClassInterface.foo()
    val bar = myClassInterface.bar()
  }
}

Bad practice

Java
Kotlin
@Module
@InstallIn(SingletonComponent.class)
public final class FooModule {
  @Provides
  Foo provideFoo() {
    return new Foo();
  }

  @EntryPoint
  @InstallIn(SingletonComponent.class)
  public interface FooInterface {
    Foo foo();
  }
}
@Module
@InstallIn(SingletonComponent::class)
object FooModule {
  @Provides
  fun provideFoo(): Foo {
    return Foo()
  }

  @EntryPoint
  @InstallIn(SingletonComponent::class)
  interface FooInterface {
    fun foo(): Foo
  }
}

Visibility

All types returned from an entry point’s method must be public. This is because the generated Dagger component, which is often not in the same package, must implement the entry point method.