Dagger: Add custom module having constructor before AndroidInjection.inject()

Created on 5 Mar 2017  路  5Comments  路  Source: google/dagger

I'm testing about dagger.android. I wonder how to add custom module having constructor.

In usual, when we use AndroidInjection.inject(activity), It declares all module has no constructor, right?

example

@Module class ApiModule {
  @Provides fun retrofit(): Retrofit = Retrofit.Builder().build()
}

But in my case, I have some module having constructor

example

@Module class SomeModule(private val a : InterfaceA, private val b : InterfaceB) {
  @Provides fun a() : InterfaceA = a
  @Provides fun b() : InterfaceB = b
}

InterfaceA and InterfaceB need to be declared in Activity or Fragment

So now it has a big problem when doing injection.

class Presenter @Inject internal constructor(private val a : InterfaceA, private val b : InterfaceB) {
  // do something....
}

// before dagger.android
class AppActivity : Activity {
  @Inject lateinit var presenter : Presenter
  override fun onCreate(bundle: Bundle?) {
    super.onCreate(bundle)
    DaggerAppActivityComponent.builder()
        .SomeModule(SomeModule(a = xxx, b = yyy)
        .inject(this)
  }

}

// after dagger.android
class AppActivity : DaggerActivity {
  @Inject lateinit var presenter : Presenter
  override fun onCreate(bundle: Bundle?) {
    super.onCreate(bundle)
  }
}

My question is this :
When we need module having custom constructor but its arguments cannot produce in dagger-graph. If we use AndroidInjection.inject(), then how do we add that module?

Most helpful comment

Can you add a module like this to your subcomponent:

@Module
public abstract class TasksModule {
  @Provides
  static TasksContract.View provideTasksContractView(TasksActivity activity) {
    return (TasksFragment) activity.getFragmentManager().findFragmentById(R.id.contentFrame);
  }
}

All 5 comments

There currently isn't a way to do this. How do you know what values to pass to the module? Are they a function of the Activity, or the Activity's Intent? If so, then you can use the activity binding do retrieve them in a provides method

https://github.com/googlesamples/android-architecture/blob/todo-mvp-dagger/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksActivity.java#L77

It's google's sample of todo-mvp-dagger
TaskPresenter's constructor is this:

@Inject
TasksPresenter(..., TasksContract.View tasksView) {
    mTasksView = tasksView;
}

It's what TasksContract.View is :

public class TasksFragment extends Fragment implements TasksContract.View {}

TaskPresenterModule is :

public TasksPresenterModule(TasksContract.View view) {
    mView = view;
}
@Provides TasksContract.View provideTasksContractView() { return mView; }

finally, DI code is this :

DaggerTasksComponent.builder()
        .tasksRepositoryComponent(((ToDoApplication) getApplication()).getTasksRepositoryComponent())
        .tasksPresenterModule(new TasksPresenterModule(tasksFragment)).build()
        .inject(this);

This pattern is usual of Android-MVP.

so now, dagger-android cannot support special component injection of MVP.

Can you add a module like this to your subcomponent:

@Module
public abstract class TasksModule {
  @Provides
  static TasksContract.View provideTasksContractView(TasksActivity activity) {
    return (TasksFragment) activity.getFragmentManager().findFragmentById(R.id.contentFrame);
  }
}

@ronshapiro
I hope modules separate from android.content.Context.
but I will try it.

@ZeroBrain in that case, could we just provide TasksContract.View on TasksFragmentModule instead of TasksPresenterModule?

@Module
class TasksFragmentModule {

    @Provides
    fun provideTasksView(tasksFragment: TasksFragment): TasksContract.View = tasksFragment

    @Provides
    fun provideTasksPresenter(tasksRepository: TasksRepository, tasksView: TasksContract.View): TasksContract.Presenter {
        return TasksPresenter(tasksRepository, tasksView)
    }
}
Was this page helpful?
0 / 5 - 0 ratings