For Dagger2 release , i plan to split the module into few small module for re-use on other projects.
Application Module contains many things, i can group it into three type.
Type A related, Type B related, Type C related.
so i want to put it into three different module , therefore it can re-use part of it if need on other projects.
Reference from the Google's Fork
build.gradle for Application
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.1.0'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
mavenCentral()
}
}
build.gradle for app module
apply plugin: 'com.neenbedankt.android-apt'
//below lines go to dependenc
compile 'com.google.dagger:dagger:2.0'
apt 'com.google.dagger:dagger-compiler:2.0'
provided 'org.glassfish:javax.annotation:10.0-b28'
after above steps , i am going to create my application module
#dagger.Module
public class MyApplicationModule {
void inject(MyApplication application);
}
and my component
#Singleton
#Component(
modules = {TypeA.class, TypeB.class, TypeC.class})
public interface MyApplicationComponent {
When i use it on activity , it looks like
#Component(dependencies = MyApplicationComponent.class,
modules = ActivityModule.class)
public interface ActivityComponent {
void injectActivity(SplashScreenActivity activity);
There are compile issue
Error:(22, 10) error: XXXXXX cannot be provided without an
#Provides- or #Produces-annotated method.
com.myapplication.mXXX
[injected field of type:
XXX]
i guess there are something wrong when i config that Activity Component extends from application component.
my purpose is some singleton object inside Application Component , activity will inject same object to reduce some object create every time on activity.
is my design wrong?? or any other things need to do for config??
find out the root cause is come from #Scope
need to expose the type for other sub-component usage
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface ActivityScope {
}
if we want to expose the Context for application ,
#Singleton
#Component(
{TypeA.class, TypeB.class, TypeC.class})
public interface MyApplicationComponent {
void inject(MyApplication application);
#ForApplication
Context appContext();
when my sub-component want to extend this
#ActivityScope
#Component(dependencies = MyApplicationComponent.class,
modules = ActivityModule.class)
public interface ActivityComponent extends MyApplicationComponent {
void injectActivity(Activity activity);
i think it is a great thing for Dagger2 , let you manually expose the object you need to use , code become more traceable.
Related
I'm just starting with Jetpack Compose and Hilt. But I'm running into issues when I inject into a ViewModel.
The error that I get:
java.lang.RuntimeException: Cannot create an instance of class com.example.chaes.login.viewModel.SignUpViewModel
Caused by: java.lang.InstantiationException: java.lang.Class<com.example.chaes.login.viewModel.SignUpViewModel> has no zero argument constructor
I can inject all fine in the Activity but not in the ViewModel. I've tried all solutions I could find.
My gradle files:
Project root level:
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:4.2.2"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10"
classpath 'com.google.gms:google-services:4.3.8'
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.37'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
Module level:
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
kapt{
correctErrorTypes true
}
dependencies {
...
// Hilt
implementation "com.google.dagger:hilt-android:2.37"
kapt "com.google.dagger:hilt-android-compiler:2.37"
...
}
My application file
#HiltAndroidApp
class BaseApplication: Application() {
}
My Module File:
#Module
#InstallIn(SingletonComponent::class)
object AuthRepoModule {
#Singleton
#Provides
fun provideAuthRepo(): FirebaseAuthRepo{
return FirebaseAuthRepo()
}
#Singleton
#Provides
fun provideRandomString(): String{
return "gejifeg"
}
}
The project is single activity with composable screens so the MainActivity:
#AndroidEntryPoint
class MainActivity : AppCompatActivity() { ... }
ViewModel:
#HiltViewModel
class SignUpViewModel #Inject constructor(
firebaseAuthRepo: FirebaseAuthRepo,
) : ViewModel() { ... }
Things I've tried:
Changing to ViewModelComponent instead of Singleton
changing to kapt "com.google.dagger:hilt-compiler:2.37" instead of kapt "com.google.dagger:hilt-android-compiler:2.37"
deleting the build folder and rebuilding the project
invalidating cache and restarting
Edit: Solution Found!
As mentioned by #sitatech in the comments, one needs to use hiltViewModel() instead of viewModel() to provide the viewModel to the composable.
In app module dependency add
...
// Hilt
implementation "com.google.dagger:hilt-android:2.37"
kapt "com.google.dagger:hilt-android-compiler:2.37"
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"
kapt "androidx.hilt:hilt-compiler:1.0.0-alpha03"
...
Other portions seem ok to me.
As mentioned by #sitatech in the comments, I needed to use hiltViewModel() instead of viewModel() in the composable.
In my application, I have two modules: app and repository.
repository depends on Room, and has a GoalRepository interface:
interface GoalRepository
and a GoalRepositoryImpl class that is internal, as I don't want to expose it or the Room dependency to other modules:
#Singleton
internal class GoalRepositoryImpl #Inject constructor(private val dao: GoalDao) : GoalRepository
app depends on repository to get a GoalRepository instance.
I have a GoalRepositoryModule that, at the moment, is:
#Module
class GoalRepositoryModule {
#Provides
#Singleton
fun provideRepository(impl: GoalRepositoryImpl): GoalRepository = impl
#Provides
#Singleton
internal fun provideGoalDao(appDatabase: AppDatabase): GoalDao = appDatabase.goalDao()
#Provides
#Singleton
internal fun provideDatabase(context: Context): AppDatabase =
Room.databaseBuilder(context, AppDatabase::class.java, "inprogress-db").build()
}
The issue is that this won't compile (obviously) as the public provideRepository function is exposing GoalRepositoryImpl, that is an internal class.
How can I structure my Dagger setup to achieve what I want?
Edit:
I tried making provideRepository internal as per #David Medenjak comment and now the Kotlin compiler complains that it cannot resolve RoomDatabase dependency:
Supertypes of the following classes cannot be resolved. Please make sure you have the required dependencies in the classpath:
class xxx.repository.database.AppDatabase, unresolved supertypes: androidx.room.RoomDatabase
For completeness, the code of my Component inside the app module:
#Component(modules = [ContextModule::class, GoalRepositoryModule::class])
#Singleton
interface SingletonComponent
After looking at the code Dagger was generating, I understood that the mistake was making the #Component inside the app module depend on the #Module inside the repository module.
So I made a separate #Component inside the repository module and made the app module's one depend on it.
The code
The repository module's component:
#Component(modules = [GoalRepositoryModule::class])
interface RepositoryComponent {
fun goalRepository(): GoalRepository
}
The app's one:
#Component(modules = [ContextModule::class], dependencies = [RepositoryComponent::class])
#Singleton
interface SingletonComponent
This way the RepositoryComponent is responsible for building the Repository and knows all its dependencies, while the SingletonComponent only have to know about RepositoryComponent.
I am migrating from Dagger 2.0.2 to Dagger 2.12 and plan to utilize dagger.android API.
My question is about migrating the test code. Currently, I have an AppModule and a TestAppModule which extends it and overrides some dependencies using #Override annotation. Here is an example:
#Module
public class TestAppModule extends AppModule {
#Override
public UserDao provideUserDao() {
return mock(UserDao.class);
}
}
Now with Dagger 2.12, if I try overriding a dependency in
TestAppModule, an error is thrown: error: #Provides methods may not be overridden in modules.
What is the recommended way of overriding and mocking some of the dependencies in a TestModule while utilizing the rest of the dependencies defined in AppModule as is?
I can post a stripped down version of my Dagger setup if people think it's necessary.
I ended up solving it by simply not annotating the TestAppModule with #Module. This achieves partial mocking.
public class TestAppModule extends AppModule {
#Override
public UserDao provideUserDao() {
return mock(UserDao.class);
}
}
I'm trying to use Dagger 2 in my app. I keep getting this error:
Error:Execution failed for task ':app:compileDebugJavaWithJavac'.
java.lang.IllegalArgumentException: #dagger.Module does not define an element subcomponents()
I don't use subcomponents at all in the app so I have no idea why this error is occurring. I have one module and one component. Module:
#Singleton
#Module
public class ApplicationModule {
private final WSTApplication application;
public ApplicationModule(WSTApplication application) {
this.application = application;
}
#Provides
public WSTApplication application() {
return this.application;
}
#Provides
public Context applicationContext() {
return this.application;
}
#Provides
Realm provideRealm() {
return Realm.getDefaultInstance();
}
#Provides
RealmHelper providesRealmHelper(final Realm realm) {
return new RealmHelper(realm);
}
#Provides
#Singleton
public WorkoutPresenter providesWorkoutPresenter(final RealmHelper helper) {
return new WorkoutPresenter(helper);
}
}
And my component:
#Singleton
#Component(modules={ApplicationModule.class})
public interface ApplicationComponent {
void inject (MainActivity activity);
WSTApplication application();
Context applicationContext();
Realm provideRealm();
RealmHelper providesRealmHelper(Realm realm);
WorkoutPresenter providesWorkoutPresenter(RealmHelper helper);
}
And here is the onCreate from my application:
#Override
public void onCreate() {
super.onCreate();
component = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.build();
}
I can't get DaggerApplicationComponent to stop being red, either, but I assume this is because the project isn't actually building because of this weird subcomponent error? I've tried using the underscore (Dagger_ApplicationComponent) but that doesn't help.
I tried Google but all I found was guides on how to use subcomponents in Dagger, which is not what I want. I don't want to use subcomponents. I just want to use the one component.
Also, just in case this matters, here is what I put in my build.gradle files:
In the project buildscript:
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
And in the app level build.gradle:
apply plugin: 'com.neenbedankt.android-apt'
and then down in the dependencies:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:24.2.1'
compile 'com.android.support:design:24.2.1'
compile 'com.google.dagger:dagger:2.1'
compile 'com.android.support:support-v4:24.2.1'
testCompile 'junit:junit:4.12'
apt 'com.google.dagger:dagger-compiler:2.7'
provided 'org.glassfish:javax.annotation:10.0-b28'
}
Thanks to anyone who can help me! I'm new to Dagger2 and even after reading several beginner guides, I still don't entirely get it (I was hoping that trying to use it would make things clearer...so far it's clear as an oil spill). Sorry in advance in the highly likely event that I'm making a stupid beginner mistake.
Why do you use com.neenbedankt.android-apt? As far as I know it is obsolete now.
The Dagger GitHub page explains how to use it within an Android project.
dependencies {
compile 'com.google.dagger:dagger:2.x'
annotationProcessor 'com.google.dagger:dagger-compiler:2.x'
}
Here you can find Dagger releases for the dependencies versions.
I'm not sure if it solves your problem right away but you should definitely give it a try.
The error you're getting is from version skew between the Dagger API and the annotation processor: compile 'com.google.dagger:dagger:2.1' and apt 'com.google.dagger:dagger-compiler:2.7'. In this particular case, the compiler is looking for #Module.subcomponents(), but it's not in your version of #Module (version 2.1) because it wasn't added until 2.7.
tl;dr
The api and the annotation processor should always be the same version.
I'm newbie for Dagger.
Current I create sample project some snip code:
MyComponent.java
#PerActivity
#Component(modules = MyModule.class)
public interface MyComponent {
void inject(TutorialActivity activity);
}
MyModule.java
#Module
public class MyModule {
#Provides
Position providePosition() {
return new Position();
}
}
PerActivity.java
#Scope
#Retention(RUNTIME)
public #interface PerActivity {}
TutorialActivity.java
public class TutorialActivity extends AppCompatActivity{}
When compile project I get error:
Error:Execution failed for task ':app:compileDebugJavaWithJavac'.
> java.lang.IllegalArgumentException: expected one element but was: <android.support.v4.app.FragmentActivity, android.support.v4.app.TaskStackBuilder.SupportParentable>
So if I change TutorialActivity as:
public class TutorialActivity extends Activity{}
or even
public class TutorialActivity{} // Without extends
Then it will working normally.(I can see class generated by Dagger2).
Please help !
Thanks.
UPDATE
My project structure:
common module.
app module. (app module will use common module as depended in gradle).
In both build.gradle (common and app module) I added:
apt "com.google.dagger:dagger-compiler:${daggerVersion}"
compile "com.google.dagger:dagger:${daggerVersion}"
In build.gradle at common module:
provide "org.glassfish:javax.annotation:${javaxAnnotationVersion}"
An error only occurs if I have 2 module. (module app depended on common).
If I move my Component/Module to module common -> It work.
But when I move that to app module -> Error when compile.
I'm not sure that your issue is a problem with Dagger because I don't see you requesting any dependencies in your Android components.
Nonetheless you need this in your build.gradle to use the depdendency injection annotations.
provided 'javax.annotation:jsr250-api:1.0'
Thanks #plash for your answer.
After I re-check for both module.
I found I only added:
provide "org.glassfish:javax.annotation:${javaxAnnotationVersion}"
in common module.
After I added that provide for both module then compile success.(Dagger generated class.)