android - MVVM Dagger2 A Binding With Matching Key Exists in Component -
i using following google sample project: https://github.com/googlesamples/android-architecture-components reference new project , having difficulties trying add second activity project.
here error when compiling
error:(22, 8) error: [dagger.android.androidinjector.inject(t)] com.apps.myapp.ui.common.mainactivity cannot provided without @inject constructor or @provides-annotated method. type supports members injection cannot implicitly provided. com.apps.myapp.ui.common.mainactivity injected @ com.apps.myapp.ui.common.navigationcontroller.<init>(mainactivity) com.apps.myapp.ui.common.navigationcontroller injected @ com.apps.myapp.ui.addcontacts.addcontactsfragment.navigationcontroller com.apps.myapp.ui.addcontacts.addcontactsfragment injected @ dagger.android.androidinjector.inject(arg0) binding matching key exists in component: com.apps.myapp.di.activitymodule_contributemainactivity.mainactivitysubcomponent
here 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 users"); string tag = "users"; addcontactsfragment userfragment = addcontactsfragment.create(); fragmentmanager.begintransaction() .replace(containerid, userfragment, tag) .addtobackstack(null) .commitallowingstateloss(); } public void navigatetocontacts() { log.i("tag", "navigate contacts"); string tag = "contacts"; contactsfragment contactsfragment = contactsfragment.create(); fragmentmanager.begintransaction() .add(contactsfragment, tag) .addtobackstack(null) .commitallowingstateloss(); } public void navigatetochalkboard() { log.i("tag", "navigate 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; } }
the example on basing project on says in notes:
an advanced sample uses architecture components, dagger , github api. requires android studio 3.0 canary 1
the way learn new technology start simple, toy-like examples , gradually move way more complex examples once clear on basic concepts. there no need embarassed starting simple. has added advantage of being easy request if goes wrong since example minimal.
in particular case, error getting can reproduced by:
cloning project github
git clone https://github.com/googlesamples/android-architecture-components.git`
copy-pasting
mainactivity
new filecontactsactivity
modifying
mainactivitymodule
activitymodule
so can conclude activitymodule
problematic. @contributesandroidinjector
not simple might seem. means creating new dagger 2 subcomponent activity specify there (see docs here).
a subcomponent can use bindings parent component not sibling components. 2 lines contributesandroidinjector
in activitymodule
create 2 sibling subcomponents: 1 mainactivity
, 1 contactsactivity
.
however, navigationcontroller
dependent on mainactivity
bound in object graph mainactivity
subcomponent not in contactsactivity
subcomponent. addcontactsfragment
has become part of object graph contactsactivity
subcomponent , doesn't have access mainactivity
anymore. means when dagger 2 tries inject navigationcontroller
inside addcontactsfragment
cannot provide mainactivity
dependency it. explains "cannot provide" part of error message.
although can't provide mainactivity
in particular object graph, androidinjector
does know in general mainactivity
hence error message "a binding key exists". binding key this? key binds mainactivity.class
mainactivityfactory
. key bound? in activitymodule
when wrote @contributesandroidinjector
mainactivity
.
how fix getting beyond scope of stackoverflow question because involves lengthy refactor of code. need re-organise object graph navigationcontroller
no longer depends on mainactivity
. perhaps make depend on appcompatactivity
since superclass of both activities. need stop using contributesandroidinjector
, write explicit modules 2 activities include bindings appcompatactivity
.
however, please go basics , start easier. recipe disaster start complex project without complete understanding , modify hoping work.
the codepath dagger 2 tutorial project easier understand , familiar basic concepts involved in dagger 2. once comfortable basic concepts , have understood dependent components , sub-components, can attempt more difficult example. luck!
Comments
Post a Comment