dagger icon indicating copy to clipboard operation
dagger copied to clipboard

Poor error messaging with Subclass and Extended Graph

Open vinc3m1 opened this issue 11 years ago • 2 comments
trafficstars

update2: turns out I forgot about a subclass! but all the error messaging and behavior seen in comments 1 and 2 were super confusing.

update: The following seems to be a result of having a no-args constructor on TabAdapter, once I add arguments it crashes. See second comment for details.

I'm seeing a weird bug with @Singleton in one of my extended graphs (an activity containing a fragment).

If I provide an Adapter with a @Provides function, I get multiple instances of the Adapter:

  @Provides @Singleton TabAdapter provideTabAdapter() {
    Timber.d("vmi provide Tab Adapter");
    return new TabAdapter();
  }

when logging the hashcode and constructor, you'll see the objectgraph is the same, but the TabAdapter is constructed twice (also notice provide Tab Adapter is only logged once, so maybe the first time dagger is directly creating from the contructor?):

02-27 14:45:06.844  D/DashApp﹕ vmi injecting objectGraph: 1107898536
02-27 14:45:06.844  D/TabAdapter﹕ vmi tabadapter constructor
02-27 14:45:06.844  D/VenueActivity﹕ vmi activity adapter: 1109289504
02-27 14:45:07.253  D/DashApp﹕ vmi injecting objectGraph: 1107898536
02-27 14:45:07.255  D/VenueModule﹕ vmi provide Tab Adapter
02-27 14:45:07.259  D/TabAdapter﹕ vmi tabadapter constructor
02-27 14:45:07.260  D/TabFragment﹕ vmi fragment adapter: 1108907360

if instead if use constructor injection, @Singleton works fine:

@Singleton
public class TabAdapter extends BaseAdapter {

  @Inject
  public TabAdapter() {
    Timber.d("vmi tabadapter constructor");
  }
  // ...
}

and you'll see the hashcodes are now the same and the constructor is only called once:

02-27 14:47:00.834  D/DashApp﹕ vmi injecting objectGraph: 1109771688
02-27 14:47:00.835  D/TabAdapter﹕ vmi tabadapter constructor
02-27 14:47:00.836  D/VenueActivity﹕ vmi activity adapter: 1108594168
02-27 14:47:01.344  D/DashApp﹕ vmi injecting objectGraph: 1109771688
02-27 14:47:01.345  D/TabFragment﹕ vmi fragment adapter: 1108594168

Shouldn't these 2 methods have the same expected behavior?

vinc3m1 avatar Feb 27 '14 19:02 vinc3m1

So after some more tinkering I've noticed that the above was happening because of the no args constructor that TabAdapter has which dagger was using to create an instance with. I haven't been able to reproduce the above again, instead I get crashes.

My setup is as follows:

  • ApplicationModule is the base Application ObjectGraph
  • VenueModule is the activity scope module, correctly defined with addsTo
  • VenueActivity has TabFragment as a child.

VenueModule annotation:

@Module(
    injects = {
        TabFragment.class,
        VenueActivity.class
    },
    addsTo = AppModule.class,
)
public class VenueModule {
  @Provides @Singleton TabAdapter provideTabAdapter() {
    // if was is a no-args constructor, dagger still finds it and creates an instance
    return new TabAdapter("asdfads");
  }
}

My VenueActivity is as follows:

private ObjectGraph objectGraph;
@Inject SessionManager sessionManager; // this comes from ApplicationModule
@Inject TabAdapter mAdapter; // this comes from VenueModule

@Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    objectGraph = DashApp.get(this).getObjectGraph().plus(new VenueModule(this));
    objectGraph.inject(this);
    // ...
}

TabFragment also uses the VenueActivity extended ObjectGraph to inject itself in onActivityCreated

This is the weird part:

  • All injection works fine on the Fragment
  • All injection passes compile time validation
  • If I try to inject anything from VenueModule into VenueActivity, it crashes and doesn't see the @Provides functions: me.dashwith.ui.venue.tab.TabAdapter has no injectable members. Do you want to add an injectable constructor? required by class me.dashwith.ui.venue.VenueActivity
  • If I remove the injections from VenueModule in VenueActivity, things work fine

So why wouldn't the Activity be able to inject itself if the Fragment injection works fine? Is the objectgraph not ready if I inject with it right away? This doesn't seem to make sense...

vinc3m1 avatar Feb 27 '14 22:02 vinc3m1

Wow. Turns out I had a subclass of VenueActivity, DemoVenueActivity, which I forgot to list under injects. I'm not sure if messaging can be improved in this case? or why that subclass causes problems?

vinc3m1 avatar Feb 28 '14 06:02 vinc3m1