How to pass parameter from dagger to Adapter in kotlin - android

Provider
#Module
abstract class AddScriptOrContractFragmentProvider {
#ContributesAndroidInjector(modules = [(AddScriptOrContractFragmentModule::class)])
abstract fun provideAddScriptOrContractFragmentFactory(): AddScriptOrContractFragment
}
Module
#Module
class AddScriptOrContractFragmentModule {
#Provides
fun provideAddScriptOrContractAdapter(context : Context, addScriptOrContractViewModel: AddScriptOrContractViewModel,manageScrip: ManageScrip): AddScriptOrContractAdapter {
return AddScriptOrContractAdapter(
ArrayList<AddScrip>(),
context,
manageScrip, // ManageScrip is Data class unable to get
addScriptOrContractViewModel
)
}
}
Adapter
class AddScriptOrContractAdapter(private val mScripResponseList: MutableList<AddScrip>?,
private val context: Context, private val manageScrip: ManageScrip?,
private val viewModel: AddScriptOrContractViewModel) : RecyclerView.Adapter<BaseViewHolder>() {
}
Fragement
mLayoutManager.orientation = LinearLayoutManager.VERTICAL
mAddScriptAdapter = AddScriptOrContractAdapter(scripList, context!!, manageScrip,addScriptOrContractViewModel)
mAddScriptContractBinding.addScriptRecyclerView.layoutManager = LinearLayoutManager(activity)
mAddScriptContractBinding.addScriptRecyclerView.itemAnimator = DefaultItemAnimator()
mAddScriptContractBinding.addScriptRecyclerView.adapter = mAddScriptAdapter
Error Facing
error: [dagger.android.AndroidInjector.inject(T)] view.marketWatch.manage.ManageScrip
cannot be provided without an #Inject constructor or from an #Provides-annotated method.
What i am trying to do is data binding in adapter with dagger . i want to send a object that is data class to adapter and on that do some business login.
I dont understand what i doing wrong , when i send arraylist() its working fine but when i try to send object or string its facing error please help. Tanx in advance

Like the error states, there are two possible way to provide a dependency:
First one by constructor injection:
data class ManageScrip #Inject constructor(val dependency: Dependency)
Second by implementing it with an annotated method within a module:
#Module
class ProvidingModule {
#Provides
fun provideManageScrip(dependency: Dependency) =
ManageScrip("some", "properties")
}
It all depends on your setup and how you actually have to create the ManageScrip. In general it would be reasonable for data classes to use the second approach.

Related

Error: cannot be provided without an #Provides-annotated method

