I'm following this tutorial to add Dagger 2 to my Android project.
After doing setup and creating the modules and components I can add the dependencies in an Activity like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_account);
ButterKnife.bind(this);
((AppController) getApplication()).getNetComponent().inject(this);
}
I am struggling in how to inject dependencies in a Fragment and IntentService?
public class FragmentBrandList extends ListFragment {
}
In this class which #Override method should I request injection in and what will be the code for this?
In this class which #Override method i should use and what will be the code to add dependency in fragment?
The correct place to call injection inside a Fragment is onAttach(Context context). This is stated in the where to inject section of the Dagger 2 user guide here
#Override
public void onAttach(Context context) {
((AppController) context.getApplicationContext()).getNetComponent().inject(this);
super.onAttach(context);
}
The correct place to call injection inside a Service is onCreate()
#Override
public void onCreate() {
((AppController) getApplication()).getNetComponent().inject(this);
super.onCreate();
}
Note that in both cases the request for injection comes before the call to super.onCreate(). The Dagger user guide explains it like this:
It is crucial to call AndroidInjection.inject() before super.onCreate() in an Activity, since the call to super attaches Fragments from the previous activity instance during configuration change, which in turn injects the Fragments. In order for the Fragment injection to succeed, the Activity must already be injected. For users of ErrorProne, it is a compiler error to call AndroidInjection.inject() after super.onCreate().
In other words:
The Activity super.onCreate() call re-attaches Fragments from a previous instance
This super call in cause Fragments to be re-injected (since Fragments are injected in onAttach)
Fragments should be injected after their Activity is injected, therefore request injection in your Activity before calling super.onCreate().
You can always check where to inject by looking at the relevant source code for the com.google.dagger:dagger-android classes like DaggerFragment and DaggerService. See the GitHub repo here
For your specific example, please make sure you have added the new injection sites to the NetComponent:
void inject(FragmentBrandList frag);
void inject(BrandListService service);
Step 1: Create your ApplicationModule
#Module
public class ApplicationModule {
private final DIApplication application;
public ApplicationModule(DIApplication application) {
this.application = application;
}
#Provides #Singleton
public DIApplication provideApplication() {
return application;
}
#Provides #Singleton
public DogModel provideDogModel() {
return new DogModelImpl("Scooby-doo");
}
}
Step 2: Create your ApplicationComponent:
#Singleton
#Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
void inject(DIApplication application);
void inject(BaseActivity activity);
void inject(BaseFragment fragment);
void inject(DogSyncService service);
}
Step 3: Create a DI Class:
public class DependencyInjector {
private static ApplicationComponent applicationComponent;
public static void initialize(DIApplication diApplication) {
applicationComponent = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(diApplication))
.build();
}
public static ApplicationComponent applicationComponent() {
return applicationComponent;
}
private DependencyInjector(){}
}
Final Step: Inject anywhere using:
DependencyInjector.applicationComponent()
Your question inspired me to create a Demo project that shows Activity, Fragment and Service injection using Dagger2. Here is the git: https://github.com/write2sv/AndroidDIDagger2/tree/master/app/src/main/java/work/shaggy/didemo
I did it using
((AppController) getActivity().getApplication()).getNetComponent().inject(this);
in Fragment.onCreate() method.
((MyApp) context.getApplicationContext()).getApplicationComponent().inject(MyFragment.this);
I added this in onAttach(Context context) method of the fragment.
You really just need to include the injector method for whatever you want to inject.
#Singleton
#Component
public interface AppComponent {
void inject(MainActivity activity);
void inject(FragmentBrandList fragmentBrandList);
}
You can use autodagger2 to avoid having to write all that boilerplate. I use this architecture quite often:
build.gradle
apt 'com.github.lukaspili.autodagger2:autodagger2-compiler:1.1'
compile 'com.github.lukaspili.autodagger2:autodagger2:1.1'
YourApp.java
#AutoComponent( modules = YourApp.YourAppModule.class )
public class YourApp extends Application {
private static YourApp instance;
private YourAppComponent component;
public YourAppComponent getComponent() {
return this.component;
}
#Override
public void onCreate() {
super.onCreate();
setupComponent();
}
private void setupComponent() {
component = DaggerYourAppComponent.builder()
.yourAppModule(new YourAppModule(instance))
.build();
}
#dagger.Module
public static class YourAppModule {
private YourApp app;
YourAppModule(YourAppApp application) {
this.app = application;
}
#Provides #AutoExpose(YourApp.class)
Application provideApplication() {
return app;
}
#Provides #AutoExpose(PoswalaApp.class)
Context provideContext() {
return app;
}
#Provides #AutoExpose(YourApp.class)
Retrofit provideApiAdapter() {
return ApiService.getServiceInstance();
}
}
}
YourActivity.java
#AutoComponent(
dependencies = YourApp.class,
modules = YourActivity.YourActivityModule.class
)
public class YourActivity extends BaseActivity implements YourActivityView {
private YourActivityComponent component;
#Inject MyPresenter presenter
// This is an abstract method from BaseActivity
#Override
protected void setupComponent(YourAppComponent appComponent) {
component = DaggerYourActivityComponent.builder()
.yourAppComponent(((YourApp) getApplication()).getComponent())
.yourActivityModule(new YourctivityModule(this))
.build();
}
#Override
protected MyPresenter getPresenter() {
return presenter;
}
#dagger.Module
public static class YourActivityModule {
private YourActivityView view;
YourActivityModule(YourActivityView view) {
this.view = view;
}
#Provides #AutoExpose(YourActivity.class)
YourActivityView provideView() {
return view;
}
// Your other dependencies
}
}
Quick explanation:
Your app's module will have to a "universal" dependency, but this way you can achieve using several modules for a class. You just need to customize the
#AutoComponent(
dependencies = YourApp.class,
modules = { YourActivity.YourActivityModule.class, YourFragment.YourFragmentModule.class }
)
block. You can add as many modules as you like using that syntax.
Hope this helps you
Related
SOLVED
I am new with Dagger 2 and I am trying to provide Activity Context to classes but without success. I search a lot but did not find any appropriate answer.
I can provide Application Context. But I also need to provide Activity Context as well and I don't know any good way to implement that.
I need to clarify that I am using Dagger for Android dependencies.
def dagger_version = "2.24"
implementation "com.google.dagger:dagger:$dagger_version"
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
implementation "com.google.dagger:dagger-android:$dagger_version"
implementation "com.google.dagger:dagger-android-support:$dagger_version"
annotationProcessor "com.google.dagger:dagger-android-processor:$dagger_version"
I also have only an AppComponent with the following code:
#Singleton
#Component(
modules = {
AndroidSupportInjectionModule.class,
ActivityBuildersModule.class,
AppModule.class,
ViewModelFactoryModule.class,
}
)
public interface AppComponent extends AndroidInjector<BaseApplication> {
SessionManager sessionManager();
#Component.Builder
interface Builder{
#BindsInstance
Builder application(Application application);
AppComponent build();
}
}
Except that I have a module for each of my activity but I didn't find a way to inject the Activity Context either for AppComponent or from a ActivityModule.
What is the right way to do that?
UPDATE
I finally found the right way to do that.
First I created a module for the class that I want to provide
#Module
public class AlertsModule {
#Provides
static Alerts provideAlerts(Activity activity){
return new Alerts(activity);
}
}
Then I go to the ActivityModules that I want to Inject that custom Class and do a Binding like that
#Module
public abstract class MainActivityModule {
...
#Binds
abstract Activity providesActivity(MainActivity activity);
...
}
And finally I just include the CustomClassModule in my ActivityBuildersModule where I use #ContributesAndroidInjector to provide my Activities.
#MainScope
#ContributesAndroidInjector(
modules = {
AlertsModule.class,
}
)
abstract MainActivity contributeMainActivity();
You can bind an instance of Activity in the same way you're currently binding an instance of Application, either by using an #Component.Builder or an #Component.Factory.
An example implementation would look something like this:
#Subcomponent(...)
interface ActivitySubcomponent {
#Subcomponent.Factory
interface Factory {
ActivitySubcomponent create(#BindsInstance MyActivity activity)
}
}
#Module(subcomponents = [ActivitySubcomponent.class])
class ApplicationModule {
....
}
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
...
((MyApplication) getApplication())
.applicationComponent
.activitySubcomponentFactory()
.create(this)
.inject(this)
}
}
#Module
class MainActivityModule(mActivity: MainActivity) {
var mActivity: MainActivity
init {
this.mActivity= mActivity
}
#Singleton
#Provides
fun getMainActivity(): MainActivity{
return mActivity
}
}
now add this module to your Component class and pass your activity to module constructor while using your component builder hope this may helps you.
#Named annotation helps us to differentiate the Context. We can differentiate the method context() in ActivityModule and ContextModule by adding the #Named annotation as below:
#Module
public class ActivityModule {
private final Context context;
ActivityModule(Activity context){
this.context = context;
}
#Named("activity_context")
#Provides
public Context context(){ return context; }
}
#Module
public class ContextModule {
private final Context context;
ActivityModule(Activity context){
this.context = context;
}
#Named("application_context")
#Provides
public Context context(){ return context.getApplicationContext(); }
Then, we tell Dagger to use the respective Context, as below:
#Module(includes = ContextModule.class)
public class OkHttpClientModule {
....
#Provides
public File file(#Named("application_context") Context context){
File file = new File(context.getCacheDir(), "HttpCache");
file.mkdirs();
return file;
}
....
}
Hope this helps.
I have two kind of fragment. One of them is ListFragment and the other one is ListWithFilterFragment that ListWithFilterFragment extend ListFragment. Also I have two presenter for these fragments that ListWithFilterPresenter extend ListPresenter. So I define separate module for presenter and separate component in Dagger. I want to inject correct presenter to these fragment but I got error
this is ListFragment:
public class ListFragment<V extends ListView> extends BaseFragment<V, ListPresenter<V>> implements ListView {
#Inject
ListPresenter presenter;
#NonNull
#Override
public ListPresenter createPresenter() {
DaggerListComponent.builder().build().inject((ListFragment<ListView>) this);
return presenter;
}
this is ListWithFilterFragment:
public class ListWithFilterFragment extends ListFragment<ListWithFilterView> implements ListWithFilterView {
#Inject
ListWithFilterPresenter presenter;
#NonNull
#Override
public ListWithFilterPresenter createPresenter() {
DaggerListWithFilterComponent.builder().build().inject(this);
return presenter;
}
}
this is ListComponent:
#Singleton
#Component(modules = {ListPresenterModule.class,
ListInteractorModule.class})
public interface ListComponent {
void inject(ListFragment<ListView> fragment);
}
this is ListWithFilterComponent:
#Singleton
#Component(modules = {ListWithFilterPresenterModule.class,
ListInteractorModule.class})
public interface ListWithFilterComponent {
void inject(ListWithFilterFragment fragment);
}
and my presenters modules
#Module
public class ListWithFilterPresenterModule {
#Provides #NonNull #Singleton
public ListWithFilterPresenter providePresenter(ListInteractor interactor) {
return new ListWithFilterPresenter(interactor);
}
}
#Module
public class ListPresenterModule {
#Provides #NonNull #Singleton
public ListPresenter providePresenter(ListInteractor interactor) {
return new ListPresenter(interactor);
}
}
Error:(16, 10) error: com.sober.appModules.List.presenter.ListPresenter cannot be provided without an #Provides- or #Produces-annotated.method.com.sober.appModules.List.presenter.ListPresenter is injected at
com.sober.appModules.List.ui.ListFragment.presenter
com.sober.appModules.List.ui.ListWithFilterFragment is injected at com.sober.appModules.List.injection.ListWithFilterComponent.inject(fragment)
Dagger does not do partial injections. Either it can provide all the objects, or it will fail with cannot be provided.
In your case ListWithFilterComponent cannot provide ListPresenter to ListWithFilterFragment—or rather its ListFragment parent.
see comments at the end of the lines
error: List.presenter.ListPresenter cannot be provided without an #Provides- or #Produces-annotated.method.
List.presenter.ListPresenter is injected at <- missing object
List.ui.ListFragment.presenter <- field in parent that is getting injected
List.ui.ListWithFilterFragment is injected at <- object it tries to inject
List.injection.ListWithFilterComponent.inject(fragment) <- component that's missing something
If you want to keep your current setup you will need to provide ListPresenter from your ListWithFilterComponent as well, or otherwise you will need to refactor your fragments so that they don't extend one another.
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 am using dagger for dependency injection, I apologize for not diving into deep and getting my hands on :)
I am using two modules(ApplicationModule, ContactServiceModule), with a component (AppComponent). I have initialized the Dagger in App.java, and I am trying to inject dependencies to a backgroud Service.
Here is the code snippet.
1)ApplicationModule.java
#Module
public class ApplicationModule {
private final App mApp;
private Service contactService;
public ApplicationModule(App app) {
mApp = app;
}
#Provides
#Singleton
public ContactModel contactModel(SQLiteDatabase database) {
return new ContactModel(mApp, database);
}
#Provides
#Singleton
public Context appContext() {
return mApp;
}
#Provides
#Singleton
public ContactController contactController() {
return new ContactController(mApp.getAppComponent());
}
//.....Some more code
}
2)ContactServiceModule.java
#Module
public class ContactServiceModule {
ContactService contactService;
public ContactServiceModule(ContactService contactService){
this.contactService =contactService;
}
#Provides
#Singleton
ContactService provideContactService() {
return this.contactService;
}
}
3)AppComponent.java
#Singleton
#Component(modules = {ApplicationModule.class,ContactServiceModule.class})
public interface AppComponent {
ContactController contactController();
Context appContext();
//...Some code
void inject(ContactController contactController);
//...Some code
void inject(ContactService contactService);
}
4)App.java
public class App extends Application {
private AppComponent mAppComponent;
#Override
public void onCreate() {
super.onCreate();
FlowManager.init(this);
mAppComponent = DaggerAppComponent.builder()
.applicationModule(new ApplicationModule(this))
.build();
}
public AppComponent getAppComponent() {
return mAppComponent;
}
}
5) ContactService.java
public class ContactService extends Service {
#Inject
Context appContext;
#Inject
ContactController contactController;
#Override
public void onCreate() {
super.onCreate();
getAppComponent().inject(this);
}
private AppComponent getAppComponent() {
return ((App)getApplication()).getAppComponent();
}
}
It could be an issue of dependencies of UI threads injected into background services. But I do not understand how should I achieve the same dependencies to be injected into services. Or what should be the Ideal way to communicate between application level dependencies and services. I want the service to be running always in the background and listening to various intents and also communicate to other dependencies like eventbus etc.
The error message:
contactServiceModule must be set at DaggerAppComponent$Builder.build(DaggerAppComponent.java)”
means that you have created a module with dependencies in the constructor (ContactServiceModule which has your Service as a parameter in the constructor). It follows that Dagger 2 cannot instantiate that module automatically and it must be instantiated in the builder when you create your component:
mAppComponent = DaggerAppComponent.builder()
.applicationModule(new ApplicationModule(this))
.contactServiceModule(contactService);
.build();
However there is an underlying issue with the architecture you are trying to create. Services, like Activities, are designed to be autonomous and instantiated by the Android OS. Much in the same way you wouldn't directly pass as an Activity as a dependency to another Activity, you shouldn't make a Service a dependency. Instead you need to use Intent, service binding, or EventBus events to communicate with the service. The Android Service documentation covers this and you should read it carefully before proceeding.
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);
}