Erik Medina
Erik Medina

Reputation: 335

How to provide custom dependencies to a constructor injection with Dagger

I have my NetworkDataSource which I want to inject. I want to use constructor injection because I'm the owner of this class:

 public class NetworkDataSource {

    @Inject
    public NetworkDataSource(String url, String method) {
        this.url = url;
        this.method = method;
    }
 }

The thing is that the arguments of this constructor (which are the dependencies) are retrieved from a Service as extras when a BroadcastIntent is triggered. So my question is, how could I provide these arguments retrieved from the Service to my NetworkDataSource constructor injection?

The Service looks like this:

public class Service extends IntentService {

   // I would like to inject NetworkDataSource
   //@Inject
   //NetworkDataSource netWorkDataSource;

   public String url;
   public String method;

   @Override
   protected void onHandleIntent(Intent workIntent) {
     url = workIntent.getStringExtra("url");
     method = workIntent.getStringExtra("method");
   }

   ............
   ............

   networkDataSource.myMethod();
}

Thank you.

Upvotes: 1

Views: 830

Answers (2)

EpicPandaForce
EpicPandaForce

Reputation: 81588

It's a tricky question because you'd think Dagger primarily solves the problem that now you no longer need to introduce "Factory" classes in order to re-configure behavior.

However, you actually still need to use "Factory" classes to create objects with dynamic constructor arguments that are obtained at runtime.

So:

 public class NetworkDataSource {

    @Inject
    public NetworkDataSource(String url, String method) {
        this.url = url;
        this.method = method;
    }
 }

Would be

@Singleton
public class NetworkDataSourceFactory {
    private final OkHttpClient okHttpClient;
    private final Gson gson;

    @Inject
    public NetworkDataSourceFactory(OkHttpClient okHttpClient, Gson gson) {
        this.okHttpClient = okHttpClient;
        this.gson = gson;
    }

    public NetworkDataSource create(String url, String method) {
        return new NetworkDataSource(okHttpClient, gson, url, method);
    }
}

Then now you can inject NetworkDataSourceFactory, and create a NetworkDataSource when you receive your arguments:

@Inject NetworkDataSourceFactory factory;

....
    networkDataSource = factory.create(url, method);

Supposedly you can use AutoFactory to help with creating the factory, but I haven't used it yet, all I know is that it exists for this particular usecase.

EDIT: you can also check out https://github.com/square/AssistedInject to help with this problem.

Upvotes: 2

ConstOrVar
ConstOrVar

Reputation: 2085

Let's do it step by step. (All code will be in kotlin)

First of all, you have

class NetworkDataSource(val url: String, val method: String)

and need dagger module, which is responsible to create it

@Module
class NetworkModule {
    @Provides
    fun dataSource(@Named("url") url: String, 
                   @Named("method") method: String): NetworkDataSource {
        return NetworkDataSource(url, method)
    }
}

after that you need dagger component

@Component(modules = [NetworkModule::class])
interface NetworkComponent {
    fun inject(service: Service)
    @Component.Builder
    interface Builder {
        @BindsInstance
        fun url(@Named("url") url: String): Builder
        @BindsInstance
        fun method(@Named("method") method: String): Builder
        fun build(): NetworkComponent
    }
}

Now you can inject it inside Service

class Service : IntentService {
    @Inject
    lateinit var netWorkDataSource: NetworkDataSource
    override protected fun onHandleIntent(workIntent: Intent) {
        val url = workIntent.getStringExtra("url")
        val method = workIntent.getStringExtra("method")
        DaggerNetworkComponent.builder()
            .url(url)
            .method(method)
            .build()
            .inject(this)
    }
}

That's it.

Upvotes: 1

Related Questions