dagger cannot inject type parameter field - android

I'm working on an android application and I'm trying to inject a field which is type parameterized in an abstract class : BaseListFragment
public abstract class BaseListFragment<E, A extends ArrayAdapter<E>, S> extends BaseFragment
{
#Inject protected S service;
}
But I get this following error at compile :
error: cannot find symbol class S
Here is my code for BaseFragment :
public class BaseFragment extends Fragment
{
#Override public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
((App) getActivity().getApplication()).inject(this);
}
}
here is my service module :
#Module(
complete = false,
library = true
)
public class ServiceModule
{
#Provides #Singleton BucketService provideBucketService(RestAdapter restAdapter)
{
return restAdapter.create(BucketService.class);
}
#Provides #Singleton ProjectService provideProjectService(RestAdapter restAdapter)
{
return restAdapter.create(ProjectService.class);
}
#Provides #Singleton ShotService provideShotService(RestAdapter restAdapter)
{
return restAdapter.create(ShotService.class);
}
#Provides #Singleton TeamService provideTeamService(RestAdapter restAdapter)
{
return restAdapter.create(TeamService.class);
}
#Provides #Singleton UserService provideUserService(RestAdapter restAdapter)
{
return restAdapter.create(UserService.class);
}
}
And here is an example of a class extending BaseListFragment :
public class ProjectFragment extends BaseEntityFragment<Project, ProjectViewAdapter, ProjectService>
{
}
Is there anyway to inject a parameterized type ?
Regards,

As of the 2.0.1 release, Dagger 2 does support the type of injection that you're talking about. Take a look at GenericTest for all of the various permutations.

I had the same problem and got around it by adding a non-parameterized inner class and injecting into that. Then using a getter to get the injected class out.
It looks like this:
public class MainClass<T>{
UtilityClass utility;
public MainClass(){
utility = new InjectorHelper().getHelper();
}
...
public static class InjectorHelper{
#Inject
UtilityClass utilityClass;
public InjectorHelper(){
Injector.getInstance().getAppComponent().inject(this);
}
public UtilityClass getUtilityClass(){
return utilityClass
}
}
}

I also could not get it work either and I think it's against the design philosophy of dagger, which is to generate code that is exactly what a developer would also write.
In this case, it generates an injection adapter for the abstract class and another one for concrete class. With generic arguments to the abstract class, you essentially have to inject the fields from the abstract class in each concrete class injector.
If it wasn't an android object (which the android runtime creates), I'd suggest to pass the service in the call to the super constructor.
In this case, I'd suggest to inject the service in the concrete classes and to provide it using an abstract method:
protected abstract S getService();

Related

Hilt migration for interface having multiple implementations and injected in one generic file constructor gives error: [Dagger/DuplicateBindings]

