MVVM Dagger2 A Binding With Matching Key Exists in Component - android

I am using the following google sample project: https://github.com/googlesamples/android-architecture-components as a reference for my new project and having difficulties trying to add a second activity to the project.
Here is the error I get when compiling
Error:(22, 8) error: [dagger.android.AndroidInjector.inject(T)] com.apps.myapp.ui.common.MainActivity cannot be provided without an #Inject constructor or from an #Provides-annotated method. This type supports members injection but cannot be implicitly provided.
com.apps.myapp.ui.common.MainActivity is injected at
com.apps.myapp.ui.common.NavigationController.<init>(mainActivity)
com.apps.myapp.ui.common.NavigationController is injected at
com.apps.myapp.ui.addContacts.AddContactsFragment.navigationController
com.apps.myapp.ui.addContacts.AddContactsFragment is injected at
dagger.android.AndroidInjector.inject(arg0)
A binding with matching key exists in component: com.apps.myapp.di.ActivityModule_ContributeMainActivity.MainActivitySubcomponent
Here is my code
ActivityModule
#Module
public abstract class ActivityModule {
#ContributesAndroidInjector(modules = FragmentBuildersModule.class)
abstract MainActivity contributeMainActivity();
#ContributesAndroidInjector(modules = FragmentBuildersModule.class)
abstract ContactActivity contributeContactActivity();
}
AppComponent
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
ActivityModule.class})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance Builder application(Application application);
AppComponent build();
}
void inject(App app);
}
AppInjector
public class AppInjector {
private AppInjector() {}
public static void init(App app) {DaggerAppComponent.builder().application(app).build().inject(app);
app.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
handleActivity(activity);
}
#Override
public void onActivityStarted(Activity activity) {
}
#Override
public void onActivityResumed(Activity activity) {
}
#Override
public void onActivityPaused(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
});
}
private static void handleActivity(Activity activity) {
if (activity instanceof HasSupportFragmentInjector) {
AndroidInjection.inject(activity);
}
if (activity instanceof FragmentActivity) {
((FragmentActivity) activity).getSupportFragmentManager()
.registerFragmentLifecycleCallbacks(
new FragmentManager.FragmentLifecycleCallbacks() {
#Override
public void onFragmentCreated(FragmentManager fm, Fragment f,
Bundle savedInstanceState) {
if (f instanceof Injectable) {
AndroidSupportInjection.inject(f);
}
}
}, true);
}
}
}
AppModule
#Module(includes = ViewModelModule.class)
class AppModule {
#Singleton #Provides
BnderAPIService provideService() {
return new Retrofit.Builder()
.baseUrl("serverurl")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(new LiveDataCallAdapterFactory())
.build()
.create(APIService.class);
}
#Singleton #Provides
Db provideDb(Application app) {
return Room.databaseBuilder(app, Db.class,"Db.db").build();
}
#Singleton #Provides
UserDao provideUserDao(Db db) {
return db.userDao();
}
#Singleton #Provides
ContactDao provideContactDao(Db db) {
return db.contactDao();
}
}
FragmentBuildersModule
#Module
public abstract class FragmentBuildersModule {
#ContributesAndroidInjector
abstract AddContactsFragment contributeAddUserFragment();
#ContributesAndroidInjector
abstract ContactsFragment contributeContactsFragment();
#ContributesAndroidInjector
abstract ChalkboardFragment contributeChalkboardFragment();
}
Injectable
public interface Injectable {
}
ViewModelKey
#Documented
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#MapKey
#interface ViewModelKey {
Class<? extends ViewModel> value();
}
ViewModelModule
#Module
abstract class ViewModelModule {
#Binds
#IntoMap
#ViewModelKey(AddContactsViewModel.class)
abstract ViewModel bindAddContactsViewModel(AddContactsViewModel addContactsViewModel);
#Binds
#IntoMap
#ViewModelKey(ContactsViewModel.class)
abstract ViewModel bindContactsViewModel(ContactsViewModel contactsViewModel);
#Binds
#IntoMap
#ViewModelKey(ChalkboardViewModel.class)
abstract ViewModel bindChalkboardViewModel(ChalkboardViewModel chalkboardViewModel);
#Binds
abstract ViewModelProvider.Factory bindViewModelFactory(ViewModelFactory factory);
}
Application
public class App extends Application implements HasActivityInjector {
#Inject
DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;
#Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
#Override
public void onCreate() {
super.onCreate();
if (BuildConfig.DEBUG) {
}
AppInjector.init(this);
}
#Override
public DispatchingAndroidInjector<Activity> activityInjector() {
return dispatchingAndroidInjector;
}
}
NavigationController
public class NavigationController {
private final int containerId;
private final FragmentManager fragmentManager;
#Inject
public NavigationController(MainActivity mainActivity) {
this.containerId = R.id.container;
this.fragmentManager = mainActivity.getSupportFragmentManager();
}
public void navigateToUsers() {
Log.i("TAG", "Navigate to users");
String tag = "users";
AddContactsFragment userFragment = AddContactsFragment.create();
fragmentManager.beginTransaction()
.replace(containerId, userFragment, tag)
.addToBackStack(null)
.commitAllowingStateLoss();
}
public void navigateToContacts() {
Log.i("TAG", "Navigate to contacts");
String tag = "contacts";
ContactsFragment contactsFragment = ContactsFragment.create();
fragmentManager.beginTransaction()
.add(contactsFragment, tag)
.addToBackStack(null)
.commitAllowingStateLoss();
}
public void navigateToChalkboard() {
Log.i("TAG", "Navigate to chalkboard");
String tag = "chalkboard";
ChalkboardFragment chalkboardFragment = ChalkboardFragment.create();
fragmentManager.beginTransaction()
.add(chalkboardFragment, tag)
.addToBackStack(null)
.commitAllowingStateLoss();
}
}
MainActivity
public class MainActivity extends AppCompatActivity implements LifecycleRegistryOwner, HasSupportFragmentInjector {
private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
#Inject
DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;
#Inject
NavigationController navigationController;
private Toolbar toolbar;
#Override
public LifecycleRegistry getLifecycle() {
return lifecycleRegistry;
}
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setHandler(this);
binding.setManager(getSupportFragmentManager());
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
}
#Override
public DispatchingAndroidInjector<Fragment> supportFragmentInjector() {
return dispatchingAndroidInjector;
}
static class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
#BindingAdapter({"handler"})
public static void bindViewPagerAdapter(final ViewPager view, final MainActivity activity) {
final ViewPagerAdapter adapter = new ViewPagerAdapter(activity.getSupportFragmentManager());
adapter.addFragment(new ChalkboardFragment(), "Chalkboard");
adapter.addFragment(new ContactsFragment(), "Contacts");
view.setAdapter(adapter);
}
#BindingAdapter({"pager"})
public static void bindViewPagerTabs(final TabLayout view, final ViewPager pagerView) {
view.setupWithViewPager(pagerView, true);
}
}
ContactActivity
public class ContactActivity extends AppCompatActivity implements LifecycleRegistryOwner, HasSupportFragmentInjector {
private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
#Inject
DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;
#Override
public LifecycleRegistry getLifecycle() {
return lifecycleRegistry;
}
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_contact);
if (savedInstanceState == null) {
}
}
#Override
public DispatchingAndroidInjector<Fragment> supportFragmentInjector() {
return dispatchingAndroidInjector;
}
}

