Dependency injection in a scheduled JobService with Dagger 2 - android

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.

Related

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.

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.

java.lang.IllegalArgumentException: No injector factory bound for Class<MyActivity_>

The error I have is following:
Caused by: java.lang.IllegalArgumentException: No injector factory
bound for Class. Injector factories were bound for
supertypes of MyActivity_: [MyActivity]. Did you mean to bind an
injector factory for the subtype?
As I understand it happens because I am using an AndroidAnnotations library.
AppComponent.class :
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
ActivityBindingModule.class })
public interface AppComponent extends AndroidInjector<DaggerApplication> {
#Component.Builder
interface Builder {
#BindsInstance Builder application(Application application);
AppComponent build();
}
void inject(Application application);
#Override
void inject(DaggerApplication instance);
}
AppModule.class :
#Module
public abstract class AppModule {
#Binds
abstract Context provideContext(Application application);
#Provides
#Singleton
static SharedPreferencesManager providesPreferences(Application application){
return SharedPreferencesManager_.getInstance_(application);
}
}
ActivityBindingModule.class :
#Module
public abstract class ActivityBindingModule {
#ContributesAndroidInjector(modules = LoginActivityModule.class)
#LoginActivityScope
abstract LoginActivity bindLoginActivity();
}
Application.class :
#EApplication
public class Application extends DaggerApplication {
#Inject
DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
AppComponent appComponent = DaggerAppComponent.builder()
.application(this)
.build();
appComponent.inject(this);
return appComponent;
}
}
LoginActivityModule.class
#Module
public class LoginActivityModule {
#Provides
#LoginActivityScope
#ActivityContext
public Context providesContext(LoginActivity loginActivity){
return loginActivity;
}
#Provides
#LoginActivityScope
public LoginViewModel providesLoginViewModel(TelephonyManager telephonyManager,
LoginModel loginModel,
SharedPreferencesManager sharedPreferencesManager,
LoginRemoteRepository loginRemoteRepository){
return new LoginViewModel(telephonyManager, loginModel, sharedPreferencesManager, loginRemoteRepository,
new CompositeSubscription());
}
#Provides
#LoginActivityScope
public LoginRemoteRepository providesRemoteRepository(#ActivityContext Context context,
MainApi mainApi,
SharedPreferencesManager sharedPreferencesManager){
return new LoginRemoteRepository(mainApi, sharedPreferencesManager, context.getContentResolver());
}
#Provides
#LoginActivityScope
public LoginModel provideLoginModel(){
return new LoginModel();
}
#Provides
#LoginActivityScope
public TelephonyManager provideTelephonyManager(Context context){
return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
}
}
LoginActivity.class :
#EActivity(R.layout.activity_login)
public class LoginActivity {
#Inject
LoginViewModel loginViewModel;
#AfterViews
void afterViews(){
AndroidInjection.inject(this);
}
}
How to deal with Dagger 2 error:
"Injector factories were bound for supertypes of ... Did you mean to bind an injector factory for the subtype?"
Suppose we do have some BaseActivity:
open class BaseActivity : MvpAppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
}
}
We do also have an ActivityBuilder Module (which provides the BaseActivity itself) and we have a Module which provides
the dependencies needed for the successful running of the BaseActivity.
#Module
abstract class ActivityBuilder {
#ContributesAndroidInjector(modules = [BaseModule::class])
abstract fun bindBaseActivity(): BaseActivity
}
If we will add a ChildActivity like that
class ChildActivity : BaseActivity() {
}
thinking: "Hey, Dagger 2 will satisfy the dependencies for the BaseActivity and since we are extending
it, we will get a ChildActivity up and running, right?". Wrong. We will get an exception "Injector factories were bound for supertypes of ... Did you mean to bind an injector factory for the subtype?"
What we should do? We should make Dagger 2 know explicitly about our ChildActivity.
a) Add AndroidInjection.inject(this) to the ChildActivity onCreate() method:
class ChildActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
}
}
b) add all subclasses of the BaseActivity to the ActivityBuilder module (which provides activities).
Note, that the BaseModule, satisfying dependencies for the BaseActivity, should also be included
to the #ContributesAndroidInjector of the child class (ChildActivity)
#Module
abstract class ActivityBuilder {
#ContributesAndroidInjector(modules = [BaseModule::class])
abstract fun bindBaseActivity(): BaseActivity
//Adding subclasses
#ContributesAndroidInjector(modules = [BaseModule::class])
abstract fun bindChildActivity(): ChildActivity
}
Just an informed guess: It probably happens because your Class is actually called different when you use AndroidAnnotations (they add the underscore somewhere). Then you have to define the binding also like so (not sure where the underscore goes):
#ContributesAndroidInjector(modules = LoginActivityModule_.class)
#LoginActivityScope
abstract LoginActivity bindLoginActivity();

Dagger 2 For Android "cannot be provided without an #Provides-annotated method"

