Dagger2 Android Depedency injection java - java

i'm new to dagger2.
I'm trying to create an 'ActivityComponent' which will retrieves all information from my activity (ex : context ...), and an another component in which i'm trying to inject the 'Activity component' (in the code below, it's the CheckErrorsModel class).
#Singleton
public class CheckErrorsModel {
private Context context;
#Inject
public CheckErrorsModel(MainActivityComponent mainActivityComponent) {
this.context = mainActivityComponent.getContext();
}
public void test() {
Log.d("test", "test lancé ");
}
}
The Interface component class :
#Singleton
#Component()
public interface CheckErrorsModelDi {
CheckErrorsModel getCheckErrorsModel();
MainActivityComponent getApplicationComponent();
}
And everything related to the Application context class :
#Component(modules = {MainActivityModule.class})
#Singleton
public interface MainActivityComponent {
Context getContext();
void inject(MainActivity mainActivity);
}
the module class :
#Module
public class MainActivityModule {
private final Context context;
public MainActivityModule(Context context) {
this.context = context;
}
#Provides
#Singleton
Context provideContext(){
return context;
}
}
But once i try to build the app :
i got some errors :
.MainActivityComponent cannot be provided without an #Provides-annotated method MainActivityComponent getApplicationComponent();
MainActivityComponent cannot be provided without an #Provides-annotated method.
CheckErrorsModel getCheckErrorsModel()

I think this is because your CheckErrorsModelDi don't have any module.
And Scope (like #Singleton) should always be on Module method and the component
And this should be a subComponent of MainActivityComponent
Alors you inject Method seems à bit strange.
You should use à builder instead see bindings instance

Related

Can't provide Activity Context with Dagger 2

SOLVED
I am new with Dagger 2 and I am trying to provide Activity Context to classes but without success. I search a lot but did not find any appropriate answer.
I can provide Application Context. But I also need to provide Activity Context as well and I don't know any good way to implement that.
I need to clarify that I am using Dagger for Android dependencies.
def dagger_version = "2.24"
implementation "com.google.dagger:dagger:$dagger_version"
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
implementation "com.google.dagger:dagger-android:$dagger_version"
implementation "com.google.dagger:dagger-android-support:$dagger_version"
annotationProcessor "com.google.dagger:dagger-android-processor:$dagger_version"
I also have only an AppComponent with the following code:
#Singleton
#Component(
modules = {
AndroidSupportInjectionModule.class,
ActivityBuildersModule.class,
AppModule.class,
ViewModelFactoryModule.class,
}
)
public interface AppComponent extends AndroidInjector<BaseApplication> {
SessionManager sessionManager();
#Component.Builder
interface Builder{
#BindsInstance
Builder application(Application application);
AppComponent build();
}
}
Except that I have a module for each of my activity but I didn't find a way to inject the Activity Context either for AppComponent or from a ActivityModule.
What is the right way to do that?
UPDATE
I finally found the right way to do that.
First I created a module for the class that I want to provide
#Module
public class AlertsModule {
#Provides
static Alerts provideAlerts(Activity activity){
return new Alerts(activity);
}
}
Then I go to the ActivityModules that I want to Inject that custom Class and do a Binding like that
#Module
public abstract class MainActivityModule {
...
#Binds
abstract Activity providesActivity(MainActivity activity);
...
}
And finally I just include the CustomClassModule in my ActivityBuildersModule where I use #ContributesAndroidInjector to provide my Activities.
#MainScope
#ContributesAndroidInjector(
modules = {
AlertsModule.class,
}
)
abstract MainActivity contributeMainActivity();
You can bind an instance of Activity in the same way you're currently binding an instance of Application, either by using an #Component.Builder or an #Component.Factory.
An example implementation would look something like this:
#Subcomponent(...)
interface ActivitySubcomponent {
#Subcomponent.Factory
interface Factory {
ActivitySubcomponent create(#BindsInstance MyActivity activity)
}
}
#Module(subcomponents = [ActivitySubcomponent.class])
class ApplicationModule {
....
}
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
...
((MyApplication) getApplication())
.applicationComponent
.activitySubcomponentFactory()
.create(this)
.inject(this)
}
}
#Module
class MainActivityModule(mActivity: MainActivity) {
var mActivity: MainActivity
init {
this.mActivity= mActivity
}
#Singleton
#Provides
fun getMainActivity(): MainActivity{
return mActivity
}
}
now add this module to your Component class and pass your activity to module constructor while using your component builder hope this may helps you.
#Named annotation helps us to differentiate the Context. We can differentiate the method context() in ActivityModule and ContextModule by adding the #Named annotation as below:
#Module
public class ActivityModule {
private final Context context;
ActivityModule(Activity context){
this.context = context;
}
#Named("activity_context")
#Provides
public Context context(){ return context; }
}
#Module
public class ContextModule {
private final Context context;
ActivityModule(Activity context){
this.context = context;
}
#Named("application_context")
#Provides
public Context context(){ return context.getApplicationContext(); }
Then, we tell Dagger to use the respective Context, as below:
#Module(includes = ContextModule.class)
public class OkHttpClientModule {
....
#Provides
public File file(#Named("application_context") Context context){
File file = new File(context.getCacheDir(), "HttpCache");
file.mkdirs();
return file;
}
....
}
Hope this helps.

Dagger dependency injection to service fails with "contactServiceModule must be set at DaggerAppComponent$Builder.build(DaggerAppComponent.java)"

I am using dagger for dependency injection, I apologize for not diving into deep and getting my hands on :)
I am using two modules(ApplicationModule, ContactServiceModule), with a component (AppComponent). I have initialized the Dagger in App.java, and I am trying to inject dependencies to a backgroud Service.
Here is the code snippet.
1)ApplicationModule.java
#Module
public class ApplicationModule {
private final App mApp;
private Service contactService;
public ApplicationModule(App app) {
mApp = app;
}
#Provides
#Singleton
public ContactModel contactModel(SQLiteDatabase database) {
return new ContactModel(mApp, database);
}
#Provides
#Singleton
public Context appContext() {
return mApp;
}
#Provides
#Singleton
public ContactController contactController() {
return new ContactController(mApp.getAppComponent());
}
//.....Some more code
}
2)ContactServiceModule.java
#Module
public class ContactServiceModule {
ContactService contactService;
public ContactServiceModule(ContactService contactService){
this.contactService =contactService;
}
#Provides
#Singleton
ContactService provideContactService() {
return this.contactService;
}
}
3)AppComponent.java
#Singleton
#Component(modules = {ApplicationModule.class,ContactServiceModule.class})
public interface AppComponent {
ContactController contactController();
Context appContext();
//...Some code
void inject(ContactController contactController);
//...Some code
void inject(ContactService contactService);
}
4)App.java
public class App extends Application {
private AppComponent mAppComponent;
#Override
public void onCreate() {
super.onCreate();
FlowManager.init(this);
mAppComponent = DaggerAppComponent.builder()
.applicationModule(new ApplicationModule(this))
.build();
}
public AppComponent getAppComponent() {
return mAppComponent;
}
}
5) ContactService.java
public class ContactService extends Service {
#Inject
Context appContext;
#Inject
ContactController contactController;
#Override
public void onCreate() {
super.onCreate();
getAppComponent().inject(this);
}
private AppComponent getAppComponent() {
return ((App)getApplication()).getAppComponent();
}
}
It could be an issue of dependencies of UI threads injected into background services. But I do not understand how should I achieve the same dependencies to be injected into services. Or what should be the Ideal way to communicate between application level dependencies and services. I want the service to be running always in the background and listening to various intents and also communicate to other dependencies like eventbus etc.
The error message:
contactServiceModule must be set at DaggerAppComponent$Builder.build(DaggerAppComponent.java)”
means that you have created a module with dependencies in the constructor (ContactServiceModule which has your Service as a parameter in the constructor). It follows that Dagger 2 cannot instantiate that module automatically and it must be instantiated in the builder when you create your component:
mAppComponent = DaggerAppComponent.builder()
.applicationModule(new ApplicationModule(this))
.contactServiceModule(contactService);
.build();
However there is an underlying issue with the architecture you are trying to create. Services, like Activities, are designed to be autonomous and instantiated by the Android OS. Much in the same way you wouldn't directly pass as an Activity as a dependency to another Activity, you shouldn't make a Service a dependency. Instead you need to use Intent, service binding, or EventBus events to communicate with the service. The Android Service documentation covers this and you should read it carefully before proceeding.