"A binding with matching key exists in component" means that you have bound a dependency somewhere in your entire object graph but it cannot be reached from the subcomponent where it needs to be injected. Here is the javadoc:
Utility code that looks for bindings matching a key in all subcomponents in a binding graph so that a user is advised that a binding exists elsewhere when it is not found in the current subgraph. If a binding matching a key exists in a sub- or sibling component, that is often what the user actually wants to use.
For instance, assume you have two Activities, ActivityA and ActivityB. You generate subcomponents with #ContributesAndroidInjector and bind Foo in the ActivityA module but not the ActivityB module. If you request injection for Foo in ActivityB with #Inject Foo foo you will get that error message.
In your particular case, the error you are getting can be reproduced by:
Cloning the project from GitHub
git clone https://github.com/googlesamples/android-architecture-components.git`
Copy-pasting MainActivity into a new file ContactsActivity
Modifying MainActivityModule to look like your ActivityModule
So from this we can conclude that your ActivityModule is problematic. The #ContributesAndroidInjector is not as simple as it might seem. It actually means you are creating a new Dagger 2 subcomponent for the Activity you specify there (see the docs here).
A subcomponent can use bindings from a parent component but not sibling components. Your two lines for ContributesAndroidInjector in the ActivityModule create two sibling subcomponents: one for MainActivity and one for ContactsActivity.
However, NavigationController is dependent on MainActivity which is bound in the object graph for the MainActivity subcomponent but not in that for the ContactsActivity subcomponent. AddContactsFragment has become part of the object graph for the ContactsActivity subcomponent and doesn't have access to MainActivity anymore. This means that when Dagger 2 tries to inject the NavigationController inside your AddContactsFragment it cannot provide MainActivity as a dependency for it. This explains the "cannot provide" part of the error message.
Although it can't provide MainActivity in that particular object graph, the AndroidInjector does know in general about MainActivity hence the error message "a binding key exists". What binding key is this? A key that binds MainActivity.class to a MainActivityFactory. Where is this key bound? In your ActivityModule when you wrote the #ContributesAndroidInjector for MainActivity.
How to fix this is getting beyond scope of a StackOverflow question because it involves a lengthy refactor of the code. You would need to re-organise the object graph so that the NavigationController no longer depends on MainActivity. Perhaps you could make it depend on AppCompatActivity since that is a superclass of both your Activities. You would then need to stop using ContributesAndroidInjector and write explicit modules for your two Activities that include bindings for AppCompatActivity.
However, for now please go back to basics and start with something easier. It is a recipe for disaster to start with a complex project without a complete understanding and just modify it hoping that it will work.
The Codepath Dagger 2 tutorial project is much easier to understand and will get you familiar with the basic concepts involved in Dagger 2. Once you are comfortable with the basic concepts and have understood dependent components and sub-components, then you can attempt a more difficult example. Good luck!

