I'm trying to refactor my code so I was thinking about Dagger2 to solve my issues. I've created AppComponent to store all of my Singletons:
#AppScope
#Component(
modules = {
AppModule.class,
// more here...
}
)
public interface AppComponent {
Context exposeContext();
CmdComponent newCmdComponent(CmdModule module);
// ... few injections here
}
My AppModule:
#Module
public class AppModule {
private Context context;
public AppModule(Context context) {
this.context = context;
}
// ... provide appContext etc.
#AppScope #Provides
MyClass provideMyClass() {
Log.i("DAGGER", "provideMyClass: ");
return new MyClass();
}
}
I inject this in my Application class:
public class App extends Application {
private static AppComponent component;
#Override
public void onCreate() {
super.onCreate();
component = DaggerAppComponent.builder()
.appModule(new AppModule(app))
.build();
}
public static AppComponent getAppComponent() {
return component;
}
}
Then I have my subcomponent CmdComponent with different #Scope
#CmdScope
#Subcomponent(
modules = {
CmdModule.class
}
)
public interface CmdComponent {
void inject(Cmd cmd);
}
Now I'm injecting dependancies into my Cmd instance like:
#Inject MyClass myClass;
public Cmd() {
App.getAppComponent()
.newCmdComponent(new CmdModule())
.inject(this);
}
Unfortunetly log: Log.i("DAGGER", "provideMyClass: "); and log inside MyClass constructor are shown multiple times... so I get new instance of MyClass every time. How to tell Dagger to give me the same instance (created once) every time?
Ok. I solve my issue. The solution is simple. My AppScope was created wrong. For some reason, I thought that annotation works like inheritance.
My custom annotation was like:
#Singleton
#Retention(RetentionPolicy.RUNTIME)
public #interface AppScope {
}
and Dagger thought that my component is unscoped.. It should be like:
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface AppScope {
}
How about the .newCmdComponent(new CmdModule()). Why do you do that, creating a new module every time? The component already has a module attached, you've set that on creation of the component.
Related
I'm new to Hilt (and dagger) and I'm not sure if the title actually is my issue but its a starting point,
I'm trying to use hilt on a recycler view adapter, it takes 4 things: an interface, glide(image loader), a type class I own, and a String identifier, Ive set up the components and modules for it and added a builder for the identifier, this seems to work as I can annotate the adapters constructor and it compiles, but I need access to the adapter in my fragment, so I thought I could just annotate the adapter field with #Inject but when i add this it fails (error below), so heres a snippet of my fragment
#AndroidEntryPoint
public class CardHolderFragment extends Fragment implements ItemTouchListener,
SwipeRefreshLayout.OnRefreshListener {
#Inject
public CardAdapter cardAdapter;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
DaggerCardAdapterComponent.builder()
.cardAdapterDependencies(() -> layoutIdentifier)
.build()
.inject(this);
DaggerSentenceViewModelComponent.builder()
.context(getActivity())
.appDependencies(() -> args)
.build()
.inject(this);
and the error I get is
/Users/martinseal/StudioProjects/SimpleAAC/app/src/main/java/com/xxx/SentenceViewModelComponent.java:14:
error: [Dagger/MissingBinding]
com.xxx.ItemTouchListener cannot be provided without an #Provides-annotated method.
public interface SentenceViewModelComponent {
^
com.xxx.ItemTouchListener is injected at
com.xxx.CardAdapter(onItemTouchListener, …)
com.xxx.CardAdapter is injected at
com.xxx.CardHolderFragment.cardAdapter
com.xxx.CardHolderFragment is injected at
com.xxx.SentenceViewModelComponent.inject(com.xxx.CardHolderFragment)
/Users/martinseal/StudioProjects/SimpleAAC/app/src/main/java/
com/xxx/CardAdapterComponent.java:13: error:
[Dagger/MissingBinding] java.lang.String cannot be provided without an #Inject constructor or an #Provides-annotated method.
So I can see that its telling me there is a problem with it not having a provides method for the ItemTouchListener (which I did add to no avail) for SentenceViewModelComponent and CardAdapterComponent but why would they be related in this dependency graph? They are both injected into the same CardHolderFragment, heres my CardAdapterComponent:
#Singleton
#Component(dependencies = CardAdapterDependencies.class, modules = {TypeFactoryModule.class, ItemTouchListenerModule.class, GlideModule.class})
public interface CardAdapterComponent {
void inject(CardHolderFragment cardHolderFragment);
#Component.Builder
interface Builder {
Builder cardAdapterDependencies(CardAdapterDependencies cardAdapterDependencies);
CardAdapterComponent build();
}
}
as you can see it relies on some modules first type factory
#Module
#InstallIn(ActivityComponent.class)
public abstract class TypeFactoryModule {
#Binds
abstract TypeFactory bindTypeFactory(TypeFactoryForList typeFactoryForList);
}
then the item touch listener
#Module
#InstallIn(ActivityComponent.class)
public abstract class ItemTouchListenerModule {
#Binds
abstract ItemTouchListener bindItemTouchListener(CardHolderFragment cardHolderFragment);
}
and then glide
#Module
#InstallIn(ApplicationComponent.class)
public class GlideModule {
#Singleton
#Provides
public static RequestManager provideGlideRequestManager(#ApplicationContext Context context) {
return Glide.with(context);
}
}
and then the dependencies for the component
#EntryPoint
#InstallIn(ApplicationComponent.class)
public interface CardAdapterDependencies {
#LayoutIdentifier String layoutIdentifier();
}
LayoutIdentifier is a qualifier with a RetentionPolicy of RUNTIME which qualifies my layout identifier module
#Module
#InstallIn(ApplicationComponent.class)
public class LayoutIdentifierModule {
#Singleton
#Provides
#LayoutIdentifier
public String provideLayoutIdentifier(){ return Constants.MAIN_IDENTIFIER; };
}
my SentenceViewModelComponent doesn't rely on any of these
#Singleton
#Component(dependencies = SentenceViewModelDependencies.class)
public interface SentenceViewModelComponent {
void inject(CardHolderFragment cardHolderFragment);
#Component.Builder
interface Builder {
Builder context(#BindsInstance Context context);
Builder appDependencies(SentenceViewModelDependencies sentenceViewModelDependencies);
SentenceViewModelComponent build();
}
}
but does have a dependency on SentenceViewModelDependencies
#EntryPoint
#InstallIn(ApplicationComponent.class)
public interface SentenceViewModelDependencies {
#SentenceTypeAndDescription String[] sentenceTypeAndDescription();
}
which has a qualifier called SentenceTypeAndDescription which annotates a module SentenceTypeAndDescriptionModule
#Module
#InstallIn(ApplicationComponent.class)
public class SentenceTypeAndDescriptionModule {
#Singleton
#Provides
#SentenceTypeAndDescription
public String[] provideSentenceTypeAndDescription(){ return new String[]{Constants.QUICKS, "PRONOUNS"}; };
}
I fail to see how the error is related other than that they both inject this same fragment, is there something Im missing maybe around sub components that I should be using here? any help would be appreciated
I study Dagger 2 and try to understand how I can attach it to an already existing project. Now I faced with the following problem with injectiing dependencies into BroadcastReceivers. I can't use DaggerReceiver because my receivers already extends another class. To use AndroidInjection inside BroadcastReceivers, I should inject DispatchingAndroidInjector to Application class. I don't want to include module with AndroidInjection contributors to main dependency graph so, how I understand, I should place it to dedicated component for Application class, but because of this duplication of Singletons occurs.
Here is my example (GitHub repo)
Classes to provide dependencies to MainActivity and other classes
AppModule.class
#Module
public class AppModule {
Application mApplication;
public AppModule(Application application) {
mApplication = application;
}
#Singleton
#Provides
Application providesApplication() {
return mApplication;
}
}
UtilityModule.class
#Module
public class UtilityModule {
#Singleton
#Provides
FirstUtilityClass providesFirstUtility(Application app) {
return new FirstUtilityClass(app);
}
#Singleton
#Provides
SecondUtilityClass providesSecondUtility(Application app) {
return new SecondUtilityClass(app);
}
}
MainComponent.class
#Singleton
#Component(modules = { AppModule.class, UtilityModule.class })
public interface MainComponent {
void inject(MainActivity activity);
}
Classes to provide DispatchingAndroidInjector to Application class
ReceiversModule.class
#Module
public abstract class ReceiversModule {
#ContributesAndroidInjector
abstract FirstReceiver contributesFirstReceiver();
#ContributesAndroidInjector
abstract SecondReceiver contributesSecondReceiver();
}
ReceiversComponent.class
#Singleton
#Component(modules = { AppModule.class, UtilityModule.class, ReceiversModule.class })
public interface ReceiversComponent {
void inject(MyApplication application);
}
Injecting...
MyApplication.class
public class MyApplication extends Application implements HasBroadcastReceiverInjector {
#Inject DispatchingAndroidInjector<BroadcastReceiver> mInjector;
#Inject FirstUtilityClass mFirstUtility;
#Inject SecondUtilityClass mSecondUtility;
MainComponent mMainComponent;
#Override
public void onCreate() {
AppModule appModule = new AppModule(this);
mMainComponent = DaggerMainComponent.builder().appModule(appModule).build();
// I should build new component only for Application class, because DispatchingAndroidInjector not required for another targets
DaggerReceiversComponent.builder().appModule(appModule).build().inject(this); // Here created new scope
super.onCreate();
// do something
mFirstUtility.doFistThings(getClass().getSimpleName());
mSecondUtility.doSecondThings(getClass().getSimpleName());
}
public MainComponent getMainComponent() {
return mMainComponent;
}
#Override
public AndroidInjector<BroadcastReceiver> broadcastReceiverInjector() {
return mInjector;
}
}
MainActivity.class
public class MainActivity extends AppCompatActivity {
#Inject FirstUtilityClass mFirstUtility;
#Inject SecondUtilityClass mSecondUtility;
#Override
protected void onCreate(Bundle savedInstanceState) {
((MyApplication) getApplication()).getMainComponent().inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Do something
mFirstUtility.doFistThings(getClass().getSimpleName());
mSecondUtility.doSecondThings(getClass().getSimpleName());
// Sending broadcasts here
}
}
Logcat output
FirstUtilityClass1: doing first things from MyApplication...
SecondUtilityClass1: doing second things from MyApplication...
FirstUtilityClass2: doing first things from MainActivity...
SecondUtilityClass2: doing second things from MainActivity...
FirstUtilityClass1: doing first things from FirstReceiver...
SecondUtilityClass1: doing second things from SecondReceiver...
How you see in log, Apllication and MainActivity receives different instances of utility classes. I had an idea to make ReceiversComponent as a subcomponent of MainComponent. But ReceiversModule is abstract and I cant add it dinamicaly to existed component instance.
MainComponent.class
#Singleton
#Component(modules = { AppModule.class, UtilityModule.class })
public interface MainComponent {
ReceiversComponent with(ReceiversModule module);
void inject(MainActivity activity);
}
ReceiversComponent.class
#Subcomponent(modules = { ReceiversModule.class })
public interface ReceiversComponent {
void inject(MyApplication application);
}
And then in MyApplication:
// It does not work because I can't create ReceiverModule instance :(
mMainComponent.with(new ReceiversModule()).inject(this);
How can I use the ReceiversComponent as a subcomponent of MainComponent?
It's clear to me that I'm doing something wrong, but I just can't understand where. Maybe there is another way to solve my problem?
I just found the answer!
Since my ReceiversModule class has a default constructor without parameters, I can skip it as parameter when describing the subcomponent in MainComponent interface:
MainComponent.class
#Singleton
#Component(modules = { AppModule.class, UtilityModule.class })
public interface MainComponent {
ReceiversComponent withReceiversModule(); // Here is the reference to the subcomponent with abstract module
void inject(MainActivity activity);
}
MyApplication.class
#Override
public void onCreate() {
AppModule appModule = new AppModule(this);
mMainComponent = DaggerMainComponent.builder().appModule(appModule).build();
mMainComponent.withReceiversModule().inject(this); // It's works!!! :)
super.onCreate();
// do something
mFirstUtility.doFistThings(getClass().getSimpleName());
mSecondUtility.doSecondThings(getClass().getSimpleName());
}
Maybe someone will think it is a trifle, but it took me several days to reach it. I'm very happy, because my code has become a little cleaner. Thanks to all!
I want to inject a custom class as an dependency in different service classes, but I don't get it work. It always ends with a NPE. Here is my example (simple Java SE) ...
My Main class to get everything running
public class Main {
public static void main(String[] args) throws IOException, TimeoutException {
MyApplication MyApp = new MyApplication();
MyApp.execute();
}
}
MyApplication class
public class MyApplication {
private MyApplicationComponent appComponent;
#Inject FooService fooService;
#Inject BarService barService;
#Inject BazService bazService;
public MyApplication() {
component = DaggerMyApplicationComponent.builder().build();
component.inject(this);
}
public void execute() {
fooService.doStuff();
barService.doStuff();
// this will happen in the FooService construct, see below
// bazService.doStuff();
}
}
Component and Module classes as defined in Dagger, without using it the #Inject constructor way
#Singleton
#Component(modules = {MyApplicationModule.class})
public interface MyApplicationComponent {
void inject(MyApplication application);
}
#Module
public class MyApplicationModule {
#Singleton
#Provides
FooService provideFooService() {
return new FooService();
}
#Singleton
#Provides
BarService provideBarService() {
return new BarService();
}
#Provides
BazService provideBazService() {
return new BarService();
}
}
Using the MyApplicationModule and MyApplicationComponent to provide needed dependencies works within the Main.class. I also want to use the BazService within the FooService class. Therefore I use the #Inject way to define it as a dependency with FooService.class.
Using #Inject of BazService within the FooService.class
public class FooService {
#Inject BazService bazService;
public FooService(){}
public doStuff(){
bazService.doStuff();
}
}
Running the Main.class always ends within a NPE, due to undefined bazService in the FooSerivce class. I don't think, that I missed to add an annotation anywhere. I think Dagger will not work this way ... any ideas?
FooService expects bazService to be injected though field injection but you are calling bazService before this happens. If you want to call bazService.doStuff() in FooService's constructor you'll have to use constructor injection.
I'm trying to follow the same example as in here by creating a Repository pattern with DI.
The problem is that I get the following error:
"Error:(16, 20) error: #mvp.model.di.scope.Local
mvp.model.repository.local.GameLocalDataSource cannot be provided
without an #Provides-annotated method. #mvp.model.di.scope.Local
mvp.model.repository.local.GameLocalDataSource is injected at
mvp.model.repository.GameRepository.(gameLocalDataSource, …)
mvp.model.repository.GameRepository is provided at
mvp.model.di.component.RepositoryComponent.getGameRepository()"
Here's the code related to the app:
public class GameApplication extends Application {
private RepositoryComponent repositoryComponent;
#Override
public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
// Normal app init code...
repositoryComponent = DaggerRepositoryComponent.builder()
.applicationModule(new ApplicationModule((getApplicationContext())))
.build();
}
public RepositoryComponent getRepositoryComponent() {
return repositoryComponent;
}
}
This is my RepositoryComponent:
#Singleton
#Component(modules = {RepositoryModule.class, ApplicationModule.class})
public interface RepositoryComponent {
GameRepository getGameRepository();
}
Here's the RepositoryModule:
#Module
public class RepositoryModule {
#Singleton
#Provides
#Local
GameDataSource provideLocalDataSource(Context context) {
return new GameLocalDataSource(context);
}
#Singleton
#Provides
#Remote
GameDataSource provideRemoteDataSource() {
return new GameRemoteDataSource();
}
}
And finally, the ApplicationModule:
#Module
public final class ApplicationModule {
private Context context;
public ApplicationModule(Context context) {
this.context = context;
}
#Provides
Context providesContext() {
return context;
}
}
Here's most of my GameRepository class:
#Singleton
public class GameRepository implements GameDataSource {
private GameDataSource remoteDataSource;
private GameDataSource localDataSource;
#Inject
public GameRepository(#Local GameLocalDataSource gameLocalDataSource, #Remote GameRemoteDataSource gameRemoteDataSource) {
remoteDataSource = gameRemoteDataSource;
localDataSource = gameLocalDataSource;
}
Also, as in the mentioned example, I created a couple of scopes, #Local and #Remote since my two data sources have the same type and Dagger needs to differentiate them.
#Qualifier
#Documented
#Retention(RetentionPolicy.RUNTIME)
public #interface Local {
}
The rest of the code I have related to dagger, is just the #Inject in the constructors where I want to inject my dependencies.
Also, the DaggerRepositoryComponent is never generated in the GameApplication class.
Thanks a lot for the help in advance!
GameLocalDataSource cannot be provided without an #Provides-annotated method
Somewhere in the code you are trying to #Inject GameLocalDataSource, but you have specified in your module how to provide GameDataSource, not GameLocalDataSource.
...
GameDataSource provideLocalDataSource(Context context) {
return new GameLocalDataSource(context);
}
...
Either ask Dagger to inject GameDataSource, or describe Dagger how to provide GameLocalDataSource.
...
GameLocalDataSource provideLocalDataSource(Context context) {
return new GameLocalDataSource(context);
}
...
I have been using using dagger 2 in my project lately,
the problem is when I try to build my project, the presenter in my login
activity which injected like below is null,
and when I try to build the project
presenter cannot be provided without an #Inject constructor or from an #Provides- or #Produces-annotated method...
I don't understand what have I done wrong??, please someone help me with this,
thanks in advance.
Here is my Login Activity, the presenter here is null, which shows that, I've not injected it properly
#Inject
LoginPresenter presenter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
InjectHelper.getRootComponent().injectPresenter(this);
presenter.setProgressBarVisiblity(View.INVISIBLE);
}
this is my presenter module
#Module
public class PresenterModule {
private final LoginActivity activity;
public PresenterModule(LoginActivity activity) {
this.activity = activity;
}
#Provides
#Singleton
public LoginActivity providesView() {
return activity;
}
}
#Provides
#Singleton
public LoginPresenter providesPresenter()
{
return new LoginPresenter();
}
}
this inject helper class
public class InjectHelper {
private static RootComponent sRootComponent;
static {
initModules();
}
private static void initModules() {
sRootComponent = getRootComponentBuilder().build();
}
public static DaggerRootComponent.Builder getRootComponentBuilder() {
return DaggerRootComponent.builder();
}
public static RootComponent getRootComponent() {
if (sRootComponent == null) {
initModules();
}
return sRootComponent;
}
}
this is root component class
#Singleton
#Component(modules = {
PresenterModule.class
})
public interface RootComponent {
void injectLoginView(LoginPresenter loginPresenter);
}
you need to inform the dagger which views want to use injection, in your component.
you must change the inject function code in your component to below:
void inject(LoginActivity activity);
for showing dagger what you want, you need to use #dagger annotation NOT by sending it as inject function in component file. as you did properly:
#Inject
LoginPresenter presenter;
Dagger will search for a variable of type LoginPresenter in your module and finds the proper provider method using the type.
what you put in your component as a argument for "inject" function tells the Dagger what view you are going to do injection in (NOT what you want to inject)
#Singleton
#Component(modules = {PresenterModule.class})
public interface RootComponent {
void inject(LoginActivity activity);
}