Hafez Divandari
Hafez Divandari

Reputation: 9069

How to use Dagger2 in Android to inject activity objects easier?

On Android, When using Dagger2, I have to call the following line on every activities that uses apiService:

@Inject
public ApiService apiService;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    DaggerApiComponent.builder()
                      .activityModule(new ActivityModule(this))
                      .build()
                      .inject(this);

    //...
}

How can I summarize it to something like:

DaggerApiComponent.builder()
                  .activity(this)
                  .build()
                  .inject(this);

or even simpler to something like:

MyApplication.injectApiService(this);

How should I change my component and modules to use Dagger2 with less copy-pasting code in my activities?

Here is my ApiComponent:

@Singleton
@Component(modules = ApiModule.class)
public interface ApiComponent {

    void inject(MainActivity activity);
    void inject(...
}

Here is ApiModule:

@Module(includes = {RetrofitModule.class, ActivityModule.class})
public class ApiModule {

    @Singleton
    @Provides
    public static ApiService provideApiService(Activity activity) {
        //...
    }
}

and ActivityModule:

@Module
public class ActivityModule {

    private final Activity context;

    public ActivityModule(Activity context) {
        this.context = context;
    }

    @Singleton
    @Provides
    public Activity provideActivityContext() {
        return context;
    }
}

Upvotes: 2

Views: 816

Answers (2)

jaychang0917
jaychang0917

Reputation: 1888

The approach of such "DI" has two problems:

  • like what the OP said: we need to copy & paste this boilerplate wherever we need injection, which is tedious and hard to refactor.
  • Injectee (e.g. Activity) should not know where the @Inject instance is from, what it cares about is just "hey, give me an instance of it".

To resolve above problems, dagger.android comes to the rescue.

  1. Create AndroidInjector for each component.
// App component
@Singleton
@Component(
    modules = [
        AndroidSupportInjectionModule::class, // build-in module
        ActivityBindingModule::class,            
        AppModule::class
    ]
)
interface AppComponent : AndroidInjector<MainApplication> {
    // we need to bind `MainApplication` instance to this component,
    // so we have a builder here.
    @Component.Builder
    abstract class Builder : AndroidInjector.Builder<MainApplication>()
}
// Each controller (e.g. `Activity` / `Fragment` / `Service`) subcomponents
@Module
abstract class ActivityBindingModule {
    // will generate a FooActivitySubcomponent under ActivityBindingModule's component
    @ActivityScoped
    @ContributesAndroidInjector(modules = [FooModule::class])
    internal abstract fun fooActivity(): FooActivity

    @ActivityScoped
    @ContributesAndroidInjector(modules = [BarModule::class])
    internal abstract fun barActivity(): BarActivity
}
  1. Wire up your AndroidInjectors, so it can do injection for us using provided injector in step 1.
// App component
class MainApplication : DaggerApplication() {

    override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
        return DaggerAppComponent.builder().create(this)
    }
}
// Each controller subcomponents
class FooActivity : DaggerAppCompatActivity() {
    @Inject lateinit var foo: Foo

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState) 
        // no need to call inject() here anymore!
        foo.doSth()
    }
}

For a concrete example: check out iosched

Upvotes: 1

fweigl
fweigl

Reputation: 22038

You can use the Android Activity injector, the usage is described well here.

Upvotes: 1

Related Questions