I have an android application and I would like to perform dependency injection on a class which is not activity or fragment therefore the applicationContext is not present.
#HiltAndroidApp
class App: Application {
#Inject
lateinit var analytics: Analytics
override fun onCreate() {
super.onCreate()
// other details
}
}
My AppModule
#Module
#InstallIn(ApplicationComponent::class)
abstract class AppModule() {
companion object {
#Provide
#Singleton
fun provideSomeClass(): SomeClass = SomeClass()
}
}
If I try to inject SomeClass in a activity it works fine but not on a non activity class it fails with an error Object is not initialized.
class Consumer {
#lateinit var SomeClass someClass;
}
Can someone point what I am doing wrong?
Inject a field of a non-Activity class
To do this you have to create an Interface that will be an #EntryPoint,
and pass to that interface the ApplicationContext.
Code sample:
// No annotations here
class Consumer(ctx: Context) { // pass here the Android context
// Create an Interface (required by #InstallIn)
#EntryPoint
#InstallIn(SingletonComponent::class) // this could be implementation specific
interface Injector {
fun getSomeClass(): SomeClass // getter that will be injected
// you can also define a proper Kotlin Getter here
}
// create the injector object
val injector = EntryPoints.get(ctx, Injector::class.java)
// retrieve the injected object
val someObject = injector.getSomeClass()
suspend fun andFinallyUseIt() {
someObject.someMethod()
}
}
More:
Make sure you don't winde your scope
Read more: Dagger #EntryPoint
Use inject in constructor
class Consumer #Inject constructor(private val someclass:SomeClass){
//some code
}
Related
Here i created application class dependency. now i want to access
#Module
#InstallIn(SingletonComponent::class)
class MyApplicationModule {
#Provides
fun providesMainApplicationInstance(application: MainApplication): MainApplication =
application
}
My application class is:
#HiltAndroidApp
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
}
fun printData(){
Log.d("Test","Awesome print data")
}
}
i want to call printData() function from the activity. i have used field injection to access application class but it gives error...
#Inject lateinit var mainApplication: MainApplication
ERROR
[Dagger/DependencyCycle] Found a dependency cycle:
MainApplication is injected at MyApplicationModule.providesMainApplicationInstance(application)
This should work:
#Module
#InstallIn(SingletonComponent::class)
class MyApplicationModule {
#Provides
fun providesMainApplicationInstance(#ApplicationContext context: Context): MainApplication {
return context as MainApplication
}
}
Hilt can inject application context. You need to simply cast it.
Let me know if it's good.
Hilt auto generate ApplicationContextModule.java, which provides both Context and Application.
With the new dependency injection library Hilt, how to inject some classes into ViewModel without constructor params and ViewModelFactory?
Is it possible?
Like in Fragment, we use only #AndroidEntryPoint and #Inject.
how to inject some classes into ViewModel without constructor params and ViewModelFactory? Is it possible?
Hilt supports constructor injection of ViewModel via the #HiltViewModel (previously #ViewModelInject) annotation.
This allows for any #AndroidEntryPoint-annotated class to redefine their defaultViewModelProviderFactory to be the HiltViewModelFactory, which allows the creation of #HiltViewModel-annotated ViewModels correctly instantiated via Dagger/Hilt.
NEW HILT VERSION:
#HiltViewModel
class RegistrationViewModel #Inject constructor(
private val someDependency: SomeDependency,
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
...
}
OLD HILT VERSION:
class RegistrationViewModel #ViewModelInject constructor(
private val someDependency: SomeDependency,
#Assisted private val savedStateHandle: SavedStateHandle
) : ViewModel() {
...
}
And then
#AndroidEntryPoint
class ProfileFragment: Fragment(R.layout.profile_fragment) {
private val viewModel by viewModels<RegistrationViewModel>() // <-- uses defaultViewModelProviderFactory
Yes, it is possible to inject dependency into a ViewModel class without constructor params. First we need to create a new interface annotated with #EntryPoint to access it.
An entry point is an interface with an accessor method for each
binding type we want (including its qualifier). Also, the interface
must be annotated with #InstallIn to specify the component in which to
install the entry point.
The best practice is adding the new entry point interface inside the class that uses it.
public class HomeViewModel extends ViewModel {
LiveData<List<MyEntity>> myListLiveData;
#ViewModelInject
public HomeViewModel(#ApplicationContext Context context) {
myListLiveData = getMyDao(context).getAllPosts();
}
public LiveData<List<MyEntity>> getAllEntities() {
return myListLiveData;
}
#InstallIn(ApplicationComponent.class)
#EntryPoint
interface MyDaoEntryPoint {
MyDao myDao();
}
private MyDao getMyDao(Context appConext) {
MyDaoEntryPoint hiltEntryPoint = EntryPointAccessors.fromApplication(
appConext,
MyDaoEntryPoint.class
);
return hiltEntryPoint.myDao();
}
}
In the code above we created a method named getMyDao and used EntryPointAccessors to retrieve MyDao from Application container.
Notice that the interface is annotated with the #EntryPoint and it's
installed in the ApplicationComponent since we want the dependency
from an instance of the Application container.
#Module
#InstallIn(ApplicationComponent.class)
public class DatabaseModule {
#Provides
public static MyDao provideMyDao(MyDatabase db) {
return db.MyDao();
}
}
Though the code above has been tested and worked properly but it is not the recommended way to inject dependency into ViewModel by android officials; and unless we know what we're doing, the best way is to inject dependency into ViewModel through constructor injection.
Why some people uses #Inject for constructor of classes and some other did not use this annotation for constructor.
Is it optional to use this?
It's not needed if you provide instance yourself:
//without #Inject
class SomeInstance contructor(...): SomeInstanceInterface{}
#Module
class Module{
#Provides()
fun provide():SomeInstanceInterface {
return SomeInstance(...)
}
}
But if you want that Dagger create instance for you, then you need to mark constructor with #Inject and ask Dagger to create instances:
#Module
class Module{
#Provides()
fun provide(inst: SomeInstance):SomeInstanceInterface = inst
}
or
#Component
interface Component{
fun someInstance():SomeInstanceInterface
}
I am trying to inject application context in a class which is giving
”lateinit property application has not been initialized"
exception.
CoreModule.kt
#Module
open class CoreModule {
#Singleton
#Provides
fun provideRealmHelper(): RealmHelper {
return RealmHelper()
}
}
MyApplication.kt
open class MyApplication : MultiDexApplication(), HasActivityInjector {
#Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
val log = LoggerFactory.getLogger(this.javaClass)!!
companion object {
var application: MyApplication? = null
fun getInstance(): MyApplication {
return application!!
}
}
override fun onCreate() {
try {
super.onCreate()
application = this
DaggerAppComponent.builder().application(this).build().inject(this)
} catch (e: Exception) {
log.error("Exception in Application", e)
Thread.setDefaultUncaughtExceptionHandler(GlobalExceptionHandler())
}
}
override fun activityInjector() = dispatchingAndroidInjector
}
AppComponent.kt
#Singleton
#Component(modules = [AndroidSupportInjectionModule::class,CoreModule::class])
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: MyApplication): Builder
fun build(): AppComponent
}
fun inject(myApplication: MyApplication)
fun inject(realmHelper: RealmHelper)
}
//I need application context in this class. I am inject applicationContext here.
Is injecting is correct way to do or I should use constructor injection?
RealmHelper.kt
class RealmHelper #Inject constructor() {
//need application context here but getting "lateinit property application has not been initialized
#Inject
lateinit var application: MyApplication
init {
Realm.init(application) // null application
}
}
Note: MyApplication is added to AndoridManifest.xml
The problem is you annotated your field but not injected. You can inject field like you did in application class : DaggerAppComponent.builder().application(this).build().inject(this)
or you can move your application field to RealmHelper constructor and in core module you need to write a provide function to return application. If you want to see an example I have an applicaton. https://github.com/volkansahin45/Moneycim
I need application context in this class. I am inject
applicationContext here. Is injecting is correct way to do or I should
use constructor injection?
Always favor constructor injection over field injection if possible.
Your CoreModule is not needed. The code below is enough.
#Singleton
class RealmHelper #Inject constructor(private val application: MyApplication) {
//Your implementation
}
fun inject(realmHelper: RealmHelper) in your Component is also unnecessary.
Removing those lines should fix it, I quickly threw together a demo project just to test it to make sure. Here is a quick gist with the code.
this might be too late but this may help other developer..
#set:Inject
internal var activityDispatchingAndroidInjector: DispatchingAndroidInjector<Activity>? = null
use this instead of
#Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
and in case you get same error elsewhere, do the same. Use #set:Inject instead of #Inject and use internal instead of lateinit.
This worked for me like charm.
I have problem using dagger2
I create Component, Module, Provide
class testModule {
#Provides #Singleton
fun provideTestServer(): TestService {
}
}
and i called onCreate() in MainActivity
DaggerImageComponent.builder().build().inject(this)
here's my problem
DI works fine in MainActivity
class MainActivity: AppCompatActivity {
#Inject
lateinit var testService: TestService
}
but other file is not working.
object TestObject {
#Inject
#JvmSynthetic // error: static field cannot inject
lateinit var testService: TestService
fun test() = testService.testfun()
}
or
#Singleton
class TestClass {
#Inject
lateinit var testService: TestService
fun test() = testService.testfun()
}
TestClass and TestObject get error
- lateinit property testInterface has not been initialized
i don't understand why error occured in TestClass, TestObject.
You should call "inject" inside of class where you want to get injected variable.
You did it for MainActivity, but also you should inject your component inside other classes. By the way, you have TestClass, it seems like you use it in client code also from injection, because it has "Singleton" annotation. If it's true - you can simply add provider for it in your module and pass service as a contructor parameter:
class testModule {
#Provides #Singleton
fun provideTestServer(): TestService {
}
#Provides #Singleton
fun provideTestServer(testService: TestService): TestClass {
}
}
then, your TestClass should have constructor:
class TestClass(var testService: TestService) {
fun test() = testService.testfun()
}
I suggest you to read once again about dagger, check this tutorial:
http://www.vogella.com/tutorials/Dagger/article.html