Not able to inject a class using SubComponent in Dagger 2 - android

I'm trying to add Dagger 2 in one project. I have created 3 components
AppComponent (the primary one)
RetrofitComponent (Dependent component works fine)
ControllerComponent (Subcomponent, not injecting properly)
this is my AppComponent
#ApplicationScope
#Component(modules = [ApplicationModule::class, PrefModule::class])
interface AppComponent {
fun inject(instance: AppInstance)
fun getAppPref(): AppPreference
fun newControllerComponent(controllerModule: ControllerModule): ControllerComponent
}
this is my ControllerComponent
#UIScope
#Subcomponent(modules = [ControllerModule::class])
interface ControllerComponent {
fun injectActivity(activity: BaseActivity)
fun injectDialog(dialog: BaseDialog)
}
ControllerModule
#Module
class ControllerModule(activity: FragmentActivity) {
var mActivity: FragmentActivity
init {
mActivity = activity
}
#Provides
fun getContext(): Context {
return mActivity
}
#Provides
fun getActivity(): Activity {
return mActivity
}
#Provides
fun getFragmentManager(): FragmentManager {
return mActivity.supportFragmentManager
}
#Provides
fun getDialogManager(fragmentManager: FragmentManager): DialogsManager
{
return DialogsManager(fragmentManager)
}
#Provides
fun getDialogsFactory(): DialogsFactory {
return DialogsFactory()
}
}
Injecting the activity is working fine, but when I try to inject the Dialog its never injecting
controller.injectDialog(singleDialog)
My entire code is here Github link. Need some help. What am I not doing or doing wrong that I can't inject from subcomponent.

To use field injection on a given class, you need to specify the concrete type rather than its superclass.
#UIScope
#Subcomponent(modules = [ControllerModule::class])
interface ControllerComponent {
//fun injectActivity(activity: BaseActivity)
//fun injectDialog(dialog: BaseDialog)
fun inject(activity: SomeSpecificActivity)
fun inject(activity: SomeOtherActivity)
fun inject(dialog: SomeSpecificDialog)
fun inject(dialog: SomeOtherDialog)
}

The Injection is working, but you are injecting nothing inside SingleClickDialog class, remember than the method inject(class : Class) is for let it to know dagger that class will be injected, but you need to put the elements you need inject inside that class with the annotation #Inject, and those elements you need to add its provider in ControllerModule as well

Related

How can I return a component from Application to module?

Trying to understand the following example and do something similar.
In my application there is a module, in which I want to use Dagger.
To do this I need an Application class in which I initialize and store AppComponent
Judging from the documentation I need to create an interface with a component from my module:
interface PasscodeSetupComponentProvider {
fun providePasscodeSetupComponent(): PasscodeComponent
}
Then I will implement this interface for my Application class:
open class FenturyApplication : PasscodeSetupComponentProvider {
lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
appComponent = DaggerAppComponent.builder()
.appModule(AppModule(applicationContext))
.build()
}
override fun providePasscodeSetupComponent(): PasscodeComponent {
return appComponent.passcodeComponent
}
}
But judging by the example from the documentation I don't understand what should be in my interface Appcomponent namely passcodeComponent.
In the example, it looks like this:
class MyApplication: Application(), LoginComponentProvider {
// Reference to the application graph that is used across the whole app
val appComponent = DaggerApplicationComponent.create()
override fun provideLoginComponent(): LoginComponent {
return appComponent.loginComponent().create()
}
}
I added the following code to my AppComponent:
#Component(modules = [AppModule::class])
#Singleton
interface AppComponent {
val applicationContext: Context
fun passcodeComponent(): PasscodeComponent
}
And if I understood correctly, then in the fragment that is in my module, I can write the following:
lateinit var passcodeComponent: PasscodeComponent
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val context = activity?.applicationContext ?: return
passcodeComponent = (context as PasscodeSetupComponentProvider).providePasscodeSetupComponent()
passcodeComponent.inject(this)
After that I hope I can use dagger in my module.
You're missing a crucial part of how AppComponent is written. You can read the complete code in this link.
Please note how Google is declaring the LoginComponent (as Subcomponent) and how do they declare it inside AppComponent in order do return a new instance of LoginComponent.
AppComponent (copied from the link I posted)
#Singleton
#Component(modules = [NetworkModule::class, SubcomponentsModule::class])
interface ApplicationComponent {
// This function exposes the LoginComponent Factory out of the graph so consumers
// can use it to obtain new instances of LoginComponent
fun loginComponent(): LoginComponent.Factory
}
And the code for LoginComponent
#Subcomponent
interface LoginComponent {
// Factory that is used to create instances of this subcomponent
#Subcomponent.Factory
interface Factory {
fun create(): LoginComponent
}
fun inject(loginActivity: LoginActivity)
}

android - Dagger 2 injecting multiple interface and the implementation class