I am trying to migrate my existing Dagger implementation to Hilt.
I have one common interface which is implemented by multiple classes.
public interface SomeInterface {
void someMethod();}
Then there are implementation classes
public class First implements SomeInterface {
#Inject
public First(
#NonNull final SomeParam someParam){
this.someParam = someParam;
}
}
public class Second implements SomeInterface {
#Inject
public Second(
#NonNull final SomeParam someParam){
this.someParam = someParam;}}
I have one presenter which has this interface injected in constructor and the presenter is in turn injected in one fragment
public class OnePresenter {
#Inject
public OnePresenter(SomeInterface interface) {}
Now, when I try to migrate this hierarchy using Hilt module, I am getting Duplicate Bindings error
#Module
#InstallIn(SingletonComponent.class)
public class InterfaceBindingModule {
#Provides
public SomeInterface getFirst(First first) {
return (First) first;
}
#Provides
public SomeInterface getSecond(Second second) {
return (Second) second;
}}
error: [Dagger/DuplicateBindings] com.package.interface.SomeInterface is bound multiple times:
At the moment Dagger does not know what implementation of SomeInterface, it needs to provide. You have two methods providing the same type SomeInterface. You need to distinguish them somehow.
Use Named annotation:
#Module
#InstallIn(SingletonComponent.class)
public class InterfaceBindingModule {
#Provides
#Named("First")
public SomeInterface getFirst(First first) {
return (First) first;
}
#Provides
#Named("Second")
public SomeInterface getSecond(Second second) {
return (Second) second;
}}
And then declare which instance you want:
public class OnePresenter {
#Inject
public OnePresenter(#Named("First") SomeInterface interface) {}

Dagger 2 - Cannot inject members into raw type

I need to have three levels of inheritance and inject with dagger2
1.MainActivity
--1.1 MainSubActivity
----1.1.1 MainSubActivityOne
----1.1.2 MainSubActivityTwo (The same structure as MainSubActivityOne)
MainActivity
public abstract class MainActivity<T extends MainPresenter> extends BaseActivity implements MainView{
#Inject
protected T mPresenter;
}
MainPresenter
public abstract class MainPresenter<T extends MainView> extends BasePresenter<T> { ... }
MainView
public interface MainView extends BaseView{ ... }
-- MainSubActivity
public abstract class MainSubActivity extends MainActivity<MainSubPresenter> implements MainSubView { ... }
-- MainSubPresenter
public abstract class MainSubPresenter<T extends MainSubView> extends MainPresenter<T> { ... }
-- MainSubView
public interface MainSubView extends MainView { ... }
---- MainSubActivityOne (Same as MainSubActivityTwo):
public class MainSubActivityOne extends MainSubActivity implements MainSubViewOne{
#Override
protected void onCreatePresenter(){
mPresenter.setView(this);
mPresenter.onCreate();
}
#Override
protected void initializeDagger() {
getActivityComponent().inject(this);
}
}
---- MainSubPresenterOne (Same as MainPresenterTwo):
public class MainSubPresenterOne extends MainSubPresenter<MainSubViewOne> { ... }
---- MainSubViewOne (Same as MainSubViewTwo):
public interface MainSubViewOne extends MainSubView { ... }
ActivityComponent
#PerActivity
#Component(dependencies = ApplicationComponent.class, modules =
{ActivityModule.class})
public interface ActivityComponent {
void inject(MainActivity mainActivity);
}
ActivityModule
#Provides
#PerActivity
MainPresenter provideMainPresenter() {
return new MainSubPresenterOne();
}
When I had only two levels, all is ok, but now I obtain this error:
...components/ActivityComponent.java:90: error: [Dagger/MembersInjection] Cannot inject members into raw type com.example.main.MainActivity
void inject(MainActivity mainActivity);
^
com.example.main.MainActivity is injected at
...components.ActivityComponent.inject(com.example.main.MainActivity)
If I change the activityComponent to:
void inject(MainSubActivityOne activity);
void inject(MainSubActivityTwo activity);
I obtain the next error instead:
.../components/ActivityComponent.java:92: error: [Dagger/MissingBinding] com.example.main.MainSubPresenterOne cannot be provided without an #Provides-annotated method.
void inject(MainSubActivityOne mainActivity);
^
com.example.main.MainSubPresenter is injected at
com.example.main.MainActivity.mPresenter
com.example.main.MainSubPresenterOne is injected at
...components.ActivityComponent.inject(com.example.main.MainSubActivityOne)
This line is your problem:
void inject(MainActivity mainActivity);
MainActivity<T> needs a generic type argument, but that's irrelevant. It's an abstract class. You're not injecting this common parent class. You're injecting the instances of its concrete children. Here's what you should do instead:
void inject(MainSubActivityOne activity);
void inject(MainSubActivityTwo activity);
[Dagger/MissingBinding] com.example.main.MainSubPresenterOne cannot be provided without an #Provides-annotated method.
This is all true. Your MainSubActivityOne expects a MainSubPresenterOne here:
#Inject
protected T mPresenter;
Yet you only created a binding for MainPresenter:
#Provides
#PerActivity
MainPresenter provideMainPresenter() {
return new MainSubPresenterOne();
}
This means that Dagger knows only how to inject a MainPresenter, it doesn't care that the MainPresenter is actually a MainSubPresenterOne.
Instead, what I would do is to scope the concrete presenters and let them have an #Inject constructor:
#PerActivity
public class MainSubPresenterOne extends MainSubPresenter<MainSubViewOne> {
#Inject
public MainSubPresenterOne() {
// ...
}
// ...
}
Now Dagger knows how to inject MainSubPresenterOne. Remove the #Provides method.
I recommend the official documentation, which – among other things – explains that #Provides is a kind of last resort and you should prefer #Inject on the types under your control.
Alternatively you would
#Inject
protected MainPresenter mPresenter;
and create a separate subcomponent for each of your activities with a module providing the actual presenter:
#Module
abstract class MainSubActivityOneModule {
#Binds
abstract MainSubPresenter<MainSubViewOne> bindMainPresenter(MainSubPresenterOne impl);
}
This assumes that the activity doesn't care about the concrete implementation of its presenter, which may or may not be what you want.

Dagger2.11 cannot be provided without an #Provides-annotated method

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
I think while using interface it is better to use #Binds.
Try the method below.
#Module
public class MainFragmentModule {
#FragmentScoped
#Binds
public abstract Toy bindToy(Puzzle puzzle);
}
public class Puzzle implements Toy {
#Inject
public Puzzle(){
}
#Override
public String play() {
Log.v("DaggerSample","Play with Puzzle");
return "Play with Puzzle";
}
}
You have a few errors to fix in your code to get your project to compile. But first, rule of thumb for efficient Dagger - always prefer making your modules as abstract classes with abstract #Binds methods or if not possible with static #Provides methods. This means you need to make AppModule an abstract class, otherwise your project won't compile as per the code you posted here.
The main reason why your code doesn't compile is because Puzzle doesn't have a constructor that is annotated with #Inject:
public class Puzzle implements Toy {
#Inject // Add this
public Puzzle() {
}
#Override
public String play() {
Log.v("DaggerSample","Play with Puzzle");
return "Play with Puzzle";
}
}
Next, you need to make the following changes to this module:
#Module
public class MainFragmentModule { // Make this class abstract
#Provides // Change this to #Binds instead
#FragmentScoped
Toy provideToy() // Change this method to look like this below method
{
return new Puzzle();
}
#Binds
#FragmentScoped
abstract Toy bindPuzzle(Puzzle puzzle);
}
If you have other classes that implement Toy interface that you want to inject, you'll have to use qualifiers (#Named annotation) to tell Dagger which implementation to inject.
You cannot inject a fragment to the activity that is hosting it. Instead, you must create the fragment and add it using the fragment manager instead.
public class MainActivity extends DaggerAppCompatActivity {
#Inject // Remove this
MainFragment injectedFragment; // And this if you don't use this field
You can't annotate the fragment constructor with #Inject. Fragment is an Android Component and Android Components cannot be injected via constructor injection. The only way you can inject Android Components is via member injection, which is already done for you if your fragment inherits from DaggerFragment. Notice that if you're using support library Fragments, make sure to use DaggerFragment variant which is from the support package.
You haven't included your DaggerSample4Application code so I can't tell if you're doing something wrong there, but the main point is that this class needs to extend DaggerApplication and implement some methods. I have a complete working sample that you can check out:
https://github.com/Nimrodda/dagger-androidinjector
It's the source code for an article I wrote about Dagger Android injection https://android.jlelse.eu/android-and-dagger-2-10-androidinjector-5e9c523679a3
I highly recommend you check it out to get better understanding.

How to inject into a java class that doesn't have any activity or fragment using dagger2

Android Studio 2.2.2
I have a NewsListModelImp class which is the model in the MVP.
I want to inject my retrofit service into the model. However, as NewsListModelImp doesn't contain any reference to a context or activity I cannot call getApplication(). Which is what you would do if you were in a activity or fragment. I don't want to pass any context or activity in the constructor of NewsListModeImp as that would have to come from the presenter and I want to avoid any android stuff there.
public class NewsListModelImp implements NewsListModelContract {
#Inject
NYTimesSearchService mNYTimesSearchService;
public NewsListModelImp() {
((NYTimesSearchApplication)getApplication()).getAppComponent().inject(this);
}
}
My Application class
public class NYTimesSearchApplication extends Application {
private AppComponent mAppComponent;
public void onCreate() {
super.onCreate();
/* Setup dependency injection */
createAppComponent();
}
private void createAppComponent() {
mAppComponent = DaggerAppComponent
.builder()
.retrofitModule(new RetrofitModule())
.build();
}
public AppComponent getAppComponent() {
return mAppComponent;
}
}
My provides module
#Module
public class RetrofitModule {
private Retrofit retrofit() {
return new Retrofit
.Builder()
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
#Provides
#Singleton
public NYTimesSearchService providesNYTimesSearch() {
return retrofit().create(NYTimesSearchService.class);
}
}
My Appcomponent
#Singleton
#Component(modules = {RetrofitModule.class})
public interface AppComponent {
void inject(NewsListModelImp target);
}
Many thanks for any suggestions,
Dagger-2 works reccurently. So if inside Activity (or Fragment) object is injected and it's constructor is properly annotated with #Inject annotation, the constructor's parameters will be injected too.
Suppose inside the application you would like to inject:
#Inject NyTimesPresenter presenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((NYTimesSearchApplication) getApplication()).getAppComponent().inject(this);
}
The constructor of NyTimesPresenter must be annotated with #Inject:
public class NyTimesPresenter {
NewsListModelImp newsListModel;
#Inject
public NyTimesPresenter(NewsListModelImp newsListModel) {
this.newsListModel = newsListModel;
}
}
NewsListModelImp constructor must also be annotated with #Inject:
public class NewsListModelImp implements NewsListModelContract {
NYTimesSearchService mNYTimesSearchService;
#Inject
public NewsListModelImp(NYTimesSearchService nYTimesSearchService) {
this.mNYTimesSearchService = nYTimesSearchService;
}
}
Then everything will be injected properly.
Why the parameters should be passed to class as constructors' parameters? Such design pattern conforms SOLID principles. Object dependencies are injected into objects and not created inside it and such code is easily testable (in tests dependencies can be replaced ie. by Mock's)
EXTRA INFO:
It is possible to inject objects implementing specific interfaces. Such technique is described here. In your case NyTimesPresenter can have NewsListModelContract as it's dependency instead of NewsListModelImp. To do this add another module to your AppComponent:
#Singleton
#Component(
modules = {
RetrofitModule.class,
AppModule.class
})
public interface AppComponent {
AppComponent method to provide concrete class implementing interface should look like:
#Singleton
#Module
public abstract class AppModule {
#Binds
public abstract NewsListModelContract provideNewsListModelContract(NewsListModelImp newsListModelImp);
}
The implementation of NyTimesPresenter should change (just to replace concrete class with interface it implements):
public class NyTimesPresenter {
NewsListModelContract newsListModel;
#Inject
public NyTimesPresenter(NewsListModelContract newsListModel) {
this.newsListModel = newsListModel;
}
}

Dagger 2 error: "RepositoryImpl cannot be provided without an #Inject constructor or from an #Provides-annotated method"

For example, I have following interface:
public interface Repository {
Observable<Pojo> getPojos();
}
And its implementation:
public class RepositoryImpl implements Repository {
public RepositoryImpl() {
}
#Override
public Observable<Pojo> getPojos() {
return null;
}
}
Module:
#Module
class AppModule {
public AppModule() {
}
#Provides
#Singleton
Repository provideRepositoryImpl() {
return new RepositoryImpl();
}
}
And component:
#Singleton
#Component(modules = { AppModule.class })
public interface AppComponent {
void inject(MainActivity mainActivity);
}
When I trying to build project, I receive error as in question title. What problem in my code?
Read your error carefully (emphasis mine):
Dagger 2 error: “RepositoryImpl cannot be provided without an #Inject constructor or from an #Provides-annotated method”
Generally this means you've tried to #Inject RepositoryImpl, not #Inject Repository. This is especially important because your Module directly calls the RepositoryImpl constructor rather than letting Dagger create your RepositoryImpl using an #Inject-annotated constructor. (If you had, you could make RepositoryImpl a parameter of your #Provides method or switch to a #Binds method, and you have your choice between injecting the interface versus the implementation.)
The way I setup Dagger 2 is in my projects Application I add the injection component. like so.
public class NyApplication extends Application {
InjectionComponent component;
#Override
public void onCreate() {
super.onCreate();
setDagger();
}
private void setDagger() {
component = DaggerAppComponent.builder()
.appComponent(new AppModule())
.build();
component.inject(this);
}
public InjectionComponent getComponent() {
return component;
}}
and then int my activity whatever it is. I inject on it's onCreate like this.
public class MainActivity extends Activity {
#Inject
Object object;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((MyApplication) getApplication()).getComponent().inject(this);
}}
I hope this helps you.

Categories

Resources