Dagger 2.10/2.11 injecting Activity failing - android

I have been trying to, unsuccessfully, inject the Activity in a class ViewUtils. I have followed a couple of different posts but I can't seem to understand what am I missing in my implementation.
I know this is probably a repetition of the posts below and I really apologize for that but I honestly cannot see what am I missing. These are the posts I've found:
Dagger 2.10 Android subcomponents and builders
How to create custom scoped modules in dagger 2.10
https://google.github.io/dagger/subcomponents.html
My implementation is as follows:
AppComponent
#Component(modules = {
AppModule.class, AndroidSupportInjectionModule.class, ActivityBindingModule.class
}) #Singleton public interface AppComponent extends AndroidInjector<EmblyApp> {
#Component.Builder abstract class Builder extends AndroidInjector.Builder<EmblyApp> {}
}
ActivityBindingModule
#Module public abstract class ActivityBindingModule {
#ContributesAndroidInjector
abstract LoginActivity loginActivity();
}
LoginSubcomponent
#Subcomponent(modules = LoginSubcomponent.LoginActivityModule.class)
public interface LoginSubcomponent extends AndroidInjector<LoginActivity> {
#Subcomponent.Builder abstract class Builder extends AndroidInjector.Builder<LoginActivity> {}
#Module abstract class LoginActivityModule {
#Binds abstract Activity bindActivity(LoginActivity activity);
#Provides #ActivityScope static ViewUtils viewUtils(Activity activity) {
return new ViewUtils(activity);
}
}
}
ViewUtils
public class ViewUtils {
private final Activity activity;
#Inject public ViewUtils(Activity activity) {
this.activity = activity;
}
}
And the error i'm getting is:
Error:(14, 22) error: [dagger.android.AndroidInjector.inject(T)] android.app.Activity cannot be provided without an #Inject constructor or from an #Provides-annotated method.
android.app.Activity is injected at
com.emblyapp.app.ui.helpers.ViewUtils.<init>(activity)
com.emblyapp.app.ui.helpers.ViewUtils is injected at
com.emblyapp.app.ui.authentication.login.LoginActivity.viewUtils
com.emblyapp.app.ui.authentication.login.LoginActivity is injected at
dagger.android.AndroidInjector.inject(arg0)
What is wrong in here? Thanks for the help!
Edit: I forgot to mention my LoginActivity has the injection with the AndroidInjection
#Override protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
}

As specified in dagger android documentation:
Pro-tip: If your subcomponent and its builder have no other methods or supertypes than the ones mentioned in step #2, you can use #ContributesAndroidInjector to generate them for you. Instead of steps 2 and 3, add an abstract module method that returns your activity, annotate it with #ContributesAndroidInjector, and specify the modules you want to install into the subcomponent. If the subcomponent needs scopes, apply the scope annotations to the method as well.
Thus, we can get rid of LoginSubcomponent and perform following changes in ActivityBindingModule:
#Module
public abstract class ActivityBindingModule {
#ActivityScope
#ContributesAndroidInjector(modules = LoginActivityModule.class)
abstract LoginActivity loginActivity();
}
LoginActivityModule.java
#Module
abstract class LoginActivityModule {
#Binds
abstract Activity bindActivity(LoginActivity activity);
#Provides
#ActivityScope
static ViewUtils viewUtils(Activity activity) {
return new ViewUtils(activity);
}
}
Your custom application class:
public class MyApp extends DaggerApplication {
#Inject
DispatchingAndroidInjector dispatchingActivityInjector;
#Override
protected AndroidInjector applicationInjector() {
return DaggerAppComponent.builder().create(this);
}
}

Related

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.

How to add DI (Dagger 2) in Activity and its parent