So i want to build multi modular android project and have a problem with injecting the interface and the implementation.
I have two interface for navigation between fragment
first interface
interface Fragment1Navigation {
fun navigateToFragment1()
fun navigateToFragment2()
}
second interface
interface Fragment2Navigation {
fun navigateToFragment3()
fun navigateToFragment4()
}
then i have the class that implement those two interfaces
class Navigator: BaseNavigator(), Fragment1Navigation, Fragment2Navigation {
override fun navigateToFragment1() {
// some implementation
}
override fun navigateToFragment2() {
// some implementation
}
override fun navigateToFragment3() {
// some implementation
}
override fun navigateToFragment4() {
// some implementation
}
}
i want to inject this Navigator class in my mainActivity to bind my navcontroller first and also i want to inject the interface in other fragment and i made the module class like this
#Module()
class NavigationModule {
#Provides
#Singleton
fun provideNavigator(): Navigator = Navigator()
#Provides
fun provideFragment1Navigation(): Fragment1Navigation = Navigator()
#Provides
fun provideFragment2Navigation(): Fragment2Navigation = Navigator()
}
I want those provideFragmentNavigation and provideNavigatioin to provide the same Navigator() so interface in fragment lead to same navigator like in main activity, but turns out it provide different instance so the interface in fragment lead to different navigator from mainactivity
Two ways u can achieve this:
using #Binds instead of #Provides
passing the Navigation() object directly to the constructor of NavigationModule
using binds
class Navigator #Inject constructor(): BaseNavigator(), Fragment1Navigation, Fragment2Navigation {
// body
}
Module
#Module()
abstract class NavigationModule {
#Binds
abstract fun provideNavigator(navigator: Navigator): BaseNavigator
#Binds
abstract fun provideFragment1Navigation(navigator: Navigator): Fragment1Navigation
#Binds
abstract fun provideFragment2Navigation(navigator: Navigator): Fragment2Navigation
}

Inject dependency that requires parameter in constructor

I'm creating an application in Kotlin using the MVP pattern.
I would need to inject a Repository into my Presenter for this purpose. Except that for this, my Repository requires a Retrofit interface as a parameter of its constructuor.
I'm a beginner in the use of Dagger2, and the answers found on the internet are far too complicated for such a basic case like mine.
Here's the repository i want to be injected :
class RepositoryInventory(private val api: Service): IRepositoryInventory {
override fun getInventoryItemByNum(itemnum: String): Observable<Response<Item>> {
return api.getInventoryItemByNum(itemnum)
.toObservable()
}
override fun getAllInventoryItems(): Single<Response<Item>> {
return api.getAllInventoryItems()
}
}
My Component
#Singleton
#Component(modules = arrayOf(ActivityModule::class))
interface ActivityComponent {
fun inject(loginActivity: LoginActivity)
fun inject(itemDetailActivity: ItemDetailActivity)
}
My module :
#Module
class ActivityModule(private var activity: Activity) {
#Provides
fun provideActivity(): Activity {
return activity
}
#Provides
fun provideLoginPresenter(): LoginPresenter {
return LoginPresenter()
}
#Provides
fun provideItemDetailPresenter(): ItemDetailPresenter {
return ItemDetailPresenter()
}
}
In my activity, my module is injected with this method :
private fun injectDependency() {
val activityComponent = DaggerActivityComponent.builder()
.activityModule(ActivityModule(this))
.build()
activityComponent.inject(this)
}
I have 2 components and 2 modules: one designed to inject into a fragment and the other into an activity.
Except in my case, I want to inject into a Presenter that is not a Fragment or an Activity but a class
Ok, my guess is you want to inject RepositoryInventory into LoginPresenter. If so, you can make use of #ContributesAndroidInjector and Binds
First, create a LoginActivityModule
#Module
abstract class LoginActivityModule {
#Binds
abstract fun loginPresenter(loginPresenter: LoginPresenter): LoginPresenter
}
Then, create a module called ActivityBindingModule
#Module
abstract class ActivityBindingModule {
#ContributesAndroidInjector(modules = [LoginActivityModule::class])
abstract fun loginActivity(): LoginActivity
#ContributesAndroidInjector()
abstract fun itemDetailActivity(): ItemDetailActivity
}
And change your ActivityComponent like this
#Singleton
#Component(modules = arrayOf(ActivityModule::class, ActivityBindingModule::class))
interface ActivityComponent {
}
And in your LoginPresenter:
class LoginPresenter #Inject constructor(private val repositoryInventory: IRepositoryInventory) {
...
}
Remember to remove this in ActivityModule:
#Provides
fun provideLoginPresenter(): LoginPresenter {
return LoginPresenter()
}

Android UI testing for MVVM architecture with Dagger2, Espresso and Mockito

