Architecture-components-samples: Pass parameter to viewmodel

Created on 2 Jun 2017  路  14Comments  路  Source: android/architecture-components-samples

Hi, It is actually dagger 2.10 specific question. But I think no problem to ask here.
My question is, what if you want to pass RepoView to your RepoViewModel? In my case I am using ViewModel. I am not using LiveData to observe. So I need to pass RepoView to RepoViewModel as parameter.

@Inject
public RepoViewModel(RepoRepository repository, RepoView repoView)

```java
@Module
public class RepoModule {

@Provides
RepoView provideRepoView(RepoFragment repoFragment){
    return repoFragment;
}

}

```java
@ContributesAndroidInjector(modules = RepoModule.class)
    abstract RepoFragment contributeRepoFragment();

I implemented it in this way but it gives me error compile time. It says that I didn't provide RepoView to inject RepoViewModel.
RepoView can not bi provided without @Provides-annotation method.
Am I missing something? @JoseAlcerreca @yigit

Most helpful comment

Don't pass your view (fragment) to your ViewModel, that's rule number one of ViewModels :) This avoids leaks.

See https://developer.android.com/topic/libraries/architecture/viewmodel.html

Note: Since the ViewModel outlives specific activity and fragment instantiations, it should never reference a View, or any class that may hold a reference to the activity context. If the ViewModel needs the Application context (for example, to find a system service), it can extend the AndroidViewModel class and have a constructor that receives the Application in the constructor (since Application class extends Context).

All 14 comments

Don't pass your view (fragment) to your ViewModel, that's rule number one of ViewModels :) This avoids leaks.

See https://developer.android.com/topic/libraries/architecture/viewmodel.html

Note: Since the ViewModel outlives specific activity and fragment instantiations, it should never reference a View, or any class that may hold a reference to the activity context. If the ViewModel needs the Application context (for example, to find a system service), it can extend the AndroidViewModel class and have a constructor that receives the Application in the constructor (since Application class extends Context).

So what is your suggestion to notify view without LiveData @JoseAlcerreca

LiveData is just an Provider but one thats lifecycle aware so if you dont want to use it you could opt for any framework of your choice that provides streaming of data eg rx but having read the doc of LiveData I would suggest you try it out.

@Nkoroi254 Yes I agree with you. I really like logic behind LiveData and I use it in my projects. But my case is different this time. I am asking about passing a callback to viewmodel using Dagger 2.10+ framework.

I don't think I get you right. But correct me if am wrong but I don't think you really need to be injecting your viewmodel using dagger since we are using viewmodelproviders to pass viewmodels to the view not viewmodel instances and I think you don't intend to pass View since @JoseAlcerreca addressed that.

I think ViewModel is layer with only Observables data, using view function is not valid.

@Nkoroi254 Actually we need dagger framework to inject into our viewmodels. In this project, Dagger injects into viewmodels and viewmodelfactory. In UI layer(fragments in this case), we pass viewmodelfactory (which is injected in fragment) to viewmodelprovider. So viewmodelprovider uses given factory to provide viewmodels.

You should not pass a callback to your ViewModel. If the View is retained, the callback (assuming it references back to the views) will be obsolete.

Instead, your ViewModel can provide observable action events on which your UI can react.

I'm also facing similar issues.
I'm using Clean Architecture where I defined many usecases. How do I pass it to the viewModel.

@Inject
public UserViewModel(GetUserUseCase getUser, UpdateUserUseCase updateUser)
@Module
public class UserModule {

    @Provides
    GetUserUseCase provideGetUserUseCase(Repository repository, Scheduler mUiThread, Scheduler mExecutorThread){
        return new GetUserUseCase (repository, mUiThread, mExecutorThread);
    }
}
@ContributesAndroidInjector(modules = UserModule .class)
    abstract UserFragment contributeUserFragment();

I'm getting compile time error that -> GetUserUseCase , UpdateUserUseCase cannot be provided without an @Provides ...

One workaround is to put all my usecases inside a single UseCaseModule & define it inside the AppComponent. But I don't want to do it.
I have organised the usecases in multiple modules, (eg: UserModule, RepoModule....) I don't want to put my entire usecases inside the AppModule.

How can I solve it?
@JoseAlcerreca @yigit @iammert

I've created Provider that mimics existing ViewModelProviders with Factory that instantiates ViewModel with parameters. Please note @yigit comment about callbacks and leaks

Kotlin https://gist.github.com/NikolaDespotoski/e33ba440cce4dda1a99e35b499e3f6a2

@NikolaDespotoski @yigit in reference to your comment on not passing in callbacks, am I right in assuming that one of the following ways are acceptable for VM -> V communication?

  1. In the RxJava world, subscribe to observables (BehaviorSubjects more specifcially).
    1.1 1 subject emitting different events to multiple subscribers
    1.2 Or multiple different subjects emitting to their own individual subscribers
  2. Purely DataBinding, using change listeners for ObservableFields
  3. Observable Delegates in kotlin

Is there any other mechanism you can think of that might be of use to us?

if you are not using LiveData or Data Binding, you need to make sure that your Activity/Fragment cleans after itself while observing the ViewModel.

So on the RxJava world, make sure that you cancel all subscriptions when the Activity/Fragment is stopped / destroyed (depends on your use case).

Data Binding and LiveData handles leaks automatically so that is not important there. (LiveData even pauses the subscription in onStop).

If you are observing a data binding value manually (e.g. to do something in your activity) then you also need to manage that subscription (ensure it is stopped in onStop otherwise you may receive unwanted callbacks).
Also, post 1.0, Data binding will become LiveData aware so hopefully that integration will be much more smooth.

Am I right in thinking that LiveData is as good a candidate to use as a callback mechanism as all the other thats mentioned?

I initially listed it down and then removed it since I was thinking it was going to be mainly a repository pattern focused component, even though it seems to fit the bill

@gaara87 FYI, you can also use ReactiveStreams to convert your Flowable to LiveData. In this way, you can use RxJava and LiveData both and you can use RxJava in all your usecase, repository, api and persistence layers then convert it to LiveData in your ViewModel when data flow arrives to your ViewModel. When you convert your flowable to livedata, you don't have to worry about disposing your disposables.

public class UserListViewModel extends ViewModel {

    private final LiveData<Resource<List<User>>> userListLiveData;

    @Inject
    public UserListViewModel(@NonNull UserRepository userRepository) {
        userListLiveData = LiveDataReactiveStreams.fromPublisher(userRepository
                .getUserList()
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread()));
    }

    LiveData<Resource<List<User>>> getUserList() {
        return userListLiveData;
    }
}

Also, post 1.0, Data binding will become LiveData aware so hopefully that integration will be much more smooth.

@yigit Before Arch Component ViewModel, I can pass my old ViewModel to my layout and my layout uses ObservableFields over viewmodel. After 1.0 is released, will be able to do same with LiveData? Or are you planning to do it another way?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

amunozg picture amunozg  路  3Comments

prabinshrestha picture prabinshrestha  路  3Comments

roybrener picture roybrener  路  5Comments

vipulyaara picture vipulyaara  路  4Comments

george5613 picture george5613  路  4Comments