Android Viewmodel savestate using dagger2 - android

I am using ViewModel savestate handle in my app, I inject ViewModel using the dagger, So I used AssistInject to inject SaveStateHandle in to viewmodel. Everything works fine for me, But one thing I need to know is that I need to pass the bundle which I received in the fragment to the savestatehandle. So in my fragment module, I provide the method injection for the fragment/activity which am using and get the arguments/intent and so while using the scope I can get the same bundle in the viewmodel factory.
Example:
Dagger Fragment/Activity module class
#FragmentScope
#ContributesAndroidInjector(modules = [TestingModule::class])
abstract fun bindTestingScreenFragment(): TestingScreen
TestingModule::class
#Module(includes = [TestingModule.TestingAbstractModule::class])
class TestingModule {
#Provides
fun provideDefaultArgs(testingScreen: TestingScreen): Bundle? = testingScreen.arguments
#Module
abstract class TestingAbstractModule {
#Binds
abstract fun bindTestingScreen(testingScreen: TestingScreen): SavedStateRegistryOwner
}
}
The doubt which i had is in the provideDefaultArgs method i inject the screen which i am currently injecting for, So how this works without the circular dependency error. does anyone have any idea on this ?

Related

Android: Hilt is no using Module, which may be the reason the app is crashing without showing any information

This is the situation:
I'm using Compose, Hilt, Navigation and ViewModel. I'm trying to get an instance of my ViewModel within a Composable Screen via Hilt:
#Composable
fun HomeScreen(
modifier: Modifier = Modifier,
homeViewModel: HomeViewModel = viewModel()
) {
...
}
#HiltViewModel
class HomeViewModel #Inject constructor(
private val updateCaptureUseCase: UpdateCaptureUseCase
) : ViewModel() {
...
}
class UpdateCaptureUseCase #Inject constructor(private val captureRepository: CaptureRepository) {
...
}
I get an instance of CaptureRepository by defining it inside a Module:
#Module
#InstallIn(ViewModelComponent::class)
abstract class CaptureModule {
#Binds
abstract fun bindCaptureLocalDataSource(
captureLocalDataSourceImpl: CaptureLocalDataSourceImpl
): CaptureLocalDataSource
#Binds
abstract fun bindCaptureRepository(
captureRepositoryImpl: CaptureRepositoryImpl
): CaptureRepository
}
The problem is that CaptureModule appears in Android Studio as if it had no usages.
I can build and run the app with no problems, but when it is supposed to show HomeScreen it crashes. What stresses me out and makes it hard to figure out a solution is that there are no errors in the Run tab nor the Logcat.
If I remove updateCaptureUseCase from the constructor of HomeViewModel, then the app works correctly and is able to reach HomeScreen without errors. Since updateCaptureUseCase depends on CaptureRepository and it is being defined in CaptureModule, but this Module shows no usages, I suspect the error comes from Hilt and ViewModel
I think when ViewModel gets initialized hilt checks the dependency graph/tree, and since it has a parameter that also needs a dependency which is the CaptureRepository , hilt also looks for it, but because your'e using #Bind, afaik, those dependencies should also define #Inject annotation.
I was able to reproduce your issue and manage to fix it by, specifying inject to your repository impl
class CaptureRepositoryImpl #Inject constructor(): CaptureRepository
another work around is having your DI module a companion object and define how hilt will provide the dependency without the need to specify #Inject in your repository impl.
#Module
#InstallIn(ViewModelComponent::class)
abstract class CaptureModule {
...
companion object {
#Provides
fun provideHomePresenter(): CaptureRepository {
return CaptureRepositoryImpl()
}
}
}
After many hours I found out a solution: I had to use #AndroidEntryPoint annotation in my Activity.
The problem is that since I'm fairly new with Compose, Hilt and Navigation I had no idea what structure I should use: I wanted to use a single Activity and instead of using Fragments for navigation I desired to use Composables.
Android Docs for Navigation provide examples about the structure I wanted; I had set up everything the same, but the only thing that was missing was that annotation. I though it was not needed since I didn't require to inject dependencies directly into the Activity, but in the end this was the root of the bug, a difficult one because the app crashed without showing a single error

Kotlin Dagger - Inject Fragment values into viewmodel

I need a solution where i could inject Fragment arguments/extras (one Longand one String to be specific) into Fragment's viewmodel and based on these values viewmodel could run it's setup in init {..}
All the ViewModel's injections are fine, dependencies in ViewModel's #Inject construct([dependencies]){..} are provided and working correctly.
My SubComponent looks like this at the moment :
`
#Scope
annotation class MyFragmentScope
#MyFragmentScope
#Subcomponent(modules = [MyFragmentModule::class])
interface MyFragmentSubcomponent : Injector<MyFragment> {
#Subcomponent.Builder
interface Builder : Injector.Factory<MyFragment>
}
#Module
abstract class MyFragmentModule {
#Binds
#IntoMap
#MyFragmentScope
#ViewModelKey(MyFragmentViewModel::class)
abstract fun bindMyFragmentViewModel(
viewModel: MyFragmentViewModel
): ViewModel
}
`
I would be super thankful for any support
Cheers
I tried to create a new ViewModelFactory with additional parameter, but sadly that didn't work out
Tried to use Hilt or Assisted Injection, which also didn't work out since my project is strictly restricted to Dagger v 2.16, if i try to update - tons of bugs arise from this old codebase, it would take months rewriting everything.
Maybe i just did something wrong

