I am trying to understand dagger.android framework that is included in Dagger 2.11. I wrote a sample code that implements some scopes, @Singleton, @ActivityScope and @FragmentScope.
My Activity has a fragment, and fragment has a Toy object. I want that MainFragment belong to Activity Scope and Toy object belong to Fragment scope.
But I have an error, Could you help me please? What is the problem? :
Error:(22, 8) error: [dagger.android.AndroidInjector.inject(T)] com.example.user.daggerapplication4.Models.Toy cannot be provided without an @Provides-annotated method. com.example.user.daggerapplication4.Models.Toy is injected at com.example.user.daggerapplication4.ui.MainFragment.toy com.example.user.daggerapplication4.ui.MainFragment is injected at com.example.user.daggerapplication4.ui.MainActivity.injectedFragment com.example.user.daggerapplication4.ui.MainActivity is injected at dagger.android.AndroidInjector.inject(arg0) A binding with matching key exists in component: com.example.user.daggerapplication4.ui.MainActivityModule_BindMainFragment.MainFragmentSubcomponent
AppComponent and Module :
@Singleton
@Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
ActivityBuilder.class
})
public interface AppComponent extends AndroidInjector<DaggerSample4Application> {
@Component.Builder
abstract class Builder extends AndroidInjector.Builder<DaggerSample4Application> {}
}
@Module
public class AppModule {
}
@Module
public abstract class ActivityBuilder {
@ActivityScoped
@ContributesAndroidInjector(modules = MainActivityModule.class)
abstract MainActivity bindMainActivity();
}
Scopes :
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface FragmentScoped {}
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScoped {}
ActivityModule and FragmentModule
@Module()
public abstract class MainActivityModule {
@FragmentScoped
@ContributesAndroidInjector(modules = MainFragmentModule.class)
abstract MainFragment bindMainFragment();
}
@Module
public class MainFragmentModule {
@Provides
@FragmentScoped
Toy provideToy()
{
return new Puzzle();
}
}
Model Classes:
public interface Toy {
public String play();
}
public class Puzzle implements Toy {
@Override
public String play() {
Log.v("DaggerSample","Play with Puzzle");
return "Play with Puzzle";
}
}
MainActivity and MainFragment
public class MainActivity extends DaggerAppCompatActivity {
@Inject
MainFragment injectedFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainFragment mainFragment = (MainFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);
// injectedFragment = new MainFragment();
if (mainFragment == null) {
mainFragment = injectedFragment;
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.contentFrame, mainFragment);
transaction.commit();
}
}
}
public class MainFragment extends DaggerFragment {
private Button btnBuy;
private TextView textResult;
@Inject
Toy toy;
@Inject
public MainFragment()
{
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_main, container, false);
btnBuy = root.findViewById(R.id.btnBuy);
textResult = root.findViewById(R.id.textRresult);
btnBuy.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
showMessage(toy.play());
}
});
return root;
}
public void showMessage(String message) {
textResult.setText(message);
}
}
If you want to investigate the code, you can access with this link
@Injectfrom the fragments constructor. Dagger will try to inject the fields after creating the fragment, all from your ActivityComponent, hence your error. I suggest you create the fragment without dagger - David Medenjak