Dagger 2, How to add interface in dependency - java

I just started working with dagger 2. I have created dependency graph for application level dependency. Now that i wanted to create dependency that are required for a specific activity So i created another Component for activity then i created the Module and scope for that component. Now that when i am done writing all the code i build the project but i get compiler error which i am unable to solve.
Here is what i am doing.
#FeedsCatalogActivityScope
#Component(modules = FeedsCatalogActivityModule.class, dependencies = FeederApplicationComponent.class)
//My activity requires Catalog adapter so i am creating dependency for that
public interface FeedsCatalogActivityComponent {
CatalogAdapter getCatalogAdapter();
}
Here is the Module
#Module
public class FeedsCatalogActivityModule {
private final SelectedInterfaceListener selectedInterfaceListener;
public FeedsCatalogActivityModule(SelectedInterfaceListener selectedInterfaceListener) {
this.selectedInterfaceListener = selectedInterfaceListener;
}
#Provides
#FeedsCatalogActivityScope
public CatalogAdapter catalogAdapter(Picasso picasso, SelectedInterfaceListener mSelectesInterfaceListener) {
return new CatalogAdapter(picasso, mSelectesInterfaceListener);
}
}
Here is the scope
#Scope
public #interface FeedsCatalogActivityScope {
}
So now when i build i get this error
/Users/Zeeshan/Desktop/personal/Feeder/app/src/main/java/io/droidninja/feeder/FeederApplication.java
Error:(10, 31) error: cannot find symbol class DaggerFeederApplicationComponent
/Users/Zeeshan/Desktop/personal/Feeder/app/src/main/java/io/droidninja/feeder/ui/activities/FeedsCatalogActivityComponent.java
Error:(13, 20) error: io.droidninja.feeder.ui.adapters.SelectedInterfaceListener cannot be provided without an #Provides-annotated method.
io.droidninja.feeder.ui.adapters.SelectedInterfaceListener is injected at
io.droidninja.feeder.ui.activities.FeedsCatalogActivityModule.catalogAdapter(…, mSelectesInterfaceListener)
io.droidninja.feeder.ui.adapters.CatalogAdapter is provided at
io.droidninja.feeder.ui.activities.FeedsCatalogActivityComponent.getCatalogAdapter()
Error:Execution failed for task ':app:compileDebugJavaWithJavac'.
Compilation failed; see the compiler error output for details.
What i am understanding is that i have problem in FeedsCatalogActivityModule. How should i provide SelectedInterfaceListener? It is a interface.
P.S i am new to dagger2 i just get started with it.

According to your code, you are suppose to pass an instance of SelectedInterfaceListener to FeedsCatalogActivityModule when your are building FeedsCatalogActivityComponent inside the activity that uses it.
in your activity create a method that setup the component like this
private void initializeInjector() {
FeedsCatalogActivityComponent feedsCatalogActivityComponent = DaggerFeedsCatalogActivityComponent.builder()
.feederApplicationComponent(feederApplicationComponent()/* create this method and it should return a correctly built FeederApplicationComponent */ )
.feedsCatalogActivityModule(
new FeedsCatalogActivityModule(
new SelectedInterfaceListener()
/* pass in the instance of SelectedInterfaceListener here */ )
).build();
}
And then remember to change the Module provides method to
#Provides
#FeedsCatalogActivityScope
public CatalogAdapter catalogAdapter(Picasso picassor) {
return new CatalogAdapter(picasso, this.selectedInterfaceListener);
}

Well, you must somehow provide an implementation for that interface. Take a look at:
#Binds
#Provides
Binds vs. Provides
Example
#Binds
abstract SelectedInterfaceListener provideDsListPresenter(SelectedInterfaceListenerImpl impl);

Related

Google Guice ProvisionException: Unable to provision. No implementation was bound