Provide all childs of a class with dagger

im creating a highly modular application, i have a lot of clases that need to be injected, all of them are childs (not direct childs) of the same class, none of them have constructor parameters.
I want to avoid having to create a "#Provides" method for each one of them in my module.
Is there a way to tell dagger to automatically provide all the classes that implement a base interface? Or is it possible to do it myself using reflection?
Im using dagger-android with kotlin
Update: Ill post some code to illustrate
In one of the modules i have this interface
interface ExampleClass: BaseExample {
fun doSomething()
}
}
Then in the main app i implement it
class ExampleClassImpl #Inject constructor() : ExampleClass {
override fun doSomething(){
}
}
The class where i need it is a Viewmodel created with dagger so inject works on the constructor.
class ExampleViewModel #Inject constructor(val exmpl :ExampleClass) : BaseViewModel {
}
I want to inject that ExampleClassImpl, to do that i need to create a #module with a method annotated with #Provides or #Bind and return that class.
Without the provider i get an error at compile time:
error: [Dagger/MissingBinding] com.myapp.ExampleClassImpl cannot be provided without an #Provides-annotated method.
You want to inject ExampleClass, but Dagger only knows about ExampleClassImpl. How would Dagger know that you want that specific subclass?
Moreover, you say you have many subclasses that you want to inject. How would Dagger know which one to provide to a constructor expecting the base class?
If you want ExampleViewModel to get an instance of ExampleClassImpl then you can simply change the declaration to:
class ExampleViewModel #Inject constructor(val exmpl :ExampleClassImpl)
Doing so you lose the ability to swap the constructor argument with a different implementation of ExampleClass.
The alternative is to have one #Named #Provides method per subclass. So something like:
// In your module
#Provides
#Named("impl1")
fun provideExampleClassImpl1(impl: ExampleClassImpl): ExampleClass = impl
// When using the named dependency
class ExampleViewModel #Inject constructor(#Named("impl1") val exmpl :ExampleClass)

Dagger Injection for dependent components

I am using Dagger2 and I want to know if it is possible to use the new Android Injector for dependent components? I have seen a few tutorials that use subcomponents and the base App component will just inject everything.
AppComponent
#Singleton
#Component(modules = [AndroidSupportInjectionModule::class])
interface ApplicationComponent {
fun inject(app: App)
#Component.Builder
interface Builder {
fun build(): ApplicationComponent
#BindsInstance
fun app(app: Context): Builder
}
}
QueueComponent
#QueueScope
#Component(dependencies = arrayOf(ApplicationComponent::class), modules = [ScreenBindingModule::class])
interface QueueComponent {
}
ScreenBindingModule
#Module
abstract class ScreenBindingModule {
#ContributesAndroidInjector()
abstract fun queueActivity(): QueueActivity
}
In the onCreate I have added AndroidInjection.inject(this) but the problem is that the app crashes with the exception:
Caused by: java.lang.IllegalArgumentException: No injector factory bound for Class....
No, this will not work without more configuration. As in AndroidInjection.inject:
Application application = activity.getApplication();
if (!(application instanceof HasActivityInjector)) { /* ... */ }
Given an Activity there's no easy way to determine which wider-than-Activity-scoped object should be used to create the Activity's subcomponent, so dagger.android tries to cast the Application to the type HasActivityInjector. Your Application evidently exposes HasActivityInjector to get that error message—likely by marking DaggerApplication or your custom subclass of DaggerApplication in your manifest—but that implementation just returns the DispatchingAndroidInjector<Activity> that searches a multibindings map.
Though #ContributesAndroidInjector automatically installs into that map, each of your Components (ApplicationComponent and QueueComponent) contains a different map, and your DaggerApplication only consults the one in ApplicationComponent.
In order to make this work with component dependencies, you would need to have your Application subclass's activityInjector method return a different AndroidInjector implementation, which creates/fetches a QueueComponent instance and reads the multibound Maps (which would necessarily be exposed on QueueComponent). Unfortunately, there isn't really a way to bind multiple elements into your ApplicationComponent's existing multibound map, as tracked in https://github.com/google/dagger/issues/687, so making this work would involve a custom AndroidInjector implementation at least.
#ContributesAndroidInjector()
abstract fun queueActivity(): QueueActivity
Which module contains this part of code? In QueueComponent You are added ScreenBindingModule::class, but define injector factory in another class - QueeScrennBindingModule::classs.
It's just a typo or it's really two different classes?

What is the use case for #Binds vs #Provides annotation in Dagger2

I am not certain on the purpose for Dagger2's #Bind annotation.
From what i have read online im still not clear but here is an example:
#Module
public abstract class HomeModule {
#Binds
public abstract HomePresenter bindHomePresenter(HomePresenterImp
homePresenterImp);
}
and the class definitions look like this:
public interface HomePresenter {
Observable<List<User>> loadUsers();
}
public class HomePresenterImp implements HomePresenter {
public HomePresenterImp(){
}
#Override
public Observable<List<User>> loadUsers(){
//Return user list observable
}
}
why would i need to use #Binds if i can just use provides annotation as follows:
#Provides
public HomePresenter provideHomePresenter() {
return new HomePresenterImp();
}
what is the usecase for #Binds instead of #Provides ? if i use #Binds do i still need to declare it in my appcomponent (its an abstract class when i use #Binds)?
#Binds can be perfectly equivalent to a #Provides-annotated method like this:
#Provides
public HomePresenter provideHomePresenter() {
return new HomePresenterImp();
}
...though you'd probably prefer a variant that takes HomePresenterImp as a method parameter, which lets Dagger instantiate HomePresenterImp (assuming it has an #Inject constructor) including passing any dependencies it needs. You can also make this static, so Dagger doesn't need to instantiate your Module instance to call it.
#Provides
public static HomePresenter provideHomePresenter(HomePresenterImp presenter) {
return presenter;
}
So why would you choose #Binds instead? Dagger has a FAQ about it, but it boils down do these reasons:
#Binds is (slightly) more compact: You can skip the implementation.
#Binds works in interfaces and abstract classes, which are strictly required for Dagger features like #BindsOptionalOf and #ContributesAndroidInjector.
#Binds helps your code stay efficient. #Provides methods can be instance methods, which require Dagger to instantiate your Module in order to call them. Making your #Provides method static will also accomplish this, but your #Provides method will still compile if you forget the static. #Binds methods will not.
#Binds prevents Dagger from having to codegen and keep a separate Factory/Provider for the object, since Java doesn't give Dagger access to know that the implementation is as simple as it is. In your case, Dagger can cast the Provider<HomePresenterImp> to a Provider<HomePresenter> and only keep one, rather than keeping one for HomePresenter that does nothing but call the one for HomePresenterImp.
Thus, the entire thing would be well-represented as:
#Binds abstract HomePresenter bindHomePresenter(HomePresenterImp presenter);
Thanks to this source: https://www.valueof.io/blog/inject-provides-binds-dependencies-dagger-hilt
#Binds:
single param
less code
However, the advantage of using #Binds is that it reduces the amount of code generated (such as Module Factory classes). Less code to generate means the Kapt plugin has less work to do which can speed up build times in larger projects.
#Binds is a very specialized annotation though—it’s used to map an interface to an implementation. It can take only a single parameter and the type return is the interface implemented by the given parameter object.
If the implementation you are binding to takes constructor parameters, you can use a combination of #Inject and #Binds as shown in the example below:
Here a concrete case where you need Bind annotation, imagine you got a BaseActivityModule which is include in all your activity modules that provides your activity viewmodel.
#Module
object BaseActivityModule {
#Provides
#ActivityScope
#ActivityContext
#JvmStatic
fun provideViewModelProvider(
activity: AppCompatActivity,
viewModelFactory: ViewModelProvider.Factory
): ViewModelProvider = ViewModelProviders.of(activity, viewModelFactory)
}
Here you see we need to provide an AppCompatActivity and a ViewModelProvider.Factory. You cannot provide AppCompatActivity with a Provide annotation since activities are created by android.
We're assuming your concrete ActivityModule for example MainActivityModule will provide MainActivity class either because you create a MainActivity sub component or you used ContributesAndroidInjector to automatically create your sub components (but this is another talk).
So we have our MainActivityModule providing MainActivity and our MainActivityModule includes our BaseActivityModule which need an AppCompatActivity. So here the Bind magic, let's tell Dagger that when you need an AppCompatActivity you can use our MainActivity.
#Module(includes = [BaseActivityModule::class])
abstract class MainActivityModule {
#Binds
#ActivityScope
abstract fun bindActivity(activity: MainActivity): AppCompatActivity
}
You can see more from my project template here
Dagger gives a great explanation in their FAQ page. Apparently, Dagger already saw this question coming :)
link summary below:
#Provides serves three functions:
Declare which type (possibly qualified) is being provided -this is the return type
Declare dependencies — these are the method parameters
Provide an implementation for exactly how the instance is provided — this is the method body
#Binds methods are a drop-in replacement for Provides methods that simply return an injected parameter. #Binds methods are abstracts methods without implementations

Categories

Resources