Fred Fred - 2 years ago 150
Android Question

How to get MainActivity inside a module using AndroidInjector

With

dagger-android
one now can simply write the following and successfully inject the app's dependencies:

@Module
public abstract class MainActivityModule {
@ContributesAndroidInjector
abstract MainActivity contributesMainActivity();
}

@Singleton
@Component(modules = {
AndroidSupportInjectionModule.class,
AndroidInjectionModule.class,
AppModule.class,
MainActivityModule.class
})
public interface ApplicationComponent {
void inject(BaseApplication baseApplication);

@Component.Builder
interface Builder {
@BindsInstance
Builder application(Application application);

ApplicationComponent build();
}
}

@Module
public abstract class AppModule {}

public class MainActivity extends AppCompatActivity implements
HasSupportFragmentInjector {
@Inject
DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;
@Inject
Whatever whatever;

@Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

@Override
public AndroidInjector<Fragment> supportFragmentInjector() {
return dispatchingAndroidInjector;
}
}


public class BaseApplication extends Application implements
HasActivityInjector {
@Inject
DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;

@Override
public AndroidInjector<Activity> activityInjector() {
return dispatchingAndroidInjector;
}

@Override
public void onCreate() {
DaggerApplicationComponent.builder()
.application(this)
.build()
.inject(this);
super.onCreate();
}
}

public class Whatever {
private final FragmentManager fragmentManager;

@Inject
Whatever(MainActivity mainActivity) {
this.fragmentManager = mainActivity.getSupportFragmentManager();
}
}


A very trivial example. Basically wire up everything to be able to get the object
Whatever
with the correct fragment manager injected. This is ok and works.

However, what if I want to use the main activity inside a module? Say I want to actually make
Whatever
explicitly expose the dependency to the fragment manager by changing the constructor argument:

@Inject
Whatever(FragmentManager fragmentManager) {...}


I now need to provide this dependency. How does one go about it? So I've tried the following:

@Module
public abstract class MainActivityModule {
@ContributesAndroidInjector
abstract MainActivity contributesMainActivity();

@Provides
static FragmentManager providesFragmentManager(MainActivity activity) {
return activity.getSupportFragmentManager();
}
}


Now dagger complains that it cannot find a
@Providers
for the
MainActivity
. I thought the
@ContributesAndroidInjector
method would be able to provide the activity. I'm also a bit puzzled as to how it has no problem injecting the dependency in the first case, but now it cannot do it.

I've also tried to build a factory for this, something like:

public FragmentManagerFactory {
private final FragmentManager fm;

@Inject
FragmentManagerFactory(MainActivity mainActivity){
this.fm = mainActivity.getSupportFragmentManager();
}

public FragmentManager get() {
return fm;
}
}

@Module
public abstract class MainActivityModule {
@ContributesAndroidInjector
abstract MainActivity contributesMainActivity();

@Provides
static FragmentManager providesFragmentManager(FragmentManagerFactory fragmentManagerFactory) {
return fragmentManagerFactory.get();
}
}


Which ends up in the same error.

Has anyone managed to do this?? Before it was pretty easy, one would just build the module with the instance of the
MainActivity
store it in a field and provide that. Now... everything happens behind the curtains.

Would appreciate a lot some help!

Answer Source

In my setup I have a di/ package in the root of sub-di-packages in my ui/ package. In the ui/di package I have declared the following module and component:

@dagger.Module
public class MainActivityModule {
   @Provides
   FragmentManager provideFragmentManager(MainActivity mainActivity) {
    return mainActivity.getSupportFragmentManager();
   }
}

MainActivitySubcomponent

@Subcomponent(modules = MainActivityModule.class)
public interface MainActivitySubcomponent  extends AndroidInjector<MainActivity> {

    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<MainActivity> {
    }
}

on the top-level di package where I keep my ApplicationComponent, I have an additional module

@Module(subcomponents = {MainActivitySubcomponent.class })
public abstract class BuildersModule {

   @Binds
   @IntoMap
   @ActivityKey(MainActivity.class)
   abstract AndroidInjector.Factory<? extends Activity> mainActivityBinder(MainActivitySubcomponent.Builder builder);

}

And as you can imagine BuildersModule, is part of @Component(modules = { of ApplicationComponent.


Disclaimer: I am fairly new to dagger2 and I am still try to figure out a lot of stuff. There may be better ways to achieve what you need.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download