Related

Dagger 2 injection returns null in Activity scope

I need to inject a dependency that requires FragmentManager in the constructor. In order to do that, I have created a custom scope for my activity linked to the MainModule of the app. After the injection, the dependency returns null. I think the problem is in submodules because the App dependencies work fine.
The code is the following:
HomeModule
#Module
public class HomeModule {
private HomeActivity homeActivity;
public HomeModule(HomeActivity homeActivity) {
this.homeActivity = homeActivity;
}
#Provides
public BackStackManager provideBackStackManager(){
return new BackStackManager(homeActivity.getSupportFragmentManager());
}
}
HomeScope
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface HomeScope {
}
HomeComponent
#HomeScope
#Subcomponent(modules = {HomeModule.class})
public interface HomeComponent {
void inject(HomeActivity activity);
}
ApplicationClass
public HomeComponent plusHomeComponent(HomeModule homeModule){
if (homeComponent == null){
homeComponent = mainComponent.plusHomeComponent(homeModule);
}
return homeComponent;
}

How to inject a Activity into a Fragment using Dagger2

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 don't know why the object injected by dagger2 is null in presenter

I don't know why loginResponseHandler of MainRankPresenter.java injected by dagger2 is null in MainRankPresenter.
I just want to inject to field for field injection.
Should I do other way instead field injection?
please, Let me know how to resolve it.
BBBApplication.java
public class BBBApplication extends MultiDexApplication
{
...
#Override
public void onCreate() {
super.onCreate();
initAppComponent();
}
private void initAppComponent() {
this.appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
}
public static BBBApplication get(Context ctx) {
return (BBBApplication) ctx.getApplicationContext();
}
public AppComponent getAppComponent() {
return this.appComponent;
}
...
}
AppModule.java
#Module
public class AppModule {
private BBBApplication application;
public AppModule(BBBApplication application) {
this.application = application;
}
#Provides
#Singleton
public Application provideApplication() {
return this.application;
}
#Provides
#Singleton
public Resources provideResources() {
return this.application.getResources();
}
#Provides`enter code here`
#Singleton
public SharedPreferences provideSharedPreferences() {
return PreferenceManager.getDefaultSharedPreferences(this.application);
}
}
AppComponent.java
#Singleton
#Component(modules = {AppModule.class, ServiceModule.class})
public interface AppComponent {
RankFragmentComponent plus(RankFragmentModule module);
Application application();
Resources resources();
}
RankFragmentModule.java
#Module
public class RankFragmentModule {
private RankFragment rankFragment;
public RankFragmentModule(RankFragment rankFragment) {
this.rankFragment = rankFragment;
}
#Provides
#ActivityScope
public LoginResponseHandler provideLoginResponseHandler() {
return new LoginResponseHandler(this.rankFragment);
}
#Provides
#ActivityScope
// #Named("rankFragment")
public RankFragment provideRankFragment() {
return this.rankFragment;
}
#Provides
#ActivityScope
public MainRankPresenter provideMainRankPresenter(RankFragment rankFragment) {
return new MainRankPresenter(new MainRankViewOps(rankFragment));
}
}
RankFragmentComponent.java
#ActivityScope
#Subcomponent(modules = {RankFragmentModule.class})
public interface RankFragmentComponent {
void inject(RankFragment rankFragment);
}
RankFragment.java
public class RankFragment extends Fragment {
#Inject
MainRankPresenter presenter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BBBApplication.get(getContext())
.getAppComponent()
.plus(new RankFragmentModule(this))
.inject(this);
presenter.test();
}
MainRankPresenter.java
public class MainRankPresenter implements Presenter {
private MainRankViewOps viewOps;
#Inject
LoginResponseHandler loginResponseHandler;
#Inject
public MainRankPresenter(MainRankViewOps viewOps) {
this.viewOps = viewOps;
}
#Override
public void test() {
Log.d(Constants.TAG_DEBUG, "=== presenter test" + this.toString());
Log.d(Constants.TAG_DEBUG, "=== " + loginResponseHandler.toString());
this.viewOps.initViewOps();
}
}
MainRankViewOps.java
public class MainRankViewOps implements ViewOps {
RankFragment fragment;
#Inject
public MainRankViewOps(RankFragment fragment) {
this.fragment = fragment;
}
#Override
public void initViewOps() {
Log.d(Constants.TAG_DEBUG, "=== view ops" + this.toString());
Log.d(Constants.TAG_DEBUG, "=== " + fragment.toString());
}
}
Injection by Dagger 2 is not recursive. Therefore, when you call inject(this) in RankFragment only #Inject annotated fields of that fragment are being injected. Dagger 2 will not search for #Inject annotations in the injected objects.
In general, you should attempt to restrict usage of Dependency Injection frameworks to "top-level" components (Activities, Fragments, Services, etc.) which are being instantiated by Android framework for you. In objects that you instantiate yourself (like MainRankPresenter) you should use other DI techniques which do not involve external framework (e.g. dependency injection into constructor).
Because you #Provides the MainRankPresenter, Dagger won't inject it: you take responsibility for this. You could possibly have your provides method be passed a MembersInjector si you can inject the fields if the object before returning it, but it'd probably be better to refactor your module you remove that provides method and let Dagger handle the injection (you have all the #Inject needed already)