Dagger 2 LocalDataSource cannot be provided without #Provides annotated method

I'm trying to follow the same example as in here by creating a Repository pattern with DI.
The problem is that I get the following error:
"Error:(16, 20) error: #mvp.model.di.scope.Local
mvp.model.repository.local.GameLocalDataSource cannot be provided
without an #Provides-annotated method. #mvp.model.di.scope.Local
mvp.model.repository.local.GameLocalDataSource is injected at
mvp.model.repository.GameRepository.(gameLocalDataSource, …)
mvp.model.repository.GameRepository is provided at
mvp.model.di.component.RepositoryComponent.getGameRepository()"
Here's the code related to the app:
public class GameApplication extends Application {
private RepositoryComponent repositoryComponent;
#Override
public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
// Normal app init code...
repositoryComponent = DaggerRepositoryComponent.builder()
.applicationModule(new ApplicationModule((getApplicationContext())))
.build();
}
public RepositoryComponent getRepositoryComponent() {
return repositoryComponent;
}
}
This is my RepositoryComponent:
#Singleton
#Component(modules = {RepositoryModule.class, ApplicationModule.class})
public interface RepositoryComponent {
GameRepository getGameRepository();
}
Here's the RepositoryModule:
#Module
public class RepositoryModule {
#Singleton
#Provides
#Local
GameDataSource provideLocalDataSource(Context context) {
return new GameLocalDataSource(context);
}
#Singleton
#Provides
#Remote
GameDataSource provideRemoteDataSource() {
return new GameRemoteDataSource();
}
}
And finally, the ApplicationModule:
#Module
public final class ApplicationModule {
private Context context;
public ApplicationModule(Context context) {
this.context = context;
}
#Provides
Context providesContext() {
return context;
}
}
Here's most of my GameRepository class:
#Singleton
public class GameRepository implements GameDataSource {
private GameDataSource remoteDataSource;
private GameDataSource localDataSource;
#Inject
public GameRepository(#Local GameLocalDataSource gameLocalDataSource, #Remote GameRemoteDataSource gameRemoteDataSource) {
remoteDataSource = gameRemoteDataSource;
localDataSource = gameLocalDataSource;
}
Also, as in the mentioned example, I created a couple of scopes, #Local and #Remote since my two data sources have the same type and Dagger needs to differentiate them.
#Qualifier
#Documented
#Retention(RetentionPolicy.RUNTIME)
public #interface Local {
}
The rest of the code I have related to dagger, is just the #Inject in the constructors where I want to inject my dependencies.
Also, the DaggerRepositoryComponent is never generated in the GameApplication class.
Thanks a lot for the help in advance!
GameLocalDataSource cannot be provided without an #Provides-annotated method
Somewhere in the code you are trying to #Inject GameLocalDataSource, but you have specified in your module how to provide GameDataSource, not GameLocalDataSource.
...
GameDataSource provideLocalDataSource(Context context) {
return new GameLocalDataSource(context);
}
...
Either ask Dagger to inject GameDataSource, or describe Dagger how to provide GameLocalDataSource.
...
GameLocalDataSource provideLocalDataSource(Context context) {
return new GameLocalDataSource(context);
}
...