I got an error
public abstract static class SingletonC implements FragmentGetContextFix.FragmentGetContextFixEntryPoint,
^
kotlin.jvm.functions.Function1<? super pl.beskidmedia.bm.viewModel.repozytory.model.Token,kotlin.Unit> is injected at
pl.beskidmedia.bm.domainlayer.viewmodel.tv.TvViewModel(fetchHlsUseCase, �)
pl.beskidmedia.bm.domainlayer.viewmodel.tv.TvViewModel is injected at
pl.beskidmedia.bm.domainlayer.viewmodel.tv.TvViewModel_HiltModules.BindsModule.binds(vm)
#dagger.hilt.android.internal.lifecycle.HiltViewModelMap java.util.Map<java.lang.String,javax.inject.Provider<androidx.lifecycle.ViewModel>> is requested at
dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.ViewModelFactoriesEntryPoint.getHiltViewModelMap() [pl.beskidmedia.bm.MainApplication_HiltComponents.SingletonC ? pl.beskidmedia.bm.MainApplication_HiltComponents.ActivityRetainedC ? pl.beskidmedia.bm.MainApplication_HiltComponents.ViewModelC]
when i tried to provide my use-case to view model, here are my functions that should create use case invoke() instance that i want to use later in my view model
fun getHls(
tvRepository: TvRepository
): Flow<List<Hls>> {
return tvRepository.getHls()
}
typealias GetHlsUseCase = () -> Flow<List<Hls>>
fun fetchHls(
tvRepository: TvRepository,
token: Token
) {
return tvRepository.fetchHls(token.token)
}
typealias FetchHlsUseCase = (Token) -> Unit
and here are my modules
#Module
#InstallIn(ViewModelComponent::class)
class ProvideFetchHlsUseCase {
#Provides
fun provideFetchHlsUseCase(
tvRepository: TvRepository
): FetchHlsUseCase = { fetchHls(tvRepository, it) }
}
#Module
#InstallIn(ViewModelComponent::class)
class ProvideGetHlsUseCase {
#Provides
fun provideGetHlsUseCase(
tvRepository: TvRepository
): GetHlsUseCase = { getHls(tvRepository) }
}
and finally my view model
#HiltViewModel
class TvViewModel #Inject constructor(
private val fetchHlsUseCase: FetchHlsUseCase,
private val getHlsUseCase: GetHlsUseCase
) : BaseViewModel() {
val hls = getHlsUseCase().asLiveData(coroutineExceptionHandler)
}
I can't change my provide to binding (this is what i saw in different questions here) or at least I don't know how to do it. When I was implementing this into my project I was inspired by https://medium.com/swlh/functional-use-cases-f896f92e768f this article, but author show only how to implement view model (no interface or bindings). From what i understands dagger should use designated provide methods that I implemented in modules but for some reason it doesn't happen.
Ok so this was a tricky one cuz this problem is actually well known but it is hard to google if you don't know how to name it, this was resolved here
In short all I needed to do was insert #JvmSuppressWildcards behind my typealias "classes" in ViewModel at the insertion point like so
#HiltViewModel
class TvViewModel #Inject constructor(
private val fetchHlsUseCase: #JvmSuppressWildcards FetchHlsUseCase,
private val getHlsUseCase: #JvmSuppressWildcards GetHlsUseCase
) : BaseViewModel() {
putting this annotation behind private keyword did nothing that why i had problems with this :D

Value not saved into the data class

I am currently building an app and I have added Dagger Hilt in order to define a single class to access data. the injection seems working fine but I am not able to store a value in the data class I use.
I have created a Singleton first, which is used by the code to set/get value from a data structure.
#Singleton
class CarListMemorySource #Inject constructor() : CarListInterface {
private var extendedCarList: ExtendedCarList? = null
override fun setListOfVehicles(listOfVehicles: List<item>)
{
extendedCarList?.listOfVehicles = listOfVehicles
}
}
When I am calling setListOfVehicles the listOfVehicules contains 10 items but
The data structure ExtendedCarList is defined as below:
data class ExtendedCarList(
var listOfVehicles: List<item>
)
The Singleton in passed using Hilt like for example in the viewModel below:
#HiltViewModel
class HomeScreenViewModel #Inject constructor(
private val carList: CarListMemorySource
): ViewModel() {
fun getList() {
--> DO SOMETHING TO Get A ListA
carList.setListOfVehicles(ListA)
}
}
And in the activity, using the viewModel, I am just doing this:
#AndroidEntryPoint
class HomeScreenActivity: AppCompatActivity() {
private val viewModel: HomeScreenViewModel by viewModels()
....
viewModel.getList()
....
}
Any idea why and how to fix it ?
Thanks
you never initialize extendedCarList.
extendedCarList?.listOfVehicles = listOfVehicles
above line is exactly the same as
if (extendedCarList != null) extendedCarList.listOfVehicles = listOfVehicles
But it never passes the null check.
I think just changing
private var extendedCarList: ExtendedCarList? = null
to
private val extendedCarList = ExtendedCarList()
might solve it

How to do constructor injection for a single parameter out of many?

I am using Hilt for dependency injection. I want to do constructor injection in adapter for a single parameter. Below is my adapter class:
public class MovieHorizontalListAdapter(
private val context: Context,
private val adapterDataList: List<Any>,
private val dataType: String = "Movie",
private val userUtils: UserUtils
): RecyclerView.Adapter<BaseViewHolder<*>>() {
// adapter related code
}
I only want to inject the userUtils: UserUtils parameter to be injected. Is this possible?
I have already created the below for UserUtils class:
#Singleton
#Provides
fun provideUserUtils(prefsManager: PrefsManager): UserUtils {
return UserUtils(prefsManager)
}
I am having trouble injecting into to adapter class. What are the possible ways to achieve this?

Hilt - How can I inject dependencies into adapter?

