Android Hilt : fragment needs to be provided for - android

I am converting an old app to HILT however I am having some difficulties with an interface.
fragment having the issue
#AndroidEntryPoint
class mainFragment : baseFragment(), Listener
My fragment implements a interface called Listener
viewModel
#HiltViewModel
class PviewModel #Inject constructor #Inject constructor(val listener: Listener) : baseViewModel()
I found what I thought was a possible solution here How to Bind/Provide Activity or Fragment with Hilt? but it gives me an error about providing the fragment still
here is the module
#Module
#InstallIn(FragmentComponent::class)
object FragmentModule {
#Provides
fun provideCallback(fragment: mainFragment) =
fragment as Listener
}
Error itself
error: [Dagger/MissingBinding] ....mainFragment cannot be provided without an #Inject constructor or an #Provides-annotated method.

Related

Activity cannot be provided without an #Inject constructor or an #Provides-annotated method | Migrating from Dagger2 to Hilt

So I'm trying to migrate my whole app from dagger to hilt and apparently I'm unable to inject activities and fragments.
My Activities:
#AndroidEntryPoint()
class MainActivity : AuthorizedFlowActivity<ActivityMainBinding>(), MainActivityUiEventHandler,
MainActivityBottomSheetBehavior,
MainActivityOpenDrawer {
My fragments:
#AndroidEntryPoint
class ProfileFragment : BaseFragment<FragmentCaptainProfileBinding>() {
My Application Class:
#HiltAndroidApp
class App : Application() {
Injecting my activities like this:
class SplashActivityNavigatorImpl #Inject constructor(
splashActivity: SplashActivity,
private val preferences: JameelPreferences
)
And it's throwing this error:
error: [Dagger/MissingBinding] com.abc.presentation.main.activities.MainActivity cannot be provided without an #Inject constructor or an #Provides-annotated method. This type supports members injection but cannot be implicitly provided.
Project Level Gradle:
classpath "com.google.dagger:hilt-android-gradle-plugin:$libVersion.hiltVersion"
App Level Gradle (all 3 modules):
id("dagger.hilt.android.plugin")
implementation "com.google.dagger:hilt-android:$libVersion.hiltVersion"
kapt "com.google.dagger:hilt-android-compiler:$libVersion.hiltCompilerVersion"
Could it be that my activities and fragments are extending bases which cannot be annotated by the #AndroidEntryPoint because they have type parameters?? HELP!
Also with dagger2, I was using interfaces like:
#Module
abstract class AppActivitiesInjector {
#ActivityScope
#ContributesAndroidInjector(modules = [RiderInviteFriendFragmentsInjector::class, RiderInviteActivityModule::class, BaseActivityModule::class])
abstract fun provideRiderInviteFriendActivity(): RiderInviteFriendActivity
#ActivityScope
#ContributesAndroidInjector(modules = [OffersActivityFragmentsInjector::class, OffersActivityModule::class, BaseActivityModule::class])
abstract fun provideOffersActivity(): OffersActivity
#ActivityScope
#ContributesAndroidInjector(modules = [TripsHistoryActivityFragmentsInjector::class, TripsHistoryActivityModule::class, BaseActivityModule::class])
abstract fun provideTripsActivity(): TripsHistoryActivity
#ActivityScope
#ContributesAndroidInjector(modules = [RiderProfileActivityFragmentsInjector::class, RiderProfileActivityModule::class, BaseActivityModule::class])
abstract fun provideRiderProfileActivity(): RiderProfileActivity
#ActivityScope
#ContributesAndroidInjector(modules = [PaymentActivityModule::class, PaymentActivityFragmentsInjector::class, BaseActivityModule::class])
abstract fun providePaymentActivity(): PaymentActivity
}
And it was working, but hilt is supposedly to reduce such boiler plate? right?
Thanks
As the error says: There is nothing that supports providing the activities and fragments. The Entry point annotations means just: "inject here". So dagger will generate everything to support "what needs to be injected in the fragments and activities". You need to provide somehow the activity. Try checking these answers:
Provide Activity instance with Hilt
How to Bind/Provide Activity or Fragment with Hilt?

How to inject adapter with hilt in fragment?

If Adapter have an interface like clickListener, Fragment implement that interface and Fragment pass the instance of interface in constructor to the adapter, How to inject adapter with hilt?
How to solve this issue?
Here is the error
error: [Dagger/MissingBinding] ... cannot be provided without an #Inject constructor or an #Provides-annotated method. This type supports members injection but cannot be implicitly provided.
#AndroidEntryPoint
class HomeFragment: Fragment(), ShowsAdapter.Interaction {
#Inject
lateinit var adapter: ShowsAdapter
private val viewModel: HomeViewModel by hiltNavGraphViewModels(R.id.my_nav)
....
}
class ShowsAdapter #Inject constructor(private val interaction: Interaction) :
RecyclerView.Adapter<ShowsAdapter.ShowsHolder>() {
....
interface Interaction {
fun onItemSelected(show: Show)
}
}
#Module
#InstallIn(FragmentComponent::class)
abstract class HomeModule {
#Binds
abstract fun provideInteraction(homeFragment: HomeFragment): ShowsAdapter.Interaction
}

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
}

Binding Activity to the LifecycleOwner with Dagger

I'm creating a lifecycle aware class (that will be injected in the activity). It takes two params - event bus and lifecycle owner.
In that case can I bind activity to the lifecycleowner?
My lifecycle aware class:
class Bus #Inject constructor(
private val eventBus: EventBus,
private val lifecycleOwner: LifecycleOwner) : LifecycleObserver { ... }
The binding in my module:
#Binds
abstract fun bindLifecycle(activity: SampleActivity): LifecycleOwner
And then I'm injecting the Bus in ActivityX as usual:
#Inject
lateinit var bus: Bus
I'm getting the following error:
ActivityX cannot be provided without an #Inject constructor or an #Provides-annotated method.
So my question: is my approach correct OR it's not possible, since activities DON'T support constructor injection?
#Binds annotation "ties" a dependency that is already provided by Dagger to some other type.
By using:
#Inject
lateinit var bus: Bus
you're not making SampleActivity a Dagger dependency (so it is not provided by Dagger). It only uses dependencies provided by Dagger so it cannot be used with #Binds annotation.
You should have an ActivityModule that can expose a LifecycleOwner as a dependency:
#Module
class ActivityModule(private val activity: AppCompatActivity) {
#Provides
fun provideLifecycleOwner(): LifecycleOwner {
return activity
}
}
(remember that the class that will use a LifecycleOwner must share the scope with the component that uses ActivityModule)