How can "ask, don't look" be applied in Android?

This Google Testing Blog post lists some strategies for making code testable. One item says in part:
Ask for things, Don't look for things (aka Dependency Injection / Law of Demeter): OK, you got rid of your new operators in you application code. But how do I get a hold of the dependencies. Simple: Just ask for all of the collaborators you need in your constructor.
In other words, do this:
Foo(final Bar bar) {
mBar = bar;
}
Not this:
Foo() {
mBar = Bar.getBar(); // or new Bar();
}
The reason for this is obvious: it lets you test Foo by passing it a mock Bar. Since Android components require no-arg constructors, the equivalent is to pass their arguments via an extras Bundle.
How do you apply this principle in Android when the things the component needs are not Parcelable or Serializable?
What I use for that is Dagger2, where you only depend on the object graph (or one of its subscoped extended subgraphs) to receive all your dependencies.
Vaguely it works like this,
Singleton
.
#Component(modules={SingletonModule.class})
#Singleton
public interface SingletonComponent {
Foo foo();
Bar bar();
void inject(MainActivity mainActivity);
}
#Module
public class SingletonModule {
#Provides
#Singleton
public Bar bar() {
return new Bar();
}
#Provides
#Singleton
public Foo foo(Bar bar) {
return new Foo(bar);
}
}
public class CustomApplication extends Application {
SingletonComponent singletonComponent;
#Override
public void onCreate() {
super.onCreate();
singletonComponent = DaggerSingletonComponent.builder()
.singletonModule(new SingletonModule())
.build();
}
public SingletonComponent getSingletonComponent() {
return singletonComponent;
}
}
public class MainActivity extends Activity {
#Inject
Foo foo;
#Inject
Bar bar;
#Override
public void onCreate(Bundle saveinstanceState) {
super.onCreate(saveinstanceState);
((CustomApplication)getApplicationContext()).getSingletonComponent().inject(this);
bar.doSomething();
foo.doSomething();
}
}
Subscoping
.
#Component(modules=SingletonModule.class)
#Singleton
public interface SingletonComponent {
Foo foo();
}
#Component(dependencies={SingletonComponent.class}, modules={MainActivityModule.class})
#ActivityScope
public interface MainActivityCompoent extends SingletonComponent {
Bar bar();
void inject(MainActivity mainActivity);
}
#Module
public class SingletonModule {
#Provides
#Singleton
public Foo foo() {
return new Foo();
}
}
#Module
public class MainActivityModule {
#Provides
#ActivityScope
public Bar bar(Foo foo) {
return new Bar(foo);
}
}
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface ActivityScope {
}
public class CustomApplication extends Application {
SingletonComponent singletonComponent;
#Override
public void onCreate() {
super.onCreate();
singletonComponent = DaggerSingletonComponent.builder()
.singletonModule(new SingletonModule())
.build();
}
public SingletonComponent getSingletonComponent() {
return singletonComponent;
}
}
public class MainActivity extends Activity {
#Inject
Foo foo;
#Inject
Bar bar;
private MainActivityComponent mainActivityComponent;
#Override
public void onCreate(Bundle saveinstanceState) {
super.onCreate(saveinstanceState);
mainActivityComponent = DaggerMainActivityComponent.builder()
.singletonComponent(((CustomApplication)getApplicationContext()).getSingletonComponent())
.mainActivityModule(new MainActivityModule())
.build();
mainActivityComponent.inject(this);
bar.doSomething();
foo.doSomething();
}
}

Unable to inject class with Dagger 2

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;
}

Categories

Resources