I am attempting to add Dagger 2 to my Android Project. I think I understand the concepts up to the point of where I build the graph. At that point I'm shooting in the dark and that is where I'm going wrong.
Everything compiles, but the injected field is null at Runtime.
I am attempting to start simply by injecting the Presenter into my MainActivity. I have written the following code and would appreciate some help figuring out where I have gone wrong.
My PresenterModule.java:
#Module
public class PresenterModule {
#Provides MainActivityPresenter providesMainActivityPresenter() {
return new DefaultMainActivityPresenter();
}
}
My Application class which also includes my Component following the Dagger2 example code:
public class App extends Application {
private PresenterComponent component;
#Singleton
#Component(modules = PresenterModule.class)
public interface PresenterComponent {
void inject(App app);
void inject(MainActivity activity);
}
#Override public void onCreate() {
Log.d("App.java", "Starting Application");
super.onCreate();
component = DaggerApp_PresenterComponent.builder()
.presenterModule(new PresenterModule())
.build();
component.inject(this);
}
public PresenterComponent component() {
return component;
}
}
And finally my MainActivity.
public class DefaultMainActivity
extends ActionBarActivity
implements MainActivity
{
#Inject MainActivityPresenter mPresenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((App)getApplication()).component().inject(this);
mPresenter.getCurrentDetailLineNumber();
setContentView(R.layout.base_layout);
getSupportActionBar();
mContainer = (Container) findViewById(R.id.container);
mPresenter.requestCurrentScreen();
}
The actual object to be injected is an implementation of an interface but is other wise a POJO object:
public class DefaultMainActivityPresenter implements MainActivityPresenter {
private static final int SCREEN_BROWSER = 0;
private static final int SCREEN_DETAIL = 1;
LineNumber mCurrentDetailLineNumber;
int mCurrentScreen;
#Inject
public DefaultMainActivityPresenter() {
}
...
}
Changing PresenterComponent to following will fix your problem:
#Singleton
#Component(modules = PresenterModule.class)
public interface PresenterComponent {
void inject(App app);
void inject(DefaultMainActivity activity);
}
This is due to covariance:
While a members-injection method for a type will accept instances of its subtypes, only Inject-annotated members of the parameter type and its supertypes will be injected; members of subtypes will not. For example, given the following types, only a and b will be injected into an instance of Child when it is passed to the members-injection method injectSelf(Self instance):
class Parent {
#Inject A a;
}
class Self extends Parent {
#Inject B b;
}
class Child extends Self {
#Inject C c;
}
Related
In my application , I am trying to create Dagger 2 components for
Context (To use in different classes) Which is already done
AppUtil (requires context for network check method) Need to get this done!
I have created components and initiated from Application class.
public class MyApp extends Application{
private ContextComponent mContextComponent;
private AppUtilComponent mAppUtilComponent;
private NetworkComponent mNetworkComponent;
#Override
public void onCreate() {
super.onCreate();
mNetworkComponent = createNetworkComponent();
mContextComponent = createContextComponent();
mAppUtilComponent = createAppUtilComponent();
}
private AppUtilComponent createAppUtilComponent() {
return DaggerAppUtilComponent.builder().appUtilModule(new AppUtilModule(this)).build();
}
public AppUtilComponent getAppUtilComponent() {
return mAppUtilComponent;
}
private NetworkComponent createNetworkComponent() {
return DaggerNetworkComponent.builder().networkModule(new NetworkModule()).build();
}
public NetworkComponent getNetworkComponent() {
return mNetworkComponent;
}
private ContextComponent createContextComponent() {
return DaggerContextComponent.builder().contextModule(new ContextModule(this)).build();
}
public ContextComponent getContextComponent(){
return mContextComponent;
}
}
The ContextModule class is as follows
#Module
public class ContextModule {
private Context mContext;
public ContextModule(Context context){
mContext = context;
}
#Provides
#Singleton
Context getContext(){
return mContext;
}
}
Context component will be
#Singleton
#Component (modules = ContextModule.class)
public interface ContextComponent {
void inject(AppUtils appUtils);
}
AppUtilModule is like
#Singleton
#Component (modules = AppUtilModule.class)
public interface AppUtilComponent {
void inject(SplashActivity splashActivity);
}
With this AppUtilModule have modified as
#Module (includes = ContextModule.class)
public class AppUtilModule {
private AppUtils mAppUtils;
private Context context;
#Inject
public AppUtilModule(Context context) {
this.context = context;
}
#Provides
public AppUtils getAppUtil(){
mAppUtils = new AppUtils(context);
return mAppUtils;
}
}
Now my AppUtils is getting context injected , which is perfectly alright.
public class AppUtils {
public Context mContext;
#Inject
public AppUtils(Context _context){
mContext = _context;
}
public boolean isNetworkConnected(){
//Using mContext to determine Network
}
... Other Utility methods which will be used throughout my application
}
But now how can I make AppUtil as a separate Dagger component (which
is internally having Context as a Dependency) and inject in other classes?
EDIT 1: After making constructor injection of context into AppUtils and using AppUtil component in SplashActivity which already had Network component
After making the AppUtil as Dagger dependency, now Project is giving
compile time error. Before this changes, NetworkProcessor in SplashActivity used to work
fine, as it was only the dependency SplashActivity had. What is that I
am missing/ doing wrong!
public class SplashActivity ....
{
#Inject
public NetworkProcessor mNetworkProcessor;
.....
#Inject
public AppUtils mAppUtils;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyApp.getInstance().getAppUtilComponent().inject(this);
MyApp.getInstance().getNetworkComponent().inject(this);
}
}
Error:
rror: [Dagger/MissingBinding] com.dev.myapp.networking.NetworkProcessor cannot be provided without an #Inject constructor or an #Provides-annotated method.
com.dev.myapp.networking.NetworkProcessor is injected at
com.dev.myapp.ui.activities.landing.SplashActivity.mNetworkProcessor
com.dev.myapp.ui.activities.landing.SplashActivity is injected at
com.dev.myapp.components.AppUtilComponent.inject(com.dev.myapp.ui.activities.landing.SplashActivity)
So a component can create multiple dependencies at once, which are usually grouped by lifetime scope (Application = whole app lifetime, Activity = activity lifetime).
So if your AppUtils class has the same scope as your ContextComponent, you can just use it to inject the AppUtils into classes that should use it, e.g. an activity:
public class MainActivity extens Activity {
#Inject AppUtils appUtils;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((MyApp) getApplication()).getContextComponent().inject(this);
}
}
after extending the ContextComponent definition to
#Singleton
#Component (modules = ContextModule.class)
public interface ContextComponent {
void inject(AppUtils appUtils);
void inject(MainActivity activity);
}
and you need to change AppUtils to use conastructor injection:
public class AppUtils {
private Context mContext;
#Inject
public AppUtils(Context context){
this.mContext = context;
}
}
After Question Edit 1
Dagger does not know how to create the NetworkProcessor-class, hence the compiler error. To make it work, you should change NetworkProcessor to have a constructor that is annotated with #Inject like you did with AppUtils (the second option would be creating a #Provides method in a dagger module, but the first solution is easier).
You did not supply the source code of NetworkProcessor so I'm assuming here it only needs a Context as a dependency.
Now that both AppUtils and NetworkProcessor have an injectible constructor that only has Context as an argument, Dagger can create the missing links by itself.
You don't need the NetworkComponent and the AppUtilComponent, just one Component, so delete them. Also delete the NetworkModule and the AppUtilModule. ContextComponent now is sufficient to inject all the dependencies into SplashActivity:
public class SplashActivity .... {
#Inject public NetworkProcessor mNetworkProcessor;
.....
#Inject public AppUtils mAppUtils;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyApp.getInstance().getContextComponent().inject(this);
}
//...
}
This works because Dagger can create the wiring code itself, since it knows how to instantiate both AppUtils and NetworkProcessor.
I am trying to make an injection using Dagger 2, but it always returns null. I think I am doing all right, but anyway it does not work.
Here is the application class:
public class ApplicationSA extends Application {
private static AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.create();
}
public static AppComponent getComponent() {
return appComponent;
}
}
The component interface:
#Component(modules = {
SnoreDetectorClass.class,
AudioRecorderClass.class
})
public interface AppComponent {
void injectsMainFunctionalityActivity(Activity activity);
}
An the main class where I am trying to get the object:
public class MainFunctionalityActivity extends AppCompatActivity {
#Inject
AudioRecorderClass audioRecorderClass;
#Inject
SnoreDetectorClass snoreDetectorClass;
#Override
protected void onCreate(Bundle savedInstanceState) {
ApplicationSA.getComponent().injectsMainFunctionalityActivity(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("TESTING","audioRecorderClass= "+audioRecorderClass); // always null
Log.d("TESTING","snoreDetectorClass= "+snoreDetectorClass); // always null
...
}
And here are the module classes:
#Module
public class AudioRecorderClass {
public interface AudioRecorderInterface {
void AudioRecorder_hasUpdate(double amplitude_in_dB);
}
public AudioRecorderInterface delegate = null;
#Provides
AudioRecorderClass provideAudioRecorderClass(Activity activity) {
delegate = (AudioRecorderInterface)activity;
return new AudioRecorderClass();
}
...
#Module
public class SnoreDetectorClass {
#Provides
SnoreDetectorClass provideSnoreDetectorClass() {
return new SnoreDetectorClass();
}
...
What am I doing wrong ? Why the objects are always null ?
Ah, I see what is going on here. You cannot inject into a subclass. So in your AppComponent you cannot have
void injectsMainFunctionalityActivity(Activity activity);
you must inject with
void injectsMainFunctionalityActivity(MainFunctionalityActivity activity);
As a side note I would suggest not combining your injector and your model class. Better to have separation of concerns. Keep them separate
You have to specifically tell dagger which activity will be injected here, not use the super class Activity but rather your own implementation of the Activity class :
void injectsMainFunctionalityActivity(Activity activity);
change to:
void injectsMainFunctionalityActivity(MainFunctionalityActivity activity);
I am trying to inject my MainActivity into the Fragment. I have an Interface that is implemented in my MainActivity that will listen to events from the Fragment. So I want to Inject the MainActivity and call the event listener on it.
I have tried doing the following but has failed to do so. Just displaying the code snippets.
interface
public interface RecipeItemListener {
void onRecipeItem();
}
MainActivity that implements the interface
public class MainActivity extends AppCompatActivity implements RecipeItemListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState == null) {
getSupportFragmentManager()
.beginTransaction()
.add(R.id.main_fragment_container, RecipeListView.newInstance(), RecipeListView.TAG)
.commit();
}
}
#Override
public void onRecipeItem() {
Timber.d("onRecipeItem");
}
}
My Module that provides the MainActivity
#Module
public class RecipeListModule {
private MainActivity mainActivity;
public RecipeListModule(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
#RecipeListScope
#Provides
public RecipeItemListener providesRecipeListMainActivity() {
return mainActivity;
}
}
My main Component
#Singleton
#Component(modules = {
AndroidModule.class,
NetworkModule.class})
public interface BusbyBakingComponent {
RecipeListComponent add(RecipeListModule recipeListModule);
}
My SubComponent
#RecipeListScope
#Subcomponent(modules = {RecipeListModule.class})
public interface RecipeListComponent {
void inject(RecipeListView target);
}
My Application class
public class BusbyBakingApplication extends Application {
private BusbyBakingComponent applicationComponent;
private RecipeListComponent recipeListComponent;
#Override
public void onCreate() {
super.onCreate();
applicationComponent = createApplicationComponent();
}
public BusbyBakingComponent createApplicationComponent() {
return DaggerBusbyBakingComponent.builder()
.networkModule(new NetworkModule())
.androidModule(new AndroidModule(BusbyBakingApplication.this))
.build();
}
public BusbyBakingComponent getApplicationComponent() {
return applicationComponent;
}
public RecipeListComponent createRecipeListComponent(MainActivity activity) {
recipeListComponent = applicationComponent.add(new RecipeListModule(activity));
return recipeListComponent;
}
public void releaseRecipeListComponent() {
recipeListComponent = null;
}
}
And in My fragment this is how I am trying to inject:
#Inject MainActivity mainActivity;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((BusbyBakingApplication)getActivity().getApplication())
.createRecipeListComponent((MainActivity)getActivity())
.inject(RecipeListView.this);
}
I keep getting the following error:
Error:(14, 8) error: [me.androidbox.busbybaking.di.RecipeListComponent.inject(me.androidbox.busbybaking.recipieslist.RecipeListView)] me.androidbox.busbybaking.recipieslist.MainActivity cannot be provided without an #Inject constructor or from an #Provides- or #Produces-annotated method.
me.androidbox.busbybaking.recipieslist.MainActivity is injected at
me.androidbox.busbybaking.recipieslist.RecipeListView.mainActivity
me.androidbox.busbybaking.recipieslist.RecipeListView is injected at
me.androidbox.busbybaking.di.RecipeListComponent.inject(target)
Many thanks for any suggestions.
If you have a look at your module
#RecipeListScope
#Provides
public RecipeItemListener providesRecipeListMainActivity() {
return mainActivity;
}
You provide the interface (which is good) and not MainActivity (the implementation).
Since you request MainActivity
#Inject MainActivity mainActivity;
You receive this error:
MainActivity cannot be provided [...]
because you only provide RecipeItemListener.
Switch your code from requiring MainActivity in RecipeListView to
#Inject RecipeItemListener recipeListener;
and it should work, if the rest of your setup is correct.
You can access activity in Fragment using getActivity() and cast it to your interface listener like this
((RecipeItemListener)getActivity()).doSomethingOnListener()
much simpler, without any unnecessary injections
I am reading documentation about Dagger2. The thing is I have clear how inject a class how has Context using this
But my doubt is with the following class for example:
public class SocialControler{
private ImageView twitterLogo;
private ImageView googleLogo;
public SocialControler(ImageView twitter, ImageView google){
twitterLogo = twitter;
googleLogo = google;
}
//Getters and Setters
}
So in my MainActivity I will have something like this
SocialControler mSocial = new SocialControler(mTwitterLogo, mGoogleLogo);
Should I inject this class using #Inject anotation instead doing new , so no more new on our Activity? If yes How?
I am stuck in the #Mudule . How I can provide a View inside de Module ?
#Module
public class AppModuleSocialMediaManager {
#Provides
public MainActivity provideActivity(){
return new MainActivity();
}
#Provides
public SocialMediaClickManager provideMediaManager(MainActivity mainActivity, View twitterLogo, View googleLogo) {
return new SocialMediaClickManager(mainActivity);
}
#Provides
public View provideTwitter(){
return ?
//from here how I can provide a View
}
}
I would not recommend the architecture you're using to build your app, i.e. make your controllers depend on exact views, but if you're fine with that then you can implement your module like this:
#Module
public class AppModuleSocialMediaManager {
private final MainActivity mainActivity;
public AppModuleSocialMediaManager(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
#Provides
#Named("twitter_view")
View provideTwitterView() {
return mainActivity.findViewById(..);
}
#Provides
#Named("google_view")
View provideGoogleView() {
return mainActivity.findViewById(..);
}
#Provides
SocialController provideSocialController(#Named("twitter_view") View twitterView, #Named("google_view") View googleView) {
return new SocialController(twitterView, googleView);
}
}
Then you need to declare another component. Let's say it's MainActivityComponent:
#Component(modules = AppModuleSocialMediaManager.class)
public interface MainActivityComponent {
void inject(MainActivity activity);
}
After that in your activity you can do something like this:
public class MainActivity extends Activity {
#Inject
SocialController socialController;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerMainActivityComponent.Builder()
.appModuleSocialMediaManager(new AppModuleSocialMediaManager(this))
.build()
.inject(this);
socialController.doSomething();
}
}
In Dagger 1 I had a base class setup such that it would handle creating a scoped graph and injecting dependencies into the current object. For example...
public abstract class MyBaseActivity extends Activity {
private ObjectGraph graph;
protected void onCreate(Bundle savedInstanceState) {
graph = ((MyApp) getApplication()).plus(getModules());
graph.inject(this);
}
protected Object[] getModules();
}
public class MyClass extends MyBaseActivity {
#Inject SomeDep someDep;
#Override
protected Object[] getModules() {
return new Object[/* Contains a module that provides SomeDep */];
}
}
This allowed for each subclass to supplement their own set of modules in addition to a standard application module.
After playing around with Dagger 2, it doesn't seem possible to handle a similar scenario...
public abstract class MyBaseActivity extends Activity {
private MyBaseActivityComponent component;
protected void onCreate(Bundle savedInstanceState) {
component = ((MyApp) getApplication()).component().plus(/* Can not accept an array */);
component.inject(this);
}
}
I worked around the above by modifying MyBaseActivityComponent such that it would list all possible modules it may use...
#Subcomponent(modules = {
Module1.class,
Module2.class
})
public interface MyBaseActivityComponent {
public void inject(MyBaseActivity activity);
}
So now I can do something like this...
public abstract class MyBaseActivity extends Activity {
private MyBaseActivityComponent component;
protected void onCreate(Bundle savedInstanceState) {
component = ((MyApp) getApplication()).component().plus(new Module1(), new Module2());
component.inject(this);
}
}
But now I have a problem where the injection will inject dependencies for MyBaseActivity but not it's subclasses. Suggestions?
Theoretically, you can do it like this.
1.) Specify a child scope
#Scope
#Retention(RUNTIME)
public #interface PerActivity {
}
2.) Specify the parent component
#Singleton
#Component(modules={Module1.class, Module2.class)
public interface MyApplicationComponent {
Dependency1 providesDependency1();
Dependency2 providesDependency2();
}
3.) Specify the child component
#PerActivity
#Component(dependencies={MyApplicationComponent.class}, modules={Module3.class})
public interface MyBaseActivityComponent extends MyApplicationComponent {
void inject(BaseActivity baseActivity);
Dependency3 providesDependency3();
}
4.) Create your module
#Module
public class Module3 {
#Provides
#PerActivity
public Dependency3 providesDependency3() {
return new Dependency3();
}
}
5.) Create Activity-level scoped component
public class BaseActivity extends AppCompatActivity {
private MyBaseActivityComponent baseComponent;
#Override
public void onCreate(Bundle saveState) {
super.onCreate(saveState);
baseComponent = DaggerBaseActivityComponent.builder()
.applicationComponent(((MyApp)getApplication()).component())
.build();
}
public MyBaseActivityComponent baseComponent() {
return baseComponent;
}
#Override
public void onDestroy() {
component = null;
super.onDestroy();
}
}
Please reply if it worked, previously I forgot to specify the dependencies in my Component and got compile errors, but it should work like this.
Also, if you need to specify a subcomponent for each Activity, then you can just specify the dependencies with provision methods in the BaseActivityComponent component...
#PerActivity
#Component(dependencies={MyBaseActivityComponent.class}, modules={Module4.class})
public interface MyActivityComponent extends MyBaseActivityComponent {
public void inject(MyActivity myActivity);
Dependency4 providesDependency4();
}
#Module
public class Module4 {
#PerActivity
#Provides
public Dependency4 providesDependency4(Dependency3 dependency3) {
return new Dependency4(dependency3);
}
}
public class MyActivity extends MyBaseActivity {
private MyActivityComponent component;
#Override
public void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
component = DaggerMyActivityComponent.builder()
.applicationComponent(((MyApp)getApplication()).component())
.myBaseActivityComponent(baseComponent())
.build();
}
#Override
public void onDestroy() {
component = null;
super.onDestroy();
}
}
EDIT: #Subcomponent works to replace component dependencies with subcomponent factory methods according to the docs only if you use the following pattern (aka, embedding the subcomponent within the parent component using a provision/factory method definition):
#Singleton #Component
interface ApplicationComponent {
// component methods...
RequestComponent newRequestComponent(RequestModule requestModule);
}
Where
#Subcomponent(modules={RequestModule.class})
interface RequestComponent {
RequestSomething requestSomething();
}