I'm trying to use the latest version of Dagger 2 V2.11 for Android
Here is my code:
AppComponent:
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
ActivityBuildersModule.class,
FragmentBuildersModule.class
})
public interface AppComponent {
void inject(MyApplication myApplication);
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
#ExceptionRequestsQualifier
ExceptionRequestsServices exceptionRequestsServices();
}
AppModule:
#Module(includes = {ActivityModule.class, FragmentModule.class})
public class AppModule {
#Provides
CompositeDisposable provideCompositeDisposable() {
return new CompositeDisposable();
}
#Provides
#ExceptionRequestsQualifier
ExceptionRequestsServices provideExceptionRequests() {
return new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(APIConstants.EXCEPTION_REQUESTS_BASE_URL)
.build()
.create(ExceptionRequestsServices.class);
}
#Singleton
#Provides
NetworkManager provideNetworkManager(Application app) {
return new NetworkManager(app);
}
}
ActivityBuildersModule:
#Module
public abstract class ActivityBuildersModule {
#ActivityScope
#ContributesAndroidInjector
abstract ExceptionRequestsActivity contributeExceptionRequestsActivity();
}
ActivityModule:
#Module()
public abstract class ActivityModule {
#Provides
#ActivityScope
static ExceptionRequestsMvpPresenter<ExceptionRequestsMvpView> bindExceptionRequestsPresenter(
ExceptionRequestsPresenter<ExceptionRequestsMvpView> presenter) {
return presenter;
}
}
FragmentBuildersModule:
#Module
public abstract class FragmentBuildersModule {
#FragmentScope
#ContributesAndroidInjector
abstract AddApplicantFragment contributeAddApplicantFragment();
#FragmentScope
#ContributesAndroidInjector
abstract PledgeFragment contributePledgeFragment();
}
FragmentModule:
#Module()
public abstract class FragmentModule {
#Provides
#FragmentScope
static AddApplicantMvpPresenter<AddApplicantMvpView> bindAddApplicantPresenter(
AddApplicantPresenter<AddApplicantMvpView> presenter) {
return presenter;
}
#Provides
#FragmentScope
static PledgeMvpPresenter<PledgeMvpView> bindPledgePresenter(
PledgePresenter<PledgeMvpView> presenter) {
return presenter;
}
}
AddApplicantPresenter:
public class AddApplicantPresenter<V extends AddApplicantMvpView> extends BasePresenter<V> implements AddApplicantMvpPresenter<V> {
#Inject
#ExceptionRequestsQualifier
ExceptionRequestsServices mExceptionRequestsServices;
#Inject
NetworkManager mNetworkManager;
#Inject
public AddApplicantPresenter(CompositeDisposable compositeDisposable) {
super(compositeDisposable);
}
}
AddApplicantMvpPresenter:
#FragmentScope
public interface AddApplicantMvpPresenter<V extends AddApplicantMvpView> extends MvpPresenter<V> {
void addApplicant(String name, String qatarId,
String date, String mobile,
ChosenImage chosenImage);
}
ActivityScope:
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface ActivityScope {
}
FragmentScope:
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface FragmentScope {
}
Error Log:
Error:(21, 1) error: mypackagename.di.component.AppComponent scoped with #Singleton may not reference bindings with different scopes:
#Provides #mypackagename.di.scope.ActivityScope mypackagename.ui.exceptionrequests.ExceptionRequestsMvpPresenter<mypackagename.ui.exceptionrequests.ExceptionRequestsMvpView> mypackagename.di.module.ActivityModule.bindExceptionRequestsPresenter(mypackagename.ui.exceptionrequests.ExceptionRequestsPresenter<mypackagename.ui.exceptionrequests.ExceptionRequestsMvpView>)
#Provides #mypackagename.di.scope.FragmentScope mypackagename.ui.addapplicant.AddApplicantMvpPresenter<mypackagename.ui.addapplicant.AddApplicantMvpView> mypackagename.di.module.FragmentModule.bindAddApplicantPresenter(mypackagename.ui.addapplicant.AddApplicantPresenter<mypackagename.ui.addapplicant.AddApplicantMvpView>)
#Provides #mypackagename.di.scope.FragmentScope mypackagename.ui.pledge.PledgeMvpPresenter<mypackagename.ui.pledge.PledgeMvpView> mypackagename.di.module.FragmentModule.bindPledgePresenter(mypackagename.ui.pledge.PledgePresenter<mypackagename.ui.pledge.PledgeMvpView>)
Modules & Components can't have different Scopes
You can have Components to have multiple Scopes and this can solve it.
Try to move it to different component and add it as component dependencies
I hope in future they can solve this, the way I've done it in my project.
Currently, Dagger2 allows module with NoScope & single scope. This should match with your components.
Thumb Rule:: Different scopes have different components.
For your application, you need three components,
FragmentComponent (FragmentScope) :- (Ideally this should be ActivityComponent)
ApplicationComponent (Singleton)
https://medium.com/#patrykpoborca/making-a-best-practice-app-4-dagger-2-267ec5f6c89a
Read more about scopes.

Dagger 2.10/2.11 injecting Activity failing

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);
}
}

Categories

Resources