So I was working on some sample MVVM project using Dagger. I have a viewmodel factory that goes like this:
class DaggerViewModelFactory #Inject constructor(private val viewModelsMap: Map<Class<out ViewModel>, #JvmSuppressWildcards Provider<ViewModel>>) :
ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
val creator = viewModelsMap[modelClass] ?:
viewModelsMap.asIterable().firstOrNull {
modelClass.isAssignableFrom(it.key)
}?.value ?: throw IllegalArgumentException("unknown model class $modelClass")
return try {
creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
A viewmodel factory module
#Module
abstract class ViewModelFactoryModule {
#Binds
abstract fun bindViewModelFactory(viewModelFactory: DaggerViewModelFactory): ViewModelProvider.Factory
}
I got a ViewModelModule:
#Module
abstract class MyViewModelModule {
#Binds
#IntoMap
#ViewModelKey(TakePicturesViewModel::class)
abstract fun bindTakePictureViewModel(takePicturesViewModel: TakePicturesViewModel): ViewModel
}
A component that goes like this:
#PerActivity
#Subcomponent(modules = [ActivityModule::class, ViewModelFactoryModule::class, MyViewModelModule::class])
interface ActivityComponent {
fun inject(mainActivity: MainActivity)
}
An a viewmodel that goes like this:
class TakePicturesViewModel #Inject constructor(app: Application): AndroidViewModel(app) {...
So I can either inject my viewmodel in my activity using a view model factory like this:
#Inject
lateinit var viewModelFactory: DaggerViewModelFactory
private lateinit var takePicturesViewModel: TakePicturesViewModel
.
.
.
takePicturesViewModel = ViewModelProviders.of(this, viewModelFactory).get(TakePicturesViewModel::class.java)
Or with not viewmodel factory at all, like this:
#Inject
lateinit var takePicturesViewModel: TakePicturesViewModel
Both ways work, so I was wondering which one is the right way to work, if using Dagger allows me to inject a viewmodel without needing a viewmodelfactory, is there a good reason to keep it?, or should I just get rid of this viewmodelfactory?
Thanks in advance for any advice.
Greetings
Both ways work, so I was wondering which one is the right way to work, if using Dagger allows me to inject a viewmodel without needing a viewmodelfactory, is there a good reason to keep it?, or should I just get rid of this viewmodelfactory?
Both ways work differently. Try rotating your screen with stored data in your ViewModel and you'll see.
Dagger can create the ViewModel, which is what you make use of in that generic ViewModelFactory. Those view models should be unscoped, thus you'll be creating a new ViewModel every single time. The Android support library will cache that ViewModel and reuse it after rotation so that you can keep your data—the factory method gets called once and there will only ever be one ViewModel created (per lifecycle). You keep your data and everything behaves as expected.
If on the other hand you use Dagger to inject your ViewModel directly none of the above will apply. Like any other dependency, a new ViewModel will be injected on creation, leading to a ViewModel being created every single time it is used—you'll not only use the data stored in it, you won't be able to share state with fragments either.
Of course you could apply a scope to the ViewModel, but that scope should be longer lived than the Activity instance (to keep state between rotations), but no longer lived than the screen is visible. So you can neither scope it to the activity nor to the application lifecycle. You can get it to work by introducing a new scope in-between, but at this point you'd be reinventing the ViewModel library.
tl;dr Inject and use the factory or you'll get a confusing/wrong ViewModel implementation.
Related
I've been trying to find a way to provide dependencies depending where a fragment is navigated from. I have the following in a library module:
interface FruitProvider {
fun provide(): String
}
class FruitFragment : Fragment() {
#Inject
lateinit var provider: FruitProvider
override fun onAttach(context: Context) {
AndroidSupportInjection.inject(this)
super.onAttach(context)
}
}
The idea is for other modules to consume this library and inject whatever they want as a FruitProvider.
As an example, if I have 2 providers:
class BananaProvider #Inject constructor() : FruitProvider {
override fun provide() = "banana"
}
class PearProvider #Inject constructor() : FruitProvider {
override fun provide() = "pear"
}
If I'm in the banana module I'd like to create and display an instance of FruitFragment injected with BananaProvider. Similarly for the pear module but with a PearProvider.
Background
Right now we have a method in FruitFragment to set the provider, i.e., setFruitProvider(provider: FruitProvider) that is called by the consumers of the library.
This approach is not ideal because if the activity gets recreated this field is null. At least this is what some debugging seems to suggest. Put simply, the fragment is recreated but the setFruitProvider is not being called again since it gets called from methods outside the fragment's lifecycle.
The interface FruitProvider cannot be Serializable so we cannot save it in the instance state. All that we think is left is to inject the fragment since this can be done by the fragment itself when attached.
I am a beginner in Kotlin and trying to implement MVVM design pattern on android development. I have to implement a Recyclerview in a fragment.
How we can set adapter with value to a recyclerview from the viewmodel class since the api call is observed within the viewmodel.
My fragment class is look like as below
class NotesFragment : Fragment() {
lateinit var binding:FragmentNotesBinding
lateinit var viewModel:NoteListViewModel
companion object {
fun newInstance(param1: String): NotesFragment {
val fragment = NotesFragment()
val args = Bundle()
fragment.arguments = args
return fragment
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
binding = DataBindingUtil.inflate(inflater,R.layout.fragment_notes,container,false)
viewModel = NoteListViewModel(binding)
return binding.root
}
is it good practice that we passing the binding object to our viewmodel class and updating the viewModel object again from ViewModel class as below
private fun onSuccess(success: NoteResponse?) {
dataVisibility.value=View.VISIBLE
success.let {
noteAdapter= noteAdapter(documentResponse?.result,mContext)
binding.viewModel=this
}
}
The core about MVVM is seperation of concerns. ViewModel should not hold any reference to the View(Activity/Fragment). LikeWise your Data/Repository layer should not hold ViewModel reference.
So to achieve data flow you can use either Reactive Observables(Rx)/ LiveData from android architecture components to pass back the data.
1) Create MutableLiveData in your Viewmodel.
2) Set the MutableLiveData with api response model.
3) Observe the MutableLiveData in your Fragment for the response data.
4) Use the data to set your adapter inside your fragment.
Please check ViewModel - Developer document to understand better.
Looking at the source code for the Android Architecture Components sample GithubBrowerSample, I don't understand the point of double injecting the githubApp.
Wouldn't the inject method be enough? Why do it need both of them in the same sentence?
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance Builder application(Application application);
AppComponent build();
}
void inject(GithubApp githubApp);
}
And they use it like:
public static void init(GithubApp githubApp) {
DaggerAppComponent.builder().application(githubApp)
.build().inject(githubApp);
As Thomas Broyer described, you've got two separate directions to set up: You want the Dagger graph to know how to get to your Application instance, and you want to get access to certain bindings out of the dependency graph.
The #BindsInstance line in the Builder creates a binding for Application set to the instance you pass in. It sounds like you understand this part.
However, after you've created your Component, presumably you want to use it. Let's say you want to get fully-injected instances of classes Dep1, Dep2, and Dep3 out of your graph. One way you could do this is to create methods on your Component that get the instances:
#Singleton #Component(/* ... */) interface AppComponent {
// [builder snipped out here]
fun getDep1(): Dep1
fun getDep2(): Dep2
fun getDep3(): Dep3
}
And then you call those as part of your App creation.
var appComponent = DaggerAppComponent.builder().application(githubApp).build()
var dep1 = appComponent.getDep1()
var dep2 = appComponent.getDep2()
var dep3 = appComponent.getDep3()
// Use dep1, dep2, and dep3 here.
However, you can also create a single-arg method, which is typically a void method called inject. This populates all of the #Inject-annotated fields and calls all of the #Inject-annotated methods on the instance you pass in. If GitHubApp has #Inject-annotated-fields (and it does), the call to inject lets you skip defining all of the getters on the Component. That reduces all of the above code to:
DaggerAppComponent.builder().application(githubApp)
.build().inject(githubApp)
...which is what you see in the demo.
The #BindsInstance tells Dagger that it should inject the application into whichever #Inject Application it finds in the dependency graph.
The second asks Dagger to inject dependencies into it's #Inject-annotated fields and methods. This the root of the dependency graph.
Technically, the component method can be called as many times as you like, while the builder method can only be called once.
I'm looking for the best way to inject a ViewModel into a Shell view.
I'm using Autofac (but I can adopt code from other IoC containers if sample is available). I have got the other VMs injecting correctly - but the method that resolves the VM using ResoleForPage method of the App class.
I'm fairly new to UWP developement and any help is greatly appreciated!
Passing a ViewModel to the Shell is indeed simpler than passing it to the other pages, because the Shell is the only page that is created explicitly by us: so, it should be enough to add a parameter to the constructor of the Shell of type ShellViewModel:
public Shell()
{
Instance = this;
this.InitializeComponent();
}
public Shell(INavigationService navService, ShellViewModel model) : this()
{
navigationMenu.NavigationService = navService;
navigationMenu.RefreshStyles(App.Current.RequestedTheme, true);
this.DataContext = model;
}
then expose the DataContext in a strongly typed way, as with any other pages (useful mainly if you use x:Bind bindings in xaml):
public ShellViewModel ViewModel => DataContext as ShellViewModel;
And now you just have to pass an instance of your ViewModel class, pulling it from your IoC container, when you create the Shell. In the latest Template 10 template for VS2017, it should be in the CreateRootElement method of the App class:
public override UIElement CreateRootElement(IActivatedEventArgs e)
{
var service = NavigationServiceFactory(BackButton.Attach, ExistingContent.Include);
return new Template10.Controls.ModalDialog
{
DisableBackButtonWhenModal = true,
Content = new Shell(service, new ShellViewModel()),
};
}
of course replacing new ShellViewModel() with the code to pull it from Autofac.
I am looking for some help and I hope that some good soul out there will be able to give me a hint :)
I am building a new application by using MVVM Light. In this application, when a View is created, it instantiates the corresponding ViewModel by using the MEF import.
Here is some code:
public partial class ContractEditorView : Window
{
public ContractEditorView ()
{
InitializeComponent();
CompositionInitializer.SatisfyImports(this);
}
[Import(ViewModelTypes.ContractEditorViewModel)]
public object ViewModel
{
set
{
DataContext = value;
}
}
}
And here is the export for the ViewModel:
[PartCreationPolicy(CreationPolicy.NonShared)]
[Export(ViewModelTypes.ContractEditorViewModel)]
public class ContractEditorViewModel: ViewModelBase
{
public ContractEditorViewModel()
{
_contract = new Models.Contract();
}
}
Now, this works if I want to open a new window in order to create a new contract... or in other words, it is perfect if I don't need to pass the ID of an existing contract.
However let's suppose I want to use the same View in order to edit an existing contract. In this case I would add a new constructor to the same View, which accepts either a model ID or a model object.
"Unfortunately" the ViewModel is created always in the same way:
[Import(ViewModelTypes.ContractEditorViewModel)]
public object ViewModel
{
set
{
DataContext = value;
}
}
As far as I know, this invokes the standard/no-parameters constructor of the corresponding ViewModel at composition-time.
So what I would like to know is how to differentiate this behavior? How can I call a specific constructor during composition time? Or how can I pass some parameters during the Import?
I really apologize if this question sounds silly, but I have only recently started to use MEF!
Thanks in advance,
Cheers,
Gianluca.
You CAN do this. Check out the Messenger implementation in MVVM-Light. You can pass a NotificationMessage(Of Integer) to send the right ID to the view model. The view model has to register for that type of message, and load it when a message is sent.
MEF Imports by default only have a parameterless constructor.