Reputation: 66759
I studied some tutorials about new dagger.android approach but can't make it right. I built a simple tutorial with subcomponent builder but i can't convert this to new approach. All tutorials and articles leave required parts empty.
This example is for learning purposes may not be suitable for practical situations and i only intended implementing field injection, constructor injection may be more appropriate.
Scope
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}
Parent Component
@Component(modules = ApplicationModule.class)
@Singleton
public interface ApplicationComponent {
// Calls SubComponent Builder from MainActivity using ApplicationComponent
ToastMakerSubComponent.Builder toastMakerBuilder();
}
Parent Module, i didn't use @BindsInstance in app module for learning different approaches
@Module(subcomponents = {ToastMakerSubComponent.class})
public class ApplicationModule {
private Context context;
public ApplicationModule(Context context) {
this.context = context;
}
@Provides
@Singleton
SharedPreferences provideSharedPreferences() {
System.out.println("ApplicationModule context: " + context);
return context.getSharedPreferences("PrefName", Context.MODE_PRIVATE);
}
}
Sub Component
@ActivityScope
@Subcomponent(modules = {ToastMakerModule.class})
public interface ToastMakerSubComponent {
void inject(MainActivity mainActivity);
/*
Builder is called from Parent Component,
and parent component is called from scope(Activity, Fragment, etc.)
*/
@Subcomponent.Builder
interface Builder {
ToastMakerSubComponent build();
@BindsInstance
Builder context(Context context);
}
}
Sub Component Module
@Module
public class ToastMakerModule {
@ActivityScope
@Provides
ToastMaker provideToastMaker(Context context) {
System.out.println("ToastMakerModule context: " + context);
return new ToastMaker(context);
}
}
Instantiate ApplicationComponent inside MyApplication
mApplicationComponent = DaggerApplicationComponent
.builder()
.applicationModule(new ApplicationModule(getApplicationContext()))
.build();
And get sub component inside Activity
ApplicationComponent applicationComponent = ((MyApplication) getApplication()).getApplicationComponent();
ToastMakerSubComponent toastMakerSubComponent = applicationComponent
.toastMakerBuilder()
.context(this)
.build();
toastMakerSubComponent.inject(this);
After following this, this, and this tutorial i built the following as
@Component(modules = ApplicationModule.class)
@Singleton
public interface ApplicationComponent {
// What should i put here ???
}
Parent module but i didn't get how i should provide provideSharedPreferences
@Module(subcomponents = {ToastMakerSubComponent.class})
abstract class ApplicationModule {
@Binds
@IntoMap
@ActivityKey(MainActivity.class)
abstract AndroidInjector.Factory<? extends MainActivity>
bindYourActivityInjectorFactory(ToastMakerSubComponent.Builder builder);
}
Sub component is missing ToastMakerSubComponent build();
and @BindsInstance Builder context(Context context);
how should i add these to this component.
@ActivityScope
@Subcomponent(modules = {ToastMakerModule.class})
public interface ToastMakerSubComponent extends AndroidInjector<MainActivity> {
// ??? Is this required?
void inject(MainActivity mainActivity);
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity> {
???
}
}
Also i see @ContributesAndroidInjector
, where does it fit in this example?
Upvotes: 0
Views: 187
Reputation: 9621
Here's the diff of changes: https://www.diffchecker.com/3IL8UQ7P
Make app component extend AndroidInjector<MyApplication>
and include the AndroidSupportInjectionModule
@Component(modules = {ApplicationModule.class, AndroidSupportInjectionModule.class})
@Singleton
public interface ApplicationComponent extends AndroidInjector<MyApplication> {
}
Make the subcomponent extend AndroidInjector<MainActivity>
and the builder AndroidInjector.Builder<MainActivity>
, use seedInstance
to provide arguments to @BindsInstance
methods.
@ActivityScope
@Subcomponent(modules = {ToastMakerModule.class})
public interface ToastMakerSubComponent extends AndroidInjector<MainActivity> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity> {
@Override
public void seedInstance(MainActivity instance) {
context(instance);
}
@BindsInstance
public abstract Builder context(Context context);
}
}
Add a binding for the subcomponent builder to a module installed in the app component:
@Module(subcomponents = {ToastMakerSubComponent.class})
public abstract class ApplicationModule {
private Context context;
public ApplicationModule(Context context) {
this.context = context;
}
@Provides
@Singleton
SharedPreferences provideSharedPreferences() {
System.out.println("ApplicationModule context: " + context);
return context.getSharedPreferences("PrefName", Context.MODE_PRIVATE);
}
@Binds
@IntoMap
@ActivityKey(MainActivity.class)
abstract AndroidInjector.Factory<? extends Activity> bindMainActivityFactory(ToastMakerSubComponent.Builder builder);
}
Let framework classes extend the Dagger*
counterparts, or implement Has*Injector
interfaces if you can't use inheritance
public class MyApplication extends DaggerApplication {
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerApplicationComponent.create();
}
}
public class MainActivity extends DaggerAppCompatActivity {
@Inject
SharedPreferences sharedPreferences;
@Inject
ToastMaker toastMaker;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toastMaker.showToast("sharedPreferences " + sharedPreferences);
}
}
Upvotes: 2