I'm currently working on a android app and using dagger for dependency injection. I've run into trouble when trying to inject dependencies into fragments. I am using a service for Bluetooth that i provide in the following module.
@Module
public abstract class BluetoothModule {
@Provides
@Singleton
@NonNull
static RxBleClient provideRxBleClient(Context context) {
RxBleClient client = RxBleClient.create(context);
RxBleClient.setLogLevel(RxBleLog.VERBOSE);
return client;
}
@Binds
@Reusable
@Production
abstract IBluetoothService provideBlueToothService(BluetoothService bluetoothService);
@Binds
@Reusable
@Test
abstract IBluetoothService provideMockBlueToothService(MockBluetoothService bluetoothService);
}
Since I am using MVP pattern I wanted to inject this service into presenter(constructor injection) and then presenter into fragment (using field injection). Everything works well if I inject presenter into main activity.
@Module
public abstract class MainActivityModule {
@Provides
@ActivityScoped
static MainActivityPresenter provideMainActivityPresenter(
SchedulerProvider schedulerProvider,
@Test IBluetoothService bluetoothService) {
return new MainActivityPresenter(schedulerProvider, bluetoothService);
}
}
However, When I inject BluetoothService int FragmentPresenter
@Module
public abstract class MapFragmentModule {
@Provides
@Reusable
static MapPresenter provideMapPresenter(SchedulerProvider provider,
@Test IBluetoothService bluetoothService) {
return new MapPresenter(provider,bluetoothService);
}
}
I have Following Error
error: [Dagger/MissingBinding] [dagger.android.AndroidInjector.inject(T)] com.wavy.services.bluetooth.IBluetoothService cannot be provided without an @Provides-annotated method.
public interface ApplicationComponent extends AndroidInjector<DaggerApplication>{
^
com.wavy.services.bluetooth.IBluetoothService is injected at
com.wavy.ui.map.MapPresenter.<init>(…, bluetoothService)
com.wavy.ui.map.MapPresenter is injected at
com.wavy.ui.map.MapFragment.mapPresenter
com.wavy.ui.map.MapFragment is injected at
com.wavy.ui.MainActivity.mapFragment
com.wavy.ui.MainActivity is injected at
dagger.android.AndroidInjector.inject(T)
component path: com.wavy.config.dagger.components.ApplicationComponent → com.wavy.config.dagger.builders.ActivityBuilder_BindMainActivity.MainActivitySubcomponent
So I have found this issue on github with similar problem as mine Issue Link
Solution was not to use @Inject annotation in Fragment constructor so i did it and after that I get another error
error: [Dagger/MissingBinding] [dagger.android.AndroidInjector.inject(T)] com.wavy.ui.map.MapFragment cannot be provided without an @Inject constructor or an @Provides-annotated method. This type supports members injection but cannot be implicitly provided.
public interface ApplicationComponent extends AndroidInjector<DaggerApplication>{
^
A binding with matching key exists in component: com.wavy.config.dagger.builders.FragmentBuilder_BindMapFragment.MapFragmentSubcomponent
com.wavy.ui.map.MapFragment is injected at
com.wavy.ui.MainActivity.mapFragment
com.wavy.ui.MainActivity is injected at
dagger.android.AndroidInjector.inject(T)
component path: com.wavy.config.dagger.components.ApplicationComponent → com.wavy.config.dagger.builders.ActivityBuilder_BindMainActivity.MainActivitySubcomponent
This is how i provide my fragment
@ContributesAndroidInjector(modules = {MapFragmentModule.class})
abstract MapFragment bindMapFragment();
Bluetooth service
public class BluetoothService implements IBluetoothService {
private final RxBleClient rxBleClient;
@Inject
public BluetoothService(RxBleClient rxBleClient) {
this.rxBleClient = rxBleClient;
}
//code omitted for clarity
}
Map Fragment
public class MapFragment extends BaseFragment {
@Inject
MapPresenter mapPresenter;
@Inject
public MapFragment() {
// Required empty public constructor
}
//code omitted for clarty
}
Map Presenter
public class MapPresenter extends BasePresenter<MapFragment> {
private final IBluetoothService bluetoothService;
@Inject
public MapPresenter(SchedulerProvider schedulerProvider,
IBluetoothService bluetoothService) {
super(schedulerProvider);
this.bluetoothService = bluetoothService;
}
//code ommited for clarity
}
ActivityBuilder
@Module
public abstract class ActivityBuilder {
@ActivityScoped
@ContributesAndroidInjector(modules = {MainActivityModule.class, FragmentBuilder.class})
abstract MainActivity bindMainActivity();
}
AppComponent
@Singleton
@Component(modules = {
AndroidSupportInjectionModule.class,
ApplicationModule.class,
ActivityBuilder.class,
BluetoothModule.class,
RetrofitRestClientModule.class,
RxModule.class,
ApiModule.class,
RoomDatabaseModule.class
})
public interface ApplicationComponent extends AndroidInjector<DaggerApplication>{
void inject(WavyApplication application);
@Override
void inject(DaggerApplication instance);
@Component.Builder
interface Builder{
@BindsInstance
Builder application(Application application);
ApplicationComponent build();
}
}
I would much appreciate any ideas how to solve this problem, thanks for help