New to DI and guice..
I want to use a service (StoreLevelClient) This is a class defined by other team.
I inject this class in my main file like this:
class ClientAccessor {
companion object {
private val LOGGER = KotlinLogging.logger { }
}
private val myStoreLevelClient: StoreLevelClient =
Guice.createInjector(ServiceModule()).getInstance(StoreLevelClient::class.java)
And made a module file for the StoreLevelClient like below:
class ServiceModule : AbstractModule() {
#Provides
#Singleton
fun getClient(myServiceClient : KasServiceClient): StoreLevelClient {
return StoreLevelClient(myServiceClient, AppConfigObject.trackedDocument, AppConfigObject.appConfigFallback)
}
It gave me errors:
Caused by: com.google.inject.ProvisionException: Unable to provision, see the following errors:
3
2022-05-20T18:27:50.800-07:00
1) No implementation for com.kasservice.KasServiceClient was bound.
4
2022-05-20T18:27:50.800-07:00
while locating com.kasservice.KasServiceClient
5
2022-05-20T18:27:50.800-07:00
for the 1st parameter of com.myservice.dependency.ServiceModule.getClient
The KasServiceClient is also from other's
So I #Provides it in the ServiceModule as well:
#Provides
#Singleton
fun getService(
cloudAuthCredentialVisitor: CloudAuthDefaultCredentialsVisitor,
metricsAwareCallVisitor: MetricsAwareCallVisitor,
#Named(BINGBONG_SERVICE_CLIENT_RETRY_STRATEGY)
retryStrategy: RetryStrategy<*>
): KasServiceClient {
val domain = AppConfig.findString(DOMAIN)
val realm = AppConfig.getRealm().name()
val qualifier = "$domain.$realm"
return ClientBuilder()
.remoteOf(KasServiceClient::class.java)
.withConfiguration(qualifier)
.withCallVisitors(cloudAuthCredentialVisitor, metricsAwareCallVisitor, CallAttachmentVisitor(Calls.retry(retryStrategy)))
.newClient()
}
But it gave me errors like below:
Could not find a suitable constructor in com.amazon.coral.client.cloudauth.CloudAuthDefaultCredentialsVisitor. Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument constructor that is not private.
Could not find a suitable constructor in com.amazon.metrics.declarative.client.MetricsAwareCallVisitor. Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument constructor that is not private.
The CloudAuthDefaultCredentialsVisitor and MetricsAwareCallVisitor are use #Provides and instantiate already.
So I don't know why guice can't find them...??
Any idea for this?? I wonder I have some mistake when using Guice. But I have hard time to debug and find
Solved the problem by changing the way of inject:
Instead use:
class ClientAccessor {
companion object {
private val LOGGER = KotlinLogging.logger { }
}
private val myStoreLevelClient: StoreLevelClient =
Guice.createInjector(ServiceModule()).getInstance(StoreLevelClient::class.java)
Tried this:
class ClientAccessor #Inject constructor(private val myStoreLevelClient: StoreLevelClient){
companion object {
private val LOGGER = KotlinLogging.logger { }
}
Reason:
use #Inject instead of using the createInjector manually on particular modules, let guice inject it for us. When I tried to directly use createInjector in instantiating in my code, it will only lookup the specified module and not able to load other modules.

Dagger 2 unable to do constructor injection

I have an app with activities and fragments with dependencies injected via dagger 2
I am able to do field injection in activities and fragments but not able to do constructor injection in other classes.
Here's my relevant code
#Module
public abstract class MainFragmentProvider {
#ContributesAndroidInjector(modules = HomeFragmentModule.class)
abstract HomeFragment provideHomeFragmentFactory();
}
and
#Module
public class HomeFragmentModule {
...
#Provides
static HomePresenter provideHomePresenter(HomeView homeView, HomeInteractor homeInteractor) {
return new HomePresenter(homeView, homeInteractor);
}
How can I write code so that I can get dependencies directly in HomePresenter by constructor injection instead of writing provideMethods in module. I am doing this because every time I want to change the constructor arguments in this case, I need to change the module code as well.
How can I do something like this in HomePresenter's constructor?
#Inject
public HomePresenter(HomeView homeView, HomeInteractor homeInteractor) {
this.homeInteractor = homeInteractor;
this.homeView = homeView;
}
To inject constructor, Dagger has to know where to get parameters passed to it i.e.
you have to provide
HomeView homeView, HomeInteractor homeInteractor
So create also methods for providing other dependencies:
#Provides
static HomeView provideHomeView() {
return ...
}
#Provides
static HomeInteractor provideHomeInteractor() {
return ...
}
I don't know much about the android extensions for dagger 2 but as far as I know there are two ways to achieve the result you are looking for.
In the relevant component you can specify a method with your type:
interface SomeComponent {
HomePresenter presenter(); // Method name does not matter here, only the type
}
and access it like this
class Home {
HomePresenter presenter;
void initialize() { //This could be your onCreate or wherever you typically inject
presenter = getSomeComponent().presenter();
}
}
or you can request it if you specify an inject method for your Home object:
interface SomeComponent {
void inject(Home home);
}
class Home {
#Inject HomePresenter presenter;
void initialize(){
getSomeComponent().inject(this);
}
}
In both cases you must ensure your Component includes the appropriate Modules.

Dagger 2 Object cannot be provided without an #Inject constructor

I'm fairly new to Dagger 2 and I have the following classes.
I have 2 modules:
DaoSessionModule
#Module
public class DaoSessionModule {
private DaoSession daoSession;
private Context context;
public DaoSessionModule(Context context) {
this.context = context;
if(daoSession == null) {
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this.context, "my_pocket");
Database db = helper.getWritableDb();
daoSession = new DaoMaster(db).newSession();
}
}
#Provides
LanguageDao providesLanguageDao() {
return daoSession.getLanguageDao();
}
#Provides
CategoryDao providesCategoryDao() {
return daoSession.getCategoryDao();
}
}
and GlobalPrefModule
#Module
public class GlobalPrefModule {
private GlobalPref globalPerf;
public GlobalPrefModule(GlobalPref globalPerf) {
this.globalPerf = globalPerf;
}
#Provides
public GlobalPref providesGlobalPref() {
return this.globalPerf;
}
}
and their components go as:
#Singleton
#Component(modules = {DaoSessionModule.class})
public interface DaoSessionComponent {
void inject(SplashActivity activity);
}
and
#Singleton
#Component(modules = {GlobalPrefModule.class })
public interface GlobalPrefComponent {
void inject(SplashActivity activity);
}
and I build both in my application class:
daoSessionComponent = DaggerDaoSessionComponent.builder()
.daoSessionModule(new DaoSessionModule(this))
.build();
globalPrefComponent = DaggerGlobalPrefComponent.builder()
.globalPrefModule(new GlobalPrefModule(new GlobalPref()))
.build();
and inject them in my splash activity:
public class SplashActivity extends BaseActivity {
#Inject
LanguageDao languageDao;
#Inject
GlobalPref globalPerf;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initInjections();
}
private void initInjections() {
ZoopiApplication.app().getDaoSessionComponent().injectDao(this);
ZoopiApplication.app().getGlobalPrefComponent().injectGlobalPref(this);
}
}
now the problem I'm facing is that if I only inject DaoSession in my splash and comment out the GlobalPref impl it'll simply work but the moment I add GlobalPref along side with Daosession it fails to build and gives me the following error messages:
Error:(8, 52) error: cannot find symbol class DaggerDaoSessionComponent
Error:(9, 52) error: cannot find symbol class DaggerGlobalPrefComponent
Error:(16, 10) error: mypocket.com.zoopi.GlobalPref cannot be provided without an #Inject constructor or from an #Provides- or #Produces-annotated method.
mypocket.com.zoopi.GlobalPref is injected at
mypocket.com.zoopi.activities.SplashActivity.globalPerf
mypocket.com.zoopi.activities.SplashActivity is injected at
mypocket.com.zoopi.dagger.dagger2.components.DaoSessionComponent.injectDao(activity)
Error:(16, 10) error: mypocket.com.zoopi.models.LanguageDao cannot be provided without an #Inject constructor or from an #Provides- or #Produces-annotated method.
mypocket.com.zoopi.models.LanguageDao is injected at
mypocket.com.zoopi.activities.SplashActivity.languageDao
mypocket.com.zoopi.activities.SplashActivity is injected at
mypocket.com.zoopi.dagger.dagger2.components.GlobalPrefComponent.injectGlobalPref(activity)
and both generated classes DaggerDaoSessionComponent and DaggerGlobalPrefComponent are generated in the build foloder.
What could be the reason that I can't inject both objects into the same activity?
Injection has to be done from one component, and one component only.
It should be easy to see that the error message states that the object that can't be provided is the one you try to inject by the other component.
Dagger does not do "half" injections and one component has to inject all the fields. If partial injection would be possible you could end up with inconsistent states, since Dagger has no way of knowing how, when, or where you'd inject the rest of the fields. In short, it's just not possible. You'll have to use a single component.
but then I'll have many more modules soon and I don't know if it's a good idea to have one component to handle all modules...
That's okay. You will end up with quite a bunch of modules and possible quite a few components, depending on your setup. Make sure to use SubComponents where appropriate and you can even have modules include other modules, if you have big dependency groups split over multiple modules.

How to inject dependencies if your module itself take parameters in as a constructor?

I have just learned about Dependency injection (DI) and I am beginning to like it. To inject dependencies I am using Google Guice framework. Everything was running conceptually fine but while writing a module a thought came to my mind that what if my module require dependencies as a constructor, after all, it is just a class extending AbstractModule.
So, basically, I have 3 modules as a whole.
Environment Module
public class EnvModule extends AbstractModule {
#Override
protected void configure() {
install(new Servicemodule());
}
}
ServiceModule
public class ServiceModule extends AbstractModule {
private final boolean isEnabled;
#Override
protected void configure() {
if (isEnabled) {
install (new ThirdModule());
}
}
ThirdModule (It does not take any arguments in any constructor and have some bindings of its own)
Basically, the variable in the service module defines whether my application needs to install the third module or not. And that variable is defined in an application configuration file. So how do I inject that variable in the ServiceModule? As the field is final, setter injection is not possible, is there a way to use construction injection or field injection to inject the value.
I see the following options:
Use system variable:
ServiceModule() {isEnabled = System.getProperty("isThirdModuleEnabled")};
Read the config file directly in the ServiceModule() constructor
Use #Provides:
class ServiceModule ... {
#Provide #Singleton ThirdModuleParam getThirdModuleParam(...) {
//read the config file
ThirdModuleParam res = new ThirdModuleParam();
res.setIsEnabed(...);
return res;
}
}
class ThirdModule {
#Provide SomeThirdModuleClass getIt(ThirdModuleParam param) {
return param.isEnabled() ? new SomeThirdModuleClass() : null;
}

Play 2.5.12 Java - Missing HttpContext when replacing deprecated GlobalSettings.OnStart

I'm trying to migrate from Play 2.3 to 2.5 but I have an issue on replacing the GlobalSettings.OnStart method.
In my old Global class that was extending GlobalSettings, in the onStartMethod I was initialising the Global Config and reading basic things from the DB.
I have created a new class and I m moving the code from the onStart method to the constructor of this one as mentioned in the doc.
https://www.playframework.com/documentation/2.5.x/GlobalSettings
https://www.playframework.com/documentation/2.4.x/PluginsToModules
I'm doing the binding as eagerSingleton from an AbstractModule. The class is correctly started but I keep getting the error:
GlobalBootstrapModule.configure:20 - Binding
OnStartConfig.:35 - Global...on start
Caused by: java.lang.RuntimeException: No EntityManager bound to this thread. Try wrapping this call in JPAApi.withTransaction, or ensure that the HTTP context is setup on this thread.
This is my code:
New class to replace onStart
public class OnStartConfig implements StartConfigInterface{
private final JPAApi jpaApi;
#Inject
public OnStartConfig(Application application, JPAApi jpa){
this.jpaApi = jpa;
Logger.debug("Global...on start");
jpaApi.withTransaction( ()-> {
GlobalConfiguration.aggregationCriteria = AggregationCriterion.getAll();
});
}
The interface I'm extending is just an empty placeholder.
AbstractModule used for the binding:
public class GlobalBootstrapModule extends AbstractModule {
#Override
protected void configure() {
Logger.debug("Binding");
bind(StartConfigInterface.class).to(OnStartConfig.class).asEagerSingleton();
}
}
And I have enabled the module in the application.conf file:
play {
modules {
enabled += modules.GlobalBootstrapModule
}
}
I suppose the problem is due to the lack of HttpContext. Where can I grab it from during the initialisation?
Any help would be hugely appreciated

Categories

Resources