1
votes

I am trying to learn Android on the side and I have been doing some research and figure the MVVM pattern is the route I want to go. I have some familiarity with it with WPF/XAML. However, I do have a question about how the View (Activity / Fragment) is inflated.

This is my general understanding of the Design Pattern:

The Model sits at the bottom layer. This layer will have the business logic and the Entities. For each declared Entity, a table is stored in the SQLite database using Room. To adhere to encapsulation, all data members are private and only accessible through get() methods.

The Data Access layer stores my Database and a DAO for an Entity. It is my understanding that the DAO is the Object responsible for interacting between the View Model and the Database.

The View Model is how the data is exposed. It will interact with the Repository to ultimately access the database since the database will be referenced in the Repository.

The View Model and the View relationship is where I have trouble. The View Model has no understanding of the View, I understand, but how do Views get inflated? I know this is incorrect - but my thought process is if you use DataBinding to bind, for example, an onClick attribute to a method in the ViewModel and suppose this method is responsible for showing a Dialog, then the ViewModel has View knowledge because it is responsible for creating a Dialog.

So essentially, my question is, how does the developer adhere to the MVVM paradigm when it comes to inflating views? If the ViewModel should not be responsible for any View related interactions, only to have its data exposed, is there another layer that I am missing to bridge the inflation?

1
DataBinding only binds to the name of something on the View Model, not the View Model itself. All it knows is the name of something you intend to provide in the View Model. You can swap out the View Model for a different one and, so long as the new View Model exposes something with the same name, the View will still successfully bind to it.Robert Harvey

1 Answers

1
votes

What I've done is wrap view-presenting logic (dialog, toast, snackbar, etc) in an abstraction, say Presenter (not to be confused with the MVP Presenter). Then the interaction is handled by the ViewModel while the actual display is handled by the View.

For example:

// "Presenter" class, say for a Dialog
public interface DialogPresenter {
    void showDialog();
}

// View Model
public class ViewModel {
    private final DialogPresenter mPresenter;

    public ViewModel(DialogPresenter presenter)  {
        mPresenter = presenter;
    }

    public void onButtonClick() {
        // You can use DataBinding to bind this "onButtonClick()" method to
        // the  button click, then trigger the showing of the dialog. You can
        // add any other non-view related business logic as well. So there is
        // no direct dependencies on Android view classes and this class is
        // unit-testable by mocking out the presenter dependency.
        mPresenter.showDialog();
    }
 }

// Fragment (the "View")
public View onCreateView(...) {
    // View provides an implementation of the abstraction that will actually
    // show the dialog
    mViewModel = FragmentViewModel(new DialogPresenter() {
        public void showDialog() {
            // Show dialog in this fragment
        }
    });
}

This is kinda round-about, but provides you the separation of the view model from the view and makes the viewmodel completely unit-testable. The downside is you need this "middleman", but that's generally the tradeoff in breaking dependencies and making things more testable.

The other option would be to just wire up the button logic directly in the view:

// Fragment (the "View")
public View onCreateView(...) {
    // Just wire everything up here and test via UI test (Espresso)
    mBinding.button.setOnClickListener(new View.OnClickListener() {
        public void onClick() {
            // Show dialog
        }
    }
}

Trade off is that this is obviously more straightforward and direct. You can test this fairly easily with a UI test. Downside is this won't scale if you need to expand the click behavior to add more logic (validation or something). Then you'll start to end up with business logic in your view.

So I'd recommend the first approach for all but the simplest of views.

Hope that helps!