I am providing dependency in module:
#Provides
#Singleton
fun provideImageUtil(#ImageUrl imageUrl: String): ImageUtil = GlideImageUtil(imageUrl)
I am trying to inject it into RecyclerView adapter:
class MainAdapter(private val goods: ArrayList<GoodItem>) : RecyclerView.Adapter<MainAdapter.DataViewHolder>() {
#Inject
lateinit private var imageUtil: ImageUtil
I used to inject this way using Dagger:
object Injector {
lateinit var appComponent: AppComponent
fun initAppComponent(context: Context){
if(context is Activity){
throw IllegalStateException("pass an Application as an argument to avoid memory leaks")
}
appComponent = DaggerAppComponent.builder()
.appModule(AppModule(context))
.build()
}
}
In adapter:
init {
Injector.appComponent.inject(this)
}
How can I inject dependency into the adapter using Hilt? As I understood now "appComponent" is generated by Hilt. How can I access it?
First create EntryPoint in your custom class
#EntryPoint
#InstallIn(SingletonComponent::class)
interface MyEntryPoint {
fun getImageUtil(): ImageUtil
}
It is simple interface with #EntryPoint annotation. Since your dependency (ImageUtil) is singleton you should use #InstallIn(SingletonComponent::class) annotation to declare component. Finally declare a method to get your dependency fun getImageUtil(): ImageUtil
You can get your dependency in init block of your Adapter
init {
val myEntryPoint = EntryPointAccessors.fromApplication(context, MyEntryPoint::class.java)
imageUtil = myEntryPoint.getImageUtil()
}
Full code
class MainAdapter(
context: Context,
private val goods: ArrayList<GoodItem>
) : RecyclerView.Adapter<MainAdapter.DataViewHolder>() {
var imageUtil: ImageUtil
#EntryPoint
#InstallIn(SingletonComponent::class)
interface MyEntryPoint {
fun getImageUtil(): ImageUtil
}
init {
val myEntryPoint = EntryPointAccessors.fromApplication(context, MyEntryPoint::class.java)
imageUtil = myEntryPoint.getImageUtil()
}
}
See also https://developer.android.com/training/dependency-injection/hilt-android#not-supported
You can take a look at Assisted Injection with Dagger 2 which allows you to pass in types that might not be available initially and needs to be passed later. https://dagger.dev/dev-guide/assisted-injection.html
You'll have to add an #AndroidEntryPoint and likely will have to inject the constructor,because the imageUrl needs to come from somewhere; for example from Gradle:
javaCompileOptions {
annotationProcessorOptions {
arguments += ["imageUrl": "..."]
}
}
And I think #Singleton #Provides / #Singleton #Binds annotations do require a scope.

I can't inject Dao to a Repository Class, with HILT, in Android

i follow this tutorial Room Dependency Injection - MVVM To-Do List App with Flow and Architecture Components #4
Now I want to convert, a hole app that I have with Room, to this Clean Architecture.
In the tutorial Florian uses DI, to inject TaskDao into TaskViewModel, but I have a Repositories clases.
So I get to a point where the app is build without errors.
and this is my Repository:
class AnioRepository constructor(
private val agrotrackerApi: AgrotrackerApi
) {
val TAG = "AnioRepository"
//val anioDao: AnioDao
fun downloadAnios(): AniosResponse? {
GlobalScope.launch(Dispatchers.IO) {
val result = agrotrackerApi.getAnios()
if (result.isSuccessful) {
for (anio in result.body()!!){
Log.d(TAG, anio.toString())
}
}
}
return null
}
fun getAnios() {
//anioDao.getListAnios()
}
}
and this is my RepositoryModule:
#Module
#InstallIn(ApplicationComponent::class)
object RepositoryModule {
#Singleton
#Provides
fun providesAnioRepository( agrotrackerApi: AgrotrackerApi) : AnioRepository {
return AnioRepository(agrotrackerApi)
}
}
So I'm trying to add Dao class to the Repository Class, like this:
class AnioRepository constructor(
private val anioDao: AnioDao,
private val agrotrackerApi: AgrotrackerApi
) {
val TAG = "AnioRepository"
...
and then, change RepositoryModule, to match constructor...
...
fun providesAnioRepository( anioDao: AnioDao, agrotrackerApi: AgrotrackerApi) : AnioRepository
= AnioRepository(anioDao, agrotrackerApi)
...
but when I press Ctrl-F9, i get this error:
public abstract class ATMDatabase extends androidx.room.RoomDatabase {
^C:\pryectos\AndroidStudioProjects\ATMobileXKt\app\build\generated\source\kapt\debug\com\cse\atm\ATMApplication_HiltComponents.java:155:
error: [Dagger/MissingBinding] #com.cse.atm.di.ApplicationScope
kotlinx.coroutines.CoroutineScope cannot be provided without an
#Provides-annotated method. public abstract static class SingletonC
implements ATMApplication_GeneratedInjector,
^
#com.cse.atm.di.ApplicationScope kotlinx.coroutines.CoroutineScope is injected at
com.cse.atm.database.ATMDatabase.Callback(�, applicationScope)
com.cse.atm.database.ATMDatabase.Callback is injected at
com.cse.atm.di.AppModule.providesDatabase(�, callback)
com.cse.atm.database.ATMDatabase is injected at
com.cse.atm.di.AppModule.providesAnioDao(db)
com.cse.atm.database.AnioDao is injected at
com.cse.atm.di.RepositoryModule.providesAnioRepository(anioDao, �)
javax.inject.Provider<com.cse.atm.data.AnioRepository> is injected at
What means this error? What I'm missing ?
#ApplicationScope annotation is in another AppModule.kt, I dont' where is the problem.
Any help wil be appreciated!!
Best Regards
Your ATMDatabase.Callback is requesting a CoroutineScope with custom qualifier #ApplicationScope, but you are not providing such a CoroutineScope in any of your modules.
To provide a coroutine scope, the tutorial you linked adds this code around the 28-minute mark:
#Provides
#Singleton
fun provideApplicationScope() = CoroutineScope(SupervisorJob())
Since you are using the #ApplicationScope qualifier when requesting a coroutine scope, you will also need to add the qualifier to this #Provides method:
#Provides
#Singleton
#ApplicationScope
fun provideApplicationScope() = CoroutineScope(SupervisorJob())

Categories

Resources