In my previous article I gave a simple solution for retaining Presenter on configuration change. Let’s continue with Model-View-Presenter topic. Today I want to discuss Navigation in the context of MVP. If you are not familiar with Model-View-Presenter pattern please google for it, there are lots of very good articles about it and how it can help you make your code more decoupled and more testable.

The main problem with Navigation: it is not clear who is responsible for replacing current Fragment or opening new Activity. There is a tendency to put navigation methods into the View. But responsibility of View are limited to displaying data and providing user events to the Presenter. Knowledge of the application and screen structure is a completely different topic. Presenter can’t do that either, since it doesn’t know anything about Android and Context in particular. But who should take care of moving to the different screen?

Navigator.

We need another guy who will hide all navigation logic from View and Presenter. This will allow us to make screens more independent from their surroundings and to combine them in a flexible way, of course, this is mostly about the case when our View is either Fragment or Android View, but even with Activity we can benefit from having a better separation of concerns.

In the sample project I made, user can go from a list of articles to the one particular article or to the list of his favorite articles. Depending on the case next screen can be opened in a different way:

New screen. In the first example of the sample project I’m opening another Activity, but same logic can be applied for replacing Fragments. Master-Detail Pattern. This could be useful in the case of tablet layout. ViewPager. Some times instead of opening new screen we want to slide to another tab.

Applying this approach on the sample project results in Contracts like this one:

public interface ArticleListContract {

interface Presenter extends core.Presenter<View> {

void setNavigator(Navigator navigator);

void onFavoriteArticleClick();

}



interface View extends core.Presenter.View {

void displayArticles(List<Article> articles);

}



interface Navigator {

void openArticle(int id);

void openFavoriteArticles();

}

}

Unfortunately we have a small problem now. Somebody should create a Navigator for our Fragment. There are two ways of solving this problem, either Fragment should know about some kind of NavigatorProvider, that can create a Navigator for him, or dependency injection. I highly recommend second approach, it will keep you code simpler and Contract smaller.

But be aware, using dependency injection with Presenters that can survive configuration change is not that straight forward. Presenter lives longer than Activity, so don’t forget to nullify Navigator in onDetachView() and then set it again before onAttachView(). If you are using my PresenterRetainer library, there is a special hook method onPresenterRestored(), where you can do that.

Nested Fragments.

Approach described above is pretty straight forward for an Activity or a single Fragment. But sometimes if we want to use nested Fragments, then it becomes more complex.

We will need to have two Navigators: parent and local ones. Local Navigator will be responsible for working with child Fragments, in one hand, and delegating to the parent Navigator in case we need to replace current Fragment or open a new Activity. From Presenter perspective nothing changes, it still works with a single Navigator. Only difference is that now we will have to create two navigators instead of one. I strongly recommend to use dependency injection for providing Navigator to the Presenter, otherwise you could end up with very big Screen Contract that is no longer readable.

Conclusion.

Usage of a Navigator makes code more decoupled and allows to combine same screen components in different ways. Injection can help us to keep our code simpler and cleaner, while keeping Contract smaller.

To illustrate all of the above I made a sample project. It contains five examples, each of them shows advantages of different approach. First three examples shares same Fragments, while applying different navigation patterns: New Screen, Master-Detail, ViewPager. Forth example illustrates how dependency injection makes code simpler and more decoupled. Fifth example shows how to work with Navigator in case of Presenters that retains on configuration change.

I’m really interesting in your opinion about this approach and in ways you are doing navigation in your own apps. Don’t forget to check the sample project on GitHub.