how to use injection between MVP layers in android?

I have been using using dagger 2 in my project lately,
the problem is when I try to build my project, the presenter in my login
activity which injected like below is null,
and when I try to build the project
presenter cannot be provided without an #Inject constructor or from an #Provides- or #Produces-annotated method...
I don't understand what have I done wrong??, please someone help me with this,
thanks in advance.
Here is my Login Activity, the presenter here is null, which shows that, I've not injected it properly
#Inject
LoginPresenter presenter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
InjectHelper.getRootComponent().injectPresenter(this);
presenter.setProgressBarVisiblity(View.INVISIBLE);
}
this is my presenter module
#Module
public class PresenterModule {
private final LoginActivity activity;
public PresenterModule(LoginActivity activity) {
this.activity = activity;
}
#Provides
#Singleton
public LoginActivity providesView() {
return activity;
}
}
#Provides
#Singleton
public LoginPresenter providesPresenter()
{
return new LoginPresenter();
}
}
this inject helper class
public class InjectHelper {
private static RootComponent sRootComponent;
static {
initModules();
}
private static void initModules() {
sRootComponent = getRootComponentBuilder().build();
}
public static DaggerRootComponent.Builder getRootComponentBuilder() {
return DaggerRootComponent.builder();
}
public static RootComponent getRootComponent() {
if (sRootComponent == null) {
initModules();
}
return sRootComponent;
}
}
this is root component class
#Singleton
#Component(modules = {
PresenterModule.class
})
public interface RootComponent {
void injectLoginView(LoginPresenter loginPresenter);
}
you need to inform the dagger which views want to use injection, in your component.
you must change the inject function code in your component to below:
void inject(LoginActivity activity);
for showing dagger what you want, you need to use #dagger annotation NOT by sending it as inject function in component file. as you did properly:
#Inject
LoginPresenter presenter;
Dagger will search for a variable of type LoginPresenter in your module and finds the proper provider method using the type.
what you put in your component as a argument for "inject" function tells the Dagger what view you are going to do injection in (NOT what you want to inject)
#Singleton
#Component(modules = {PresenterModule.class})
public interface RootComponent {
void inject(LoginActivity activity);
}

