Reputation: 13546
I have application-level scoped Singleton classes TaskRepository and AppConfig, which have dependencies of singleton types. In my activity, instance of TaskRepository
is injected, but all its fields are null.
public class MVPApplication extends Application implements HasActivityInjector {
@Inject
DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;
@Inject
TasksRepository tasksRepository;
@Inject
AppConfig appConfig;
private static MVPApplication instance;
@Override
public void onCreate() {
super.onCreate();
instance = this;
Realm.init(this);
DaggerAppComponent
.builder()
.application(this)
.realmBuilder(Realm.getDefaultInstance())
.build()
.inject(this);
}
@Override
public DispatchingAndroidInjector<Activity> activityInjector() {
return activityDispatchingAndroidInjector;
}
}
@Singleton
@Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
ActivityBindingModule.class})
public interface AppComponent extends AndroidInjector<DaggerApplication> {
@Component.Builder
interface Builder {
@BindsInstance
Builder application(Application application);
@BindsInstance
Builder realmBuilder(Realm realm);
AppComponent build();
}
void inject(MVPApplication application);
TasksRepository getRepository();
AppConfig getAppConfig();
}
@Singleton
@Module
public class AppModule {
@Provides
@Singleton
TasksRepository provideTasksRepository() {
return new TasksRepository();
}
@Provides
@Singleton
AppConfig provideAppConfig() {
return new AppConfig();
}
}
@Singleton
public class TasksRepository implements RepositoryDatasource {
@Inject
Context mContext;
@Inject
AppConfig appConfig; //Singleton
@Inject
Realm realm; //Singleton
//There is no constructor. Only public functions using fields
}
@Singleton
public class AppConfig {
@Inject
Context mContext;
//There is no constructor. Only public functions using fields
}
Please highlight my mistakes... Any help will be appreciated. Thanks!
Upvotes: 1
Views: 1890
Reputation: 21497
There are two main types of injection with Dagger 2 - field injection and constructor injection. Field injection is where you annotate fields of a class like an Activity or Fragment with @Inject
and then explicitly request injection from a Dagger 2 Component:
class MainActivity extends AppCompatActivity {
@Inject TasksRepository tasksRepository;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidInjector.inject(this); //explicit request for injection
}
}
Note that without the explicit request for injection in line 2 of onCreate
the field tasksRespository would remain null
.
For classes instantiated by the Android OS like Activity or Application field injection is appropriate. For others, including your repositories, it is preferable to use constructor injection. Why? When you are testing you can pass in test doubles through the constructor and stub behaviour or verify on the test doubles.
So to get it working you should refactor your TasksRepository
to use constructor injection:
@Singleton
public class TasksRepository implements RepositoryDatasource {
private final Context context;
private final AppConfig appConfig;
private final Realm realm;
@Inject
TasksRepository(Context context, AppConfig appConfig, Realm realm) {
this.context = context;
this.appConfig = appConfig;
this.realm = realm;
}
Note that this step will highlight any mistakes you have made in setting up your object graph. So if Dagger 2 cannot provide any of the 3 parameters in the construtor for TasksRepository
it will let you know with a compile-time message.
Upvotes: 4