Dagger2 not working in android - 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

Related

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
}

Android Kotlin: integration tests using Dagger 2 and Mockito getting error, "zero interactions with this mock"

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

UninitializedPropertyAccessException occur when i use #inject in my presenter

Am getting UninitializedPropertyAccessException when i inject a methord in my presenter
My provider class
#Module
class ActivityModule(private var activity: BaseActivity) {
#Provides
fun provideActivity(): BaseActivity {
return activity
}
#Provides
#Inject
fun providePresenter(): MainContract.Presenter {
return MainPresenter()
}
#Provides
#Singleton
fun provideGson(): Gson {
return GsonBuilder().setLenient().create()
}
#Provides
#Inject
#Singleton
fun provideServiceGenerator(): ServiceGenerator {
return ServiceGenerator()
}
}
My component class
#Component(modules = [ActivityModule::class])
interface ActivityComponent {
fun inject(mainActivity: MainActivity)
}
And in my Activity class i am injecting the Components as shown below
val activityComponent = DaggerActivityComponent.builder()
.activityModule(ActivityModule(this))
.build()
activityComponent.inject(this)
All works fine in a button click i am calling one api As you can see in my provide i have MainContract.Presenter, am injecting the presenter in my activity and its injected successfully.
#Inject
lateinit var presenter: MainContract.Presenter
Now in My Presenter there is a ServiceGenerator class which i also provided in my provider class and i am injecting the service generatort in my presenter,The proble happens when i call the presenter the injected ServiceGenerator inside the presenter is giving UninitializedPropertyAccessException What is the cause of it and how can i solve this?
The snippet of presenter class as shown below
class MainPresenter : MainContract.Presenter {
#Inject
lateinit var serviceGenerator: ServiceGenerator
When i going for val newsService = serviceGenerator.createService(ApiService::class.java,Constants.BASE_URL)
call am getting the error,Please guide me if am doing anything wrong
You can do one of two things:
Stick with property/field injection for your presenter, in this case you need to add an inject method for it in your Component, and call it in your presenter, just like you've done with your Activity.
Use constructor injection, this way the presenter's dependencies will be injected "automatically" when it's injected in the Activity:
class MainPresenter #Inject constructor(
private val serviceGenerator: ServiceGenerator
) : MainContract.Presenter {
}

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.

How can i inject object into presenter in android kotlin MVP mosby app with dagger

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)
}

Categories

Resources