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.
Related
We're using Dagger2 in our application. I am trying to do a room database and I am writing the repository code, but I would like to inject application context and the DAO for the class.
I have a feeling that you can only do Dagger injection in Fragments, Activities, Services, Applications, etc.
Here's what I have:
class DownloadsDataRepositoryImpl : IDownloadsDataRepository, HasAndroidInjector {
#Inject
lateinit var androidInjector : DispatchingAndroidInjector<Any>
#Inject
lateinit var downloadsDao: DownloadsDao
override fun androidInjector(): AndroidInjector<Any> = androidInjector
init {
androidInjector()
}
}
But I'm sure it's not going to work. Is there a way to do it?
As stated, dagger-android is just a tool to help injecting specific framework classes that you can't have control on it's creation.
The proper approach is to use simple construction injection.
To be more direct on how you should expose it on your #Component, I would need more code, specifically on what you have on your activity/fragment, but here is a crude example (that I did not tested, if there are minor errors, you can fix them following the compiler error messages):
First, you will have some object that exposes your DAO. Probably it's room?
#Entity(tableName = "download_table")
data class DownloadEntity(
#PrimaryKey
val key: String
)
#Dao
interface DownloadsDao {
#Query("SELECT * FROM download_table")
fun load(): List<DownloadEntity>
}
#Database(
entities = [DownloadEntity::class], version = 1
)
abstract class DownloadRoomDatabase : RoomDatabase() {
abstract val downloadsDao: DownloadsDao
}
Now we will create a crude repository that is build with #Inject annotation. Dagger will take care of building this object for us. Notice that I am not using dagger-android for it:
interface IDownloadsDataRepository
class DownloadsDataRepositoryImpl #Inject constructor(
val downloadsDao: DownloadsDao
) : IDownloadsDataRepository
How to expose it to your activity/fragment/service requires more details on your implementation. For example, if it's inside a ViewModel or a Presenter that is annotated with #Inject or you are accessing directly on your activity will result in different implementations. Without more details, I will suppose that you are accessing the repository directly on your activity:
class DownloadActivity : FragmentActivity() {
#Inject
lateinit val repo: IDownloadsDataRepository
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
DaggerDownloadComponent.factory().create(this).inject(this)
}
}
Now we need to instruct Dagger on how to:
Bind your concrete DownloadsDataRepositoryImpl to the IDownloadsDataRepository interface that the activity requires
How to provide the dependencies to build DownloadsDataRepositoryImpl
For this we will need a module:
#Module
abstract class RepositoryModule {
//We will bind our actual implementation to the IDownloadsDataRepository
#Binds
abstract fun bindRepo(repo: DownloadsDataRepositoryImpl): IDownloadsDataRepository
#Module
companion object {
//We need the database to get access to the DAO
#Provides
#JvmStatic
fun provideDataBase(context: Context): DownloadRoomDatabase =
Room.databaseBuilder(
context,
DownloadRoomDatabase::class.java,
"download_database.db"
).build()
//With the database, we can provide the DAO:
#Provides
#JvmStatic
fun provideDao(db: DownloadRoomDatabase): DownloadsDao = db.downloadsDao
}
}
With this, we can finish the last part of our puzzle, creating the #Component:
#Component(
modules = [
RepositoryModule::class
]
)
interface DownloadComponent {
fun inject(activity: DownloadActivity)
#Component.Factory
interface Factory {
fun create(context: Context): DownloadComponent
}
}
Notice that I did not use any dagger-android code, I don't think it's useful and causes more confusion than necessary. Stick with basic dagger2 constructs and you are fine. You can implement 99.9% of your app only understanding how those constructs works:
#Module, #Component and #Subcomponent
Edit: As stated in the comments, probably you will need to properly manage the scope of your repository, specially the DB creation if you are actually using Room.
Not sure how you implemented dagger, but here is an example how you can provide context to non activity class.
Suppose you have AppModule class, so there you can add provideContext() method:
#Module
class AppModule(app: App) {
private var application: Application = app
#Provides
fun provideContext(): Context {
return application
}
}
and here is non activity class written in Kotlin:
class Utils #inject constructor(private val context: Context) {
..
}
And that's it, just rebuild j
I have a feeling that you can only do Dagger injection in Fragments, Activities, Services, Applications, etc.
You were correct to assume that before Dagger-Android 2.20, but not after 2.20+.
Now you can create a #ContributesAndroidInjector for any class, which will generate an AndroidInjector<T> for that T for which you added #ContributesAndroidInjector.
This means that there is a multi-binding that allows you to get an AndroidInjector<T> for a T, and this is what HasAndroidInjector does for you.
So the following worked for me in a different scenario (for member-injecting Workers in work-manager, instead of creating a multi-binding and a factory):
#Keep
class SyncWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
init {
val injector = context.applicationContext as HasAndroidInjector
injector.androidInjector().inject(this)
}
#Inject
lateinit var apiService: ApiService
and
#ContributesAndroidInjector
abstract fun syncWorker(): SyncWorker
HOWEVER in your particular case, none of this is required.
Dagger-Android is for member-injecting classes using an auto-generated subcomponent, that you typically need only if your injected type is inside a different module, and therefore you can't directly add fun inject(T t) into your AppComponent, OR you don't see your AppComponent.
In your case, simple constructor injection is enough, as you own your own class.
#Singleton
class DownloadsDataRepositoryImpl #Inject constructor(
private val downloadsDao: DownloadsDao
): IDownloadsDataRepository {}
Which you can bind via a module
#Module
abstract class DownloadsModule {
#Binds
abstract fun dataRepository(impl: DownloadsDataRepositoryImpl): IDownloadsDataRepository
}
And otherwise you just create your component instance inside Application.onCreate()
#Component(modules = [DownloadsModule::class])
#Singleton
interface AppComponent {
fun dataRepository(): DownloadsDataRepository
#Component.Factory
interface Factory {
fun create(#BindsInstance appContext: Context): AppComponent
}
}
And
class CustomApplication: Application() {
lateinit var component: AppComponent
private set
override fun onCreate() {
super.onCreate()
component = DaggerAppComponent.factory().create(this)
}
}
Then you can get it as
val component = (context.applicationContext as CustomApplication).component
Though technically you may as well create an extension function
val Context.appComponent: AppComponent
get() = (applicationContext as CustomApplication).component
val component = context.appComponent
I am developing an Android application using Kotlin. I am writing integration tests for my application. Now I am having a problem mocking an injected dependency class that is injected using the Dagger 2 test if a method of the mocked object is called. Following is my code.
I have the TestAppComponent class with the following code
#Singleton
#Component(modules = [ TestAppModule::class ])
interface TestAppComponent
{
fun inject(app: ApplicationController)
fun inject(app: MockApplicationController)
}
As you can see in the class, I have two inject methods. One for the ApplicationController which basically is the application class used for the actual application. Another one for MockAppllication controller which is also the application class but it is used for the tests.
Following is the implementation of the ApplicationController class
open class ApplicationController: Application()
{
lateinit var appComponent: AppComponent
private fun initDagger(app: ApplicationController): AppComponent = DaggerAppComponent
.builder()
.appModule(AppModule(app)).build()
#Inject
lateinit var fileService: IFileService
override fun onCreate() {
super.onCreate()
appComponent = initDagger(this)
instance = this
this.appComponent.inject(this)
}
}
As you can see in the code, I am initializing the Dagger AppComponent class in the onCreate method of the application class. Then inject the dependency class.
This is the implementation of my MockApplicationController class which is used for the tests.
class MockApplicationController: ApplicationController()
{
private fun initDagger(app: MockApplicationController): AppComponent = DaggerTestAppComponent
.builder()
.testAppModule(TestAppModule(app))
.build()
override fun onCreate() {
super.onCreate()
//override the dagger app component appComponent
this.appComponent = initDagger(this)
instance = this
this.appComponent.inject(this)
}
}
This is my activity class.
class MainActivity: AppCompatActivity()
{
override fun onCreate(savedInstanceState: Bundle?) {
//rest of the code
button_save_file.setOnClickListener {
//rest of the code
ApplicationController.instance.fileService.saveFile(filePath)
}
}
}
As you can see, basically, what I am doing within the activity class is that I am just calling the saveFile of the IFileService interface.
Following is the definition of the IFileService interface
interface IFileService
{
fun saveFile(path: String)
}
There are two classes that are implementing the IFileService. One is FakeFileService class which will be used for the tests and the other one is FileService class which will be used for the actual application.
Following is the implementation of FakeFileService class
class FakeFileService: IFileService
{
fun saveFile(path: String)
{
//literally, this is doing nothing since we will only test that if the method is called
}
}
I also created two classes for the Dagger app modules. One for the tests and the other one for the actual application.
Following is the implementation of the TestAppModule class which will be used for tests.
#Module
open class TestAppModule (private val app: Application) {
private var fileService: IFileService? = null
#Singleton
#Provides
fun provideContext(): Context = app
//to override injecting the Mockito mock instance
fun injectFileService(fileService: IFileService) {
this.fileService = fileService
}
#Singleton
#Provides
open fun fileService(): IFileService {
if (this.fileService != null) {
return this.fileService as IFileService
}
return FakeFileService()
}
}
Note the injectFileService method. I created that method to inject the mock object/ instance mocked using Mockito within the tests.
This is my test class
#RunWith(AndroidJUnit4::class)
#LargeTest
class CameraTest: TestBuilder()
{
#get:Rule
var mainActivityRule: ActivityTestRule<MainActivity> = ActivityTestRule<MainActivity>(CameraActivity::class.java, true, false)
private lateinit var fileServiceMock: IFileIOService
#Before
fun setUp() {
this.fileServiceMock = mock(IFileService::class.java)
MockitoAnnotations.initMocks(this)
val instrumentation = InstrumentationRegistry.getInstrumentation()
val app = instrumentation.targetContext.applicationContext as MockApplicationController
var testModule = TestAppModule(app)
testModule.injectFileService(this.fileServiceMock)
app.appComponent = DaggerTestAppComponent
.builder()
.testAppModule(testModule)
.build()
app.appComponent.inject(app)
}
#Test
fun itSavesFileWhenButtonIsClicked() {
Intents.init()
var targetContext: Context = InstrumentationRegistry.getInstrumentation().targetContext
var intent: Intent = Intent(targetContext, MainActivity::class.java)
this.mainActivityRule.launchActivity(intent)
onView(withId(R.id.button_save_file)).perform(click())
verify(this.fileServiceMock).saveFile(any())
Intents.release()
}
}
As you can see, in my test class, I try to inject the mocked version of IFileService object before the test is run. Then in my test, I am asserting that the method is executed. When I run the test it is complaining that "zero interactions with this mock". How can I fix it? What is wrong with my code and how can I fix it?
This is my ApplicationController class
open class ApplicationController: Application()
{
lateinit var appComponent: AppComponent
private fun initDagger(app: ApplicationController): AppComponent = DaggerAppComponent
.builder()
.appModule(AppModule(app)).build()
#Inject
lateinit var fileService: IFileService
override fun onCreate() {
super.onCreate()
appComponent = initDagger(this)
instance = this
this.appComponent.inject(this)
}
}
When I put the loggin in each onCreate method of application controller classes and #Before method, they are called in the following order.
ApplicationController
MockApplicationController
The code to modify the MockApplicationController with the #Before method
I want to implement authentication in my application, so I want to get the current logged user in the application class, I am following GithubBrowserSample to develop my application, but I am very new to dagger injection and I am a little lost in how to inject my authRepository to my application class.
class App : Application(), HasActivityInjector {
#Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
AppInjector.init(this)
}
override fun activityInjector() = dispatchingAndroidInjector
}
I tried adding something like
#Inject
lateinit var authRepository: AuthRepository
in the application class and in the AppModule
//I already have functions that provide everything the repository needs
#Singleton
#Provides
fun provideAuthRepository(
appExecutors: AppExecutors,
db: AppDatabase,
trainersService: TrainersService,
sessionDao: SessionDao,
tokenInterceptor: TokenInterceptor
): AuthRepository {
return AuthRepository(
appExecutors,
db,
trainersService,
sessionDao,
tokenInterceptor
)
}
but it throws Uninitialized Property Access Exception
Hope you can help me to achive this following the github sample approach.
I am using Dagger 2.14.1 in my Android application and migrated recently from 2.10. The new version makes it much easier to inject Activities and Fragments, but I can't find a way to inject a custom class where I cannot change the constructor as well. With 2.10 I could write custom inject functions and then use them to simply inject any class:
Simplified Dagger 2.10 Injection with Kotlin:
#Singleton
#Component(dependencies = [], modules = [ApplicationModule::class, ...])
interface ApplicationComponent {
fun application(): App
fun inject(authenticationActivity: AuthenticationActivity)
fun inject(converters: Converters)
// ...
}
class App : Application() {
companion object {
#JvmStatic
lateinit var component: ApplicationComponent
}
override fun onCreate() {
super.onCreate()
component = DaggerApplicationComponent.builder()
.applicationModule(ApplicationModule(this))
.build()
}
}
// This class is used by the Room database framwork and I cannot change the constructor and do class Converters #Inject constructor(private val gson: Gson) {
class Converters {
#Inject
protected lateinit var gson: Gson
init {
App.component.inject(this)
}
// ...
}
Simplified Dagger 2.14.1 Injection with Kotlin doesn't provide me with a ApplicationComponent Object to inject my custom classess:
#Singleton
#Component(modules = [ApplicationModule::class, ...])
interface ApplicationComponent : AndroidInjector<App> {
#Component.Builder
abstract class Builder : AndroidInjector.Builder<App>()
fun inject(converters: Converters) // doesn't work!
}
class App : Application(), HasActivityInjector {
#Inject
lateinit var activityInjector: DispatchingAndroidInjector<Activity>
override fun onCreate() {
super.onCreate()
// From where do I get my component to call .inject() from another class?
DaggerApplicationComponent.builder().create(this).inject(this)
}
override fun activityInjector(): AndroidInjector<Activity> = activityInjector
}
In short: the DaggerApplicationComponent.builder().create(this) returns an AndroidInjector<App!> Object. This Object only has one .inject() functions which only accepts my App Class. So I cannot inject anything else.
Of course there is a lot missing, but I wanted to only post the relevant code. My Dagger 2.14.1 implementation works and all my dependencies in Activities, Fragments and View Models get injected.
I am quite new to Dagger and the more I use it the more I love it, but I couldn't figure out on how to inject custom classes where I cannot change the constructor. Thanks for you help!
Try this:
class App : Application(), HasActivityInjector {
#Inject
lateinit var activityInjector: DispatchingAndroidInjector<Activity>
companion object {
#JvmStatic
lateinit var instance: App
}
#Inject
lateinit var component: ApplicationComponent
override fun onCreate() {
super.onCreate()
instance = this
DaggerApplicationComponent.builder().create(this).inject(this)
}
override fun activityInjector(): AndroidInjector<Activity> = activityInjector
}
Then
object Injector {
#JvmStatic fun get(): ApplicationComponent = App.instance.component
}
Now you can do
class Converters {
#Inject
protected lateinit var gson: Gson
init {
Injector.get().inject(this)
}
I am trying to get dagger working in my application.
After creating Module Component and MyApp i can use dagger to inject database service into view but i am having trouble doing same thing with presenter.
Code:
class MyApp : Application() {
var daoComponent: DaoComponent? = null
private set
override fun onCreate() {
super.onCreate()
daoComponent = DaggerDaoComponent.builder()
.appModule(AppModule(this)) // This also corresponds to the name of your module: %component_name%Module
.daoModule(DaoModule())
.build()
}
}
Module
#Module
class DaoModule {
#Provides
fun providesEstateService(): EstateService = EstateServiceImpl()
}
Component
#Singleton
#Component(modules = arrayOf(AppModule::class, DaoModule::class))
interface DaoComponent {
fun inject(activity: MainActivity)
}
AppModule
#Module
class AppModule(internal var mApplication: Application) {
#Provides
#Singleton
internal fun providesApplication(): Application {
return mApplication
}
}
MainActivity
class MainActivity : MvpActivity<MainView, MainPresenter>(), MainView {
#Inject
lateinit var estateService : EstateService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
(application as MyApp).daoComponent!!.inject(this)estateService.numberOfInvoicedEstates.toString()
}
override fun createPresenter(): MainPresenter = MainPresenterImpl()
}
After injecting estateService this way I can use it, but I cant figure out how do I inject service directly into the presenter.
I tried doing it like this but it isn't working.
Should I just pass injected objects from the activity? or maybe I should pass MyApp as an argument or make static method allowing my to get it from any place in the application?
class MainPresenterImpl
#Inject
constructor(): MvpBasePresenter<MainView>(),MainPresenter {
#Inject
lateinit var estateService : EstateService
}
Your component should provide the presenter like that:
#Component(modules = arrayOf(AppModule::class, DaoModule::class))
#Singleton
interface MyComponent {
mainPresenter() : MainPresenter
}
And all dependencies the presenter needs are injected to the presenter via constructor parameters:
class MainPresenterImpl
#Inject constructor(private val estateService : EstateService ) :
MvpBasePresenter<MainView>(),MainPresenter {
...
}
Than in createPresenter() just grab the presenter from dagger component like this:
class MainActivity : MvpActivity<MainView, MainPresenter>(), MainView {
...
override fun createPresenter(): MainPresenter =
(application as MyApp).myComponent().mainPresenter()
}
Please note that the code shown above will not compile. This is just pseudocode to give you an idea how the dependency graph could look like in Dagger.
Please refer to this example on how to use Dagger 2 in combination with MVP.
You have to tell your component where you want to inject it.
So, try with this component:
#Singleton
#Component(modules = arrayOf(AppModule::class, DaoModule::class))
interface DaoComponent {
fun inject(presenter: MainPresenterImpl)
}