Thracian
Thracian

Reputation: 66759

Dagger2 conversion to android.dagger

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

Answers (1)

arekolek
arekolek

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

Related Questions