I use Dagger 2 in my android project and want to use the #Inject in an Activity and inside another Activity which extends the first one -> MainActivity extends NetworkBaseActivity. In both Activities inside the onCreate() methods I have this:
AndroidInjection.inject(this);
Also, I have the next structure:
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
ActivityBuilderModule.class,
WebServiceModule.class})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
void inject(App app);
}
#Module
public abstract class ActivityBuilderModule {
#ContributesAndroidInjector(modules = {NetworkActivityModule.class})
public abstract NetworkBaseActivity bindNetworkBaseActivity();
#ContributesAndroidInjector(modules = {MainActivityModule.class})
public abstract MainActivity bindMainActivity();
}
#Module
public class NetworkActivityModule {
#Provides
public NetworkViewModelFactory
provideNetworkViewModelFactory(AuthRepository authRepository) {
return new NetworkViewModelFactory(authRepository);
}
#Provides
public AuthRepository provideAuthRepository(WebServiceApi webServiceApi,
SharedPreferencesManager sharedPreferencesManager) {
return new AuthRepository(webServiceApi, sharedPreferencesManager);
}
}
#Module
public class MainActivityModule {
}
And in the onCreate() callback of my Application class I have this:
DaggerAppComponent
.builder()
.application(this)
.build()
.inject(this);
But receive this error:
error: [Dagger/MissingBinding] [dagger.android.AndroidInjector.inject(T)]
com.example.android.view.base.NetworkViewModelFactory cannot be provided
without an #Inject constructor or an #Provides-annotated method.
A binding with matching key exists in component:
com.example.android.data.di.ActivityBuilderModule_BindNetworkBaseActivity
.NetworkBaseActivitySubcomponent
com.example.android.view.base.NetworkViewModelFactory is injected at
com.example.android.view.base.NetworkBaseActivity.mViewModelFactory
com.example.android.view.main.MainActivity is injected at
dagger.android.AndroidInjector.inject(T)
component path: com.example.android.data.di.AppComponent ?
com.example.android.data.di.ActivityBuilderModule_BindMainActivity
.MainActivitySubcomponent
Any suggestions? :)
As MainActivity extends from NetworkBaseActivity you just need an AndroidInjector for the MainActivity. NetworkBaseActivity will then be injected through MainActivity.
#Module
public class NetworkActivityModule {
#ContributesAndroidInjector(modules = { NetworkActivityModule.class })
public abstract MainActivity bindMainActivity();
}
You have to inject the constructor of your NetworkViewModelFactory class. The constructor should look something like this:
#Inject
public NetworkViewModelFactory(AuthRepository authRepository) { }
But I'm not sure, it would be better to see the NetworkViewModelFactory class code too.

Dependency injection in a scheduled JobService with Dagger 2

What's the best way to inject dependencies in a scheduled jobservice in Android.
My JobService is scheduled to run in the night to do some stuff.
In JobService constructor i'm trying to inject my dependencies over my Application class.
MyApp.component().inject(this);
But sometimes MyApp isn't initialized at this time and so the injection failes.
Maybe i'm using Dagger in a wrong way? Or do i have to create an own component for the JobService?
Here is my Application class
public class MyApp extends Application {
private static AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
buildAppComponent();
}
public static AppComponent component(){
return appComponent;
}
private void buildAppComponent(){
if(appComponent == null){
appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
}
}
}
You should use AndroidInjector to inject android classes such as Activities/Fragments/Services/BroadcastReceivers/ContentProviders.
First make sure you added dagger-android dependency from your build.gradle
implementation 'com.google.dagger:dagger:2.16'
implementation 'com.google.dagger:dagger-android:2.16'
implementation 'com.google.dagger:dagger-android-support:2.16'
annotationProcessor 'com.google.dagger:dagger-compiler:2.16'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.16'
Then make sure you app component inherit from AndroidInjector
#Singleton
#Component(modules = {AndroidSupportInjectionModule.class,
ApplicationModule.class,
ActivityBuilderModule::class,
ServiceBuilderModule::class})
public interface ApplicationComponent extends AndroidInjector<MyApp> {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(MyApp application);
ApplicationComponent build();
}
}
ActivityBuilderModule and ServiceBuilderModule reference all you activitiy and service subcomponents using an handy annotation ContributesAndroidInjector that will generate the subcomponent automatically for you
#Module
abstract class ActivityBuilderModule {
#ContributesAndroidInjector(modules = {MainActivityModule.class})
#ActivityScope
abstract MainActivity contributeMainActivity();
#ContributesAndroidInjector(modules = {DummyModule.class})
#ActivityScope
abstract DummyActivity contributeDummyActivity();
//...
}
same for services
#Module
abstract class ServiceBuilderModule {
#ContributesAndroidInjector
abstract MyService contributeAuthenticatorService();
//...
}
Finally here how your MyApp should look like
public class MyApp extends DaggerApplication() {
private ApplicationComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerApplicationComponent.builder()
.application(this)
.build();
}
AndroidInjector<? extends DaggerApplication> applicationInjector(){
return appComponent;
}
}
Your service should now be injectable, to wrap this up you'll certainly want to inject fragments as well so for example for MainActivity fragments you'll make a FragmentBuilderModule from your MainActivityModule
#Module(includes = {FragmentBuilderModule.class})
abstract class MainActivityModule {
#Binds
#ActivityScope
abstract AppCompatActivity bindActivity(MainActivity activity);
//...
}
and here the FragmentBuilderModule class
#Module
abstract class FragmentBuilderModule {
#ContributesAndroidInjector(modules = {HomeFragmentModule.class})
#FragmentScope
abstract HomeFragment contributeHomeFragment();
#ContributesAndroidInjector(modules = DummyFragmentModule.class})
#FragmentScope
abstract DummyFragment contributeDummyFragment() ;
//...
}
You can see more from my project template here though it's kotlin.