Inject Adapter class to Fragment using Dagger2

I have followed Android Architecture Blueprints Dagger2 for dependency injection: URL
Now I want to inject Adapter to my Fragment class:
#ActivityScoped
class MainFragment #Inject
constructor(): DaggerFragment(), ArtistClickCallback {
#Inject lateinit var adapter : ArtistAdapter
}
Main Module class:
#Module
abstract class MainModule {
#FragmentScoped
#ContributesAndroidInjector(modules = [MainFragmentModule::class])
internal abstract fun mainFragment(): MainFragment
#Binds
internal abstract fun bindArtistClickCallback(mainFragment: MainFragment) : ArtistClickCallback
}
MainFragmentModule:
#Module
class MainFragmentModule {
#Provides
fun provideArtistAdapter() = ArtistAdapter()
}
And this is my adapter class:
class ArtistAdapter #Inject constructor(
private val artistClickCallback : ArtistClickCallback
) : PagedListAdapter<LastFmArtist, RecyclerView.ViewHolder>(POST_COMPARATOR)
When I build the project I get following Kotlin compiler error:
error: [Dagger/DependencyCycle] Found a dependency cycle:
public abstract interface AppComponent extends dagger.android.AndroidInjector<com.sample.android.lastfm.LastFmApp> {
^
com.sample.android.lastfm.ui.main.MainFragment is injected at
com.sample.android.lastfm.ui.main.MainModule.bindArtistClickCallback$app_debug(mainFragment)
com.sample.android.lastfm.ArtistClickCallback is injected at
com.sample.android.lastfm.ui.main.ArtistAdapter.artistClickCallback
com.sample.android.lastfm.ui.main.ArtistAdapter is injected at
com.sample.android.lastfm.ui.main.MainFragment.adapter
com.sample.android.lastfm.ui.main.MainFragment is injected at
com.sample.android.lastfm.ui.main.MainActivity.mainFragment
com.sample.android.lastfm.ui.main.MainActivity is injected at
dagger.android.AndroidInjector.inject(T) [com.sample.android.lastfm.di.AppComponent → com.sample.android.lastfm.di.ActivityBindingModule_MainActivity$app_debug.MainActivitySubcomponent]
Can you suggest me how to solve this problem?
Codes can be found at URL
Your fragment should probably not have #ActivityScoped as a scope. Further do not use constructor injection with fragments (or any other framework type)! The Android framework will create those objects in some cases, and you will end up with the wrong reference in your classes. Add the fragment to the corresponding component via its builder.
Also you're using a provides annotated method as well as constructor injection (#Inject constructor()). Pick one. Since you also use field injection within the ArtistAdapter the next "error" you would encounter would be a null callback because you don't inject the adapter anywhere. You just create the object.
Constructor injection should usually be favored, which will also inject fields. Remove the following completely, keep the annotation on the construcor:
#Provides
fun provideArtistAdapter() = ArtistAdapter()
Moving on, your error originates in MainActivitySubcomponent (last line) and seems to be because your MainFragment is bound as an ArtistClickCallback, but requires a ArtistAdapter which requires a ArtistClickCallback...hence your dependency cycle.
This issue should resolve itself once you fix the problems mentioned (#Inject constructor on the fragment in this case) above, since it originates through the MainFragment being constructed by Dagger within the MainActivitySubcomponent, which is the wrong place anyways since your fragment should have a lower scope than the Activity.
Further you need to move your binding (#Binds fun bindArtistClickCallback) into the MainFragmentModule, since there is no fragment to bind in the Activity component (where you add the binding currently)
When you fix all those issues, you will bind your fragment to the correct FragmentSubcomponent, where you will bind it as a Callback, with which you can then create the Adapter and it should work.
I recommend you have a more thorough look on Dagger and make sure to understand all the issues / fixes pointed out.
This is how it should look
#FragmentScoped
class MainFragment(): DaggerFragment(), ArtistClickCallback {
#Inject lateinit var adapter : ArtistAdapter
}
#Module
abstract class MainModule {
#FragmentScoped
#ContributesAndroidInjector(modules = [MainFragmentModule::class])
internal abstract fun mainFragment(): MainFragment
}
#Module
class MainFragmentModule {
#Binds
internal abstract fun bindArtistClickCallback(mainFragment: MainFragment) : ArtistClickCallback
}
class ArtistAdapter #Inject constructor(
private val artistClickCallback : ArtistClickCallback
) : PagedListAdapter<LastFmArtist, RecyclerView.ViewHolder>(POST_COMPARATOR)

Categories

Resources