I'm trying to add UI tests for the first time to an app, and I was looking at using espresso.
The app uses Dagger2 for DI with #Inject annotations for classes that should be injectable, and AndroidInjection/AndroidSupportInjection in screens (Activity / Fragment).
class Application : android.app.Application(), HasActivityInjector, HasServiceInjector {
...
override fun onCreate() {
super.onCreate()
initDagger()
Timber.d("Application initialized successfully!")
}
protected open fun initDagger() {
Components.initialize(this)
}
}
object Components : ComponentFactory {
private lateinit var sComponent: AppComponent
fun initialize(app: Application) {
sAppComponent = DaggerAppComponent.builder()
.applicationModule(ApplicationModule(this))
.build()
}
// overrides
}
interface ComponentFactory {
fun app(): AppComponent
fun authenticated(): AuthenticatedComponent
}
Next the Components and Modules. The AppActivitiesModule and AuthenticatedActivitiesModule are classes with #ContributesAndroidInjector for screens.
#Singleton
#Component(modules = [AppModule::class, AppActivitiesModule::class, AndroidInjectionModule::class, AndroidSupportInjectionModule::class])
interface AppComponent {
fun authenticatedComponentBuilder(): AuthenticatedComponent.Builder
fun inject(app: Application)
#Component.Builder
interface Builder {
fun build(): AppComponent
fun applicationModule(applicationModule: ApplicationModule): Builder
}
}
#Module
open class AppModule(private val application: Application) {
// some #Provides
}
#AuthenticatedScope
#Subcomponent(modules = [AuthenticatedModule::class, AuthenticatedActivitiesModule::class])
interface AuthenticatedComponent {
fun inject(application: Application)
#Subcomponent.Builder
interface Builder {
fun userModule(module: UserModule): Builder
fun build(): AuthenticatedComponent
}
}
#Module
class AuthenticatedModule(private val userId: Long,
private val userRole: User.Role) {
// Some #Provides #AuthenticatedScope
}
And a typical use case would be:
#Singleton
class AppLevelService
#Inject constructor(...) { ... }
#AuthenticatedScope
class AuthenticatedLevelServices
#Inject constructor(...) { ... }
class ViewModel
#Inject constructor(private val appService: AppLevelService,
private val authService: AuthenticatedLevelServices) { ... }
class MyActivity : BaseActivity {
#Inject
lateinit var vmProvider: Provide<ViewModel>
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
}
}
How can I make the test setup for this "type" of dagger usage?
I find a lot of examples of testing with dagger2 but for #Provides annotations, and I'm thinking there should be a way to mock injectable classes with #Inject.
I've tried DaggerMock but I get:
You must define overridden objects using a #Provides annotated method instead of using #Inject annotation
Not that it should affect anything, but I'm also using a custom Runner with DexOpener.
Any ideas or good documentation / examples for testing this setup?

Injecting dependencies in a Android ViewModel?

Hi i have a ViewModel that has two var's that get injected using Dagger 2.11.
However it never gets injected in my viewModel whilst everywhere else in my app, these same dependecy Vars get initialised and injected perfectly
Below is my viewModel
class MyViewModel : AndroidViewModel, MyViewModelContract {
private lateinit var pointsBalance: MutableLiveData<PointsBalance>
#Inject
lateinit var accountDelegator: AccountDelegatorContract
#Inject
constructor(application: Application) : super(application)
init {
DaggerApplicationComponent.builder().application(getApplication() as MyApplication).build().inject(this)
}
override fun getPointsBalance(): LiveData<PointsBalance> {
if (!this::pointsBalance.isInitialized) {
//get balance from network api etc
}
return pointsBalance
}
accountDelegator complains that it is not initialised
Below is how i bind this viewModel inside MyModule.class
#Provides
#JvmStatic
#Singleton
fun providesViewModel(viewModel: MyViewModel): MyViewModelContract = viewModel
my custom application
class MyApplication : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
val applicationComponent = DaggerApplicationComponent.builder()
.application(this)
.build()
applicationComponent.inject(this)
return applicationComponent
}
}
my applicationComponent
#Singleton
#Component(modules = arrayOf(MyModule::class,
))
interface ApplicationComponent : AndroidInjector<DaggerApplication> {
fun inject(mApplication: MyApplication)
override fun inject(instance: DaggerApplication)
#Component.Builder
interface Builder {
#BindsInstance
fun application(applicaton: MyApplication): Builder
fun build(): ApplicationComponent
}
}
How i use this viewmodel in a activity/fragment
viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
I believe issue is that you're not calling inject. You'd need for a start to add following to ApplicationComponent
void inject(MyViewModel viewModel);
What I'm doing here then fwiw is inheriting from AndroidViewModel (which gives access to application instance) then calling following in ViewModel class (you'd need to substitute DaggerProvider.getComponent with however you're accessing component in your code)
init {
DaggerProvider.getComponent(application).inject(this)
}

Categories

Resources