Dagger with Conductor Controllers

I am experimenting with the 'new' Android support in Dagger 2.
I want to set up the following architecture in Dagger:
Application => Activity => Controller (Conductor)
(Controller is basically a View that gets instantiated by the system. You can think of it like Fragments but without Dagger Injection support)
For each level I have defined a dependency: ApplicationDep, ActivityDep and ControllerDep.
My Controller should be able to inject all of these dependencies.
My Activity should be able to inject the ApplicationDep and the ActivityDep
My Application should only be able to inject the ApplicationDep
Everything works except in my Controller.
I am unable to inject the ActivityDep.
public class MyController extends Controller {
#Inject
ApplicationDependency applicationDependency;
//#Inject
//ActivityDependency activityDependency; // can not be provided
#Inject
ControllerDependency controllerDependency;
#NonNull #Override protected View onCreateView(#NonNull LayoutInflater layoutInflater, #NonNull ViewGroup viewGroup) {
ConductorInjection.inject(this);
return layoutInflater.inflate(R.layout.controller_main, viewGroup, false);
}
}
Currently I have my ControllerBindingModule bound on my ApplicationComponent, however this should be bound on the Activity in order for it to be also available in the Controller.
How can I do this?
#Singleton
#Component(modules = {
ApplicationModule.class,
ActivityBindingModule.class,
AndroidSupportInjectionModule.class,
ConductorInjectionModule.class,
ControllerBindingModule.class
})
interface AppComponent extends AndroidInjector<App> {
#Component.Builder
abstract class Builder extends AndroidInjector.Builder<App> {}
}
The full code can be found on Github.
Thanks.
It sounds like controller is a subcomponent of activity component.
I took a look at your GitHub, so I change some of your code to answer.
First, for the Activity injection.
Controller is not subcomponent of Appcomponent, so it only need ActivityBindingModule.
AppComponent.java
#Singleton
#Component(modules = {
AppModule.class
ActivityBindingModule.class,
AndroidSupportInjectionModule.class,
})
interface AppComponent {
#Component.Builder
abstract class Builder extends AndroidInjector.Builder<App> {}
}
For the same reason, we only need to implement HasActivityInjector in App.
App.java
public class App extends Application implements HasActivityInjector {
#Inject protected DispatchingAndroidInjector<Activity> dispatchingActivityInjector;
#Override
public AndroidInjector<Activity> activityInjector() {
return dispatchingActivityInjector;
}
#Override
public void onCreate() {
super.onCreate();
DaggerAppComponent
.builder()
.create(this)
.inject(this);
}
}
Because I need to declare Controller as subcomponent of ActivityComponent,
I use step 2 & step 3 in Dagger documentation about injecting activity objects
Change your ActivityBindingModule
ActivityBindingModule.java
#Module(subcomponents = {MainActivityComponent.class})
public abstract class ActivityBindingModule {
#Binds
#IntoMap
#ActivityKey(MainActivity.class)
abstract AndroidInjector.Factory<? extends Activity>
bindYourActivityInjectorFactory(MainActivityComponent.Builder builder);
}
Create an ActivityComponent.
MainActivityComponent.java
#Subcomponent(modules = {MainActivityModule.class})
#ActivityScope
public interface MainActivityComponent extends AndroidInjector<MainActivity>{
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity> {}
}
Next, it time to set controller as a subcomponent of activitycomponent.
Add ControllerBindingModule.class to MainActivityComponent.
#Subcomponent(modules = {MainActivityModule.class, ControllerBindingModule.class})
#ActivityScope
public interface MainActivityComponent extends AndroidInjector<MainActivity>{
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity> {}
}
And implement HasControllerInjector in MainActivity.
MainActivity.java
public class MainActivity extends AppCompatActivity implements HasControllerInjector {
#Inject protected DispatchingAndroidInjector<Controller> dispatchingControllerInjector;
#Override
public DispatchingAndroidInjector<Controller> controllerInjector() {
return dispatchingControllerInjector;
}
No need to change other files, and what you want is done.

Dagger 2 Android: inject the scoped activity

I'm using Dagger Android to link dependencies in my Android application.
I have 2 Activities, both have their own Scope (#ActivityScoped).
Here is my code so far :
MyApplication.java
public class MyApplication extends Application implements HasActivityInjector {
#Inject DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;
#Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.builder()
.application(this)
.build()
.inject(this);
}
#Override
public AndroidInjector<Activity> activityInjector() {
return activityDispatchingAndroidInjector;
}
}
AppComponent.java
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
ActivityBindingModule.class
})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application app);
AppComponent build();
}
void inject(MyApplication app);
}
ActivityBindingsModule.java
#Module(subcomponents = ActivityComponent.class)
public abstract class ActivityBindingModule {
#ActivityScoped
#ContributesAndroidInjector
public abstract MainActivity contributeMainActivityInjector();
#ActivityScoped
#ContributesAndroidInjector
public abstract SecondActivity contributeSecondActivityInjector();
}
Let's say I have a class MyClass tied to ActivityScoped that I want to inject into both Activities :
MyClass.java
#ActivityScoped
public class MyClass {
#Inject
public MyClass(Activity activity) {
// Do something with the activity instance...
}
}
What I want to achieve is to inject into MyClass the Activity instance of the enclosing scope.
For example, if MyClass is injected into ActivityA, then the activity constructor argument of MyClass should be an instance of ActivityA.
What I tried :
#Binds annotated method to inject ActivityA and ActivityB as Activity. This doesn't work since Dagger does not allow binding multiple implementations to the same base type.
Map Multibindings doesn't suit my needs : I don't want MyClass to know about the Activity in which it is injected.
Maybe I'm missing something, as dependency injection is rather hard to setup. How can I do that with Dagger Android ?
#Binds annotated method to inject ActivityA and ActivityB as Activity. This doesn't work since Dagger does not allow binding multiple implementations to the same base type.
That's the right way to do so. You only get an error when you try to bind multiple implementations to the same type in the same component. So you should not bind both within your AppComponent, but to the #ActivityScoped subcomponents.
You have to create a module for each Activity, A and B, that binds the implementation to Activity. Then you just add that module to the Subcomponent that Dagger creates for you.
#ActivityScoped
#ContributesAndroidInjector(modules={MainActivityBindingsModule.class})
public abstract MainActivity contributeMainActivityInjector();
That way the binding only exists within the #ActivityScoped component and you should be able to repeat it.

Categories

Resources