Dagger Android View Injection Error

I'm having a heck of a time figuring out how to set up Dagger dependencies to satisfy what is essentially this snippet.
I have a controller, WelcomeScreen, (which extends Path - a Mortar/Flow thing), which states that it injects a ThingView. I then inject a provider of ThingView and my ThingView constructor has an #Inject annotation with an Activity, which I supply elsewhere.
I still end up getting this error: No inject registered for members/com.....view.ThingView. You must explicitly add it to the 'injects' option in one of your modules.
Thoughts on what I'm missing?
public class WelcomeScreen extends Path {
#dagger.Module(
injects = {
ThingView.class,
},
addsTo = ActivityModule.class)
public class Module {
}
#Singleton
public static class Presenter extends ViewPresenter<WelcomeView> {
#Inject
public Presenter(
Provider<ThingView> thingViewProvider,) {
// This causes an error: No inject registered for members/com.....view.ThingView.
// You must explicitly add it to the 'injects' option in one of your modules.
thingViewProvider.get()
}
}
}
public class ThingView extends LinearLayout {
#Inject
public ThingView(Activity activity) {
super(activity);
init(activity);
}
// Note - this might not be used anywhere.
public ThingView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
ObjectGraphService.inject(context, this);
View view = View.inflate(context, R.layout.view_thread_pack, this);
ButterKnife.inject(view);
}
}
Update: I've also tried adding the below which has had no effect on the error message:
#Module(addsTo = ApplicationModule.class, library = true)
public class ActivityModule {
#Provides
public ThreadPackView providesThreadPackView() {
return new ThreadPackView(activity);
}
}
Thats not quite how Mortar works. You cannot inject the view into the presenter. Instead you inject your presenter to your view. Here's aa few samples I found that shows a similar setup to what you are doing https://github.com/Zhuinden/MortarFlowInitialDemo https://github.com/matthew-compton/NerdRoll

Categories