What is difference between in inject vs viewmodel in Android koin - android

I'm using Koin 3.2 which has the new module includes feature. I am want to know what is the difference between the use of by inject() or by viewmodel().
val viewModel by inject<MainActivityViewModel>()
or
val viewModel by viewmodel<MainActivityViewModel>()
in viewmodel. I know both works correctly.
Thanks

The difference is by viewmodel will hook to register the instance to your Lifecycle. Where a simple by inject just create the instance

Related

MVVM: Why the ViewModel is always recreated when Activity rotated

My setup is: java Activity getting a ViewModel using Koin. All good with this, but when I rotate a phone, the ViewModel is always recreated. So how to avoid ViewModel recreation in the case? Thanks.
Activity:
private final FlowViewModel viewModel = get(FlowViewModel.class);
Koin:
val appModule = module {
...
viewModel { FlowViewModel(get()) }
}
You're injecting the viewmodel as a regular object with get() and not as a viewmodel. It does not get any viewmodel special treatment.
To use koin-managed viewmodels in java, you can add the library io.insert-koin:koin-android-compat and access viewmodels in koin graph with getViewModel() rather than get(). See https://insert-koin.io/docs/reference/koin-android/viewmodel#viewmodel-api---java-compat

Android Dagger Hilt: Do we need scope annotations for ViewModels?

in my app I have a MainActivity which requires access to a ViewModel. I am injecting the ViewModel using DaggerHilt and the #ViewModelInject annotation. Additionally, I have two Fragments within the Activity that require access to the same ViewModel in order to pass data to each other using observables.
The problem:
I have found that whenever one of my Fragments go through onDestroy() its ViewModel is killed. This leads me to think that the Activity and Fragments are not sharing the same ViewModel.
My question:
Does anyone know if we are supposed to use scope annotations for ViewModels in Dagger Hilt? I didn't see this stated in the Hilt docs or the android dev tutorials/guides. I had assumed that they were making ViewModels app level singletons, which would make sense.
If we do have to use scope annotations for ViewModels, does anyone know which level is appropriate?
This is my viewmodel code:
class MainActivityViewModel #ViewModelInject constructor(
private val repo: Repo,
private val rxSharedPrefsRepo: RxSharedPrefsRepo,
private val resourcesRepo: ResourcesRepo,
#Assisted private val savedStateHandle: SavedStateHandle
) : ViewModel() {
As per the Scoping in Android and Hilt blog post, using #ViewModelInject means that the objects you pass into the ViewModel are scoped to the ViewModel.
The scope of the ViewModel, however, is based on how you get the ViewModel (what ViewModelStore the ViewModel is associated with) - not anything that Hilt controls. If you use by viewModels() in a Fragment, then the ViewModel is scoped to the Fragment. If you use by activityViewModels() or by navGraphViewModels(), then the ViewModel would be scoped to the activity or navigation graph, respectively.
As mentioned in the blog post, if you want an object that is scoped to the activity and survives configuration changes, you can use Hilt's #ActivityRetainedScoped on any object and inject that object into both fragments.
Whether you should use #ActivityRetainedScoped or a ViewModel where you control the scope separately from Hilt is covered in the blog post:
The advantage of scoping with Hilt is that scoped types are available in the Hilt component hierarchy whereas with ViewModel, you have to manually access the scoped types from the ViewModel.
The advantage of scoping with ViewModel is that you can have ViewModels for any LifecycleOwner objects in your application. For example, if you use the Jetpack Navigation library, you can have a ViewModel attached to your NavGraph.
Hilt provides a limited number of scopes. You might find that you don’t have a scope for your particular use case — for example, when using nested fragments. For that case, you can fall back to scoping using ViewModel.

Why should I use viewmodelproviders for viewmodels?

Why should I use viewmodelproviders for viewmodels?
Why I just can't add custom singleton annotation to my viewmodel, and then inject this viewmodel to fragment class?
Like so:
#MainScope
class MainViewModel #Inject constructor(): ViewModel()
And then:
open class BaseFragment<T: ViewModel>: DaggerFragment() {
#Inject
protected lateinit var viewModel: T
Both cases are independent of screen rotation.
Is there any drawbacks of singleton annotation case?
I see only advantages, with this approach I don't need to copy/paste tons of code.
Why should I use viewmodelproviders for viewmodels?
To get viewModel.onCleared() callback called properly at the right time by the ComponentActivity.
(and to ensure it's created only once for the given ViewModelStoreOwner).
Why I just can't add custom singleton annotation to my viewmodel, and then inject this viewmodel to fragment class?
Because you won't get viewModel.onCleared() callback called properly at the right time by the ComponentActivity.
Is there any drawbacks of singleton annotation case? I see only advantages,
That you don't get viewModel.onCleared().
Also if you have a singleton variant, then the ViewModel won't die along with its enclosing finishing Activity, and stay alive even on back navigation (which is probably not intended).
with this approach I don't need to copy/paste tons of code.
You're using Kotlin. Use extension functions.

ViewModelScope is canceled permanently?

I am trying to use MVVM in my latest Android app. I am also using coroutines. I have ViewModel, that is injected into Activity using koin. To run coroutines in my ViewModel I am using ViewModelScope. Then after Activity is finished, ViewModel is cleared, and I will run this activity again - viewModelScope is canceled since the beginning.
That's very odd. As viewModelScope should be... reseted somehow or something? Or maybe my viewModelScope isn't closing correctly?
It would be helpful to see some code, but one possible thing to consider is - Are you injecting your ViewModel as a singleton with Koin?
single { MyViewModel() }
If so, this is your issue, as Koin is creating a single instance of your viewmodel and using this when you next load your activity. Change your Koin module to use the viewModel injection like so:
viewModel { MyViewModel() }

Understanding Android Architecture Components example GithubBrowserSample: ViewModelModule, ViewModel parameters

One of the most up to date samples covering Android Architecture Components is GithubBrowserSample provided by Google. I reviewed the code and a few questions arose:
I have noticed that ViewModelModule is included in AppModule. It means that all the viewmodels are added to the DI graph. Why that is done in that way instead of having separate Module for each Activity/Fragment that would provide only needed ViewModel for specific Activity/Fragment?
In this specific example, where viewmodels are instantiated using GithubViewModelFactory is there any way to pass a parameter to the specific ViewModel? Or the better solution would be to create a setter in ViewModel and set needed param via setter?
[...] It means that all the viewmodels are added to the DI graph. Why that is done in that way instead of having separate Module for each Activity/Fragment [...]?
They are added to the DI graph, but they are not yet created. Instead they end up in a map of providers, as seen in the ViewModelFacory.
#Inject
public GithubViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) { }
So we now have a GithubViewModelFactory that has a list of providers and can create any ViewModel that was bound. Fragments and Activities can now just inject the factory and retrieve their ViewModel.
#Inject
ViewModelProvider.Factory viewModelFactory;
// ...later...
repoViewModel = ViewModelProviders.of(this, viewModelFactory).get(RepoViewModel.class);
As to the why...alternatively you could create a ViewModelProvider.Factory for every Activity / Fragment and register the implementation in every Module. This would be a lot of duplicated boilerplate code, though.
In this specific example, where viewmodels are instantiated using GithubViewModelFactory is there any way to pass a parameter to the specific ViewModel? Or the better solution would be to create a setter in ViewModel and set needed param via setter?
It seems like all the ViewModels only depend on #Singleton objects—which is necessary, since they all get provided from the AppComponent. This means that there is no way to pass in "parameters" other than other #Singleton dependencies.
So, as you suggested, you'd either have to move the factory down into the Activity / Fragment component so that you can provide lower-scoped dependencies, or use a setter method.

Categories

Resources