Android Kotlin: Hilt how to get object of Application class? - android

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.

Related

Dagger/MissingBinding but module exists

I'm trying to put dagger into my project and I'm facing a compilation issue I don't get since I've done everything like the android developer tutorial. I get:
error: [Dagger/MissingBinding] INotificationService cannot be provided without an #Provides-annotated method
Here is my app annotation:
#HiltAndroidApp
class App : MultiDexApplication() {
Activity annotation:
#AndroidEntryPoint
class MainActivity: AppCompatActivity() {
Fragment annotation:
#AndroidEntryPoint
class NotificationFragment: Fragment(R.layout.fragment_notification) {
I think everything's ok until there. Then the problem I'm facing is here:
Viewmodel class:
#HiltViewModel
class NotificationViewModel #Inject constructor(private val notificationService: INotificationService): ViewModel()
Here is the interface for INotificationService:
interface INotificationService {
fun refreshNotification(): Single<List<INotification>>
fun markAsRead(notification: INotification)
}
and the implementation:
class NotificationServiceImpl #Inject constructor(#ApplicationContext context: Context): INotificationService
with associated module:
#Module
#InstallIn(ActivityComponent::class)
abstract class NotificationModule {
#Binds
abstract fun bindNotificationService(impl: NotificationServiceImpl): INotificationService
}
The bindNotificationService binds function from the module is greyed out, it's not the case on the android developer tutorial and the error makes me think I missed something to make this function findable at compile time but since there is #Module and #InstallIn(ActivityComponent::class) I have absolutly no idea why it doesn't compile.
INotificationService is ViewModel dependency and it should bind with ViewModel lifecycle so instead of this
#Module
#InstallIn(ActivityComponent::class)
abstract class NotificationModule {
#Binds
abstract fun bindNotificationService(impl: NotificationServiceImpl): INotificationService
}
Use this:-
#Module
#InstallIn(ViewModelComponent::class)
abstract class NotificationModule {
#Binds
abstract fun bindNotificationService(impl: NotificationServiceImpl): INotificationService
}

Android How to dependency inject variables into non activity?

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
}

How to access injected properties in attachBaseContext using Hilt?

For the sake of changing application's default Locale, I have to get access of my WrapContext class in attachBaseContext method inside the Activity:
#AndroidEntryPoint
class MainActivity : AppCompatActivity() {
#Inject lateinit var wrapper: WrapContext
.
.
.
override fun attachBaseContext(newBase: Context?) {
super.attachBaseContext(wrapper.setLocale(newBase!!))
}
}
But as you can imagine I get nullPointerException because the field is injected after attachBaseContext is called.
Here is the WrapContext class:
#Singleton
class WrapContext #Inject constructor() {
fun setLocale(context: Context): Context {
return setLocale(context, language)
}
.
.
.
}
I also tried to inject WrapContext inside MyApp class so the field should be initialized when calling it inside Activity.
#HiltAndroidApp
class MyApp : Application() {
#Inject lateinit var wrapper: WrapContext
}
attachBaseContext inside activity:
override fun attachBaseContext(newBase: Context?) {
super.attachBaseContext((applicationContext as MyApp).wrapper.setLocale(newBase!!))
}
But I still get the same error. I debugged the code and I found that applicationContext is Null in the method.
I searched online and I found the same issue someone had with dagger here. But there is no accepted answer which can maybe give me some sights to do it in hilt.
Is there a way to get this WrapContext class in the attachBaseContext method inside activity?
You can use an entry point to obtain dependencies from ApplicationComponent as soon as you have a Context attached to your application. Fortunately, such a context is passed into attachBaseContext:
#EntryPoint
#InstallIn(ApplicationComponent::class)
interface WrapperEntryPoint {
val wrapper: WrapContext
}
override fun attachBaseContext(newBase: Context) {
val wrapper = EntryPointAccessors.fromApplication(newBase, WrapperEntryPoint::class).wrapper
super.attachBaseContext(wrapper.setLocale(newBase))
}

Dagger-2 "lateinit property application has not been initialized"

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.

Dagger2 not working in android

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

Categories

Resources