The bundle Moxy MVP + Cicerone Navigation

Since my first introduction to Android fragments and Single Activity architecture usage in applications, I had to write a navigation manager manually. Sometimes it was uncomfortable and problematic to save configuration parameter statuses changes in an activity. I've been studying something new permanently until I built the necessary knowledge base, but it took much time to write navigation for the specific case anyway...
About a year ago my colleagues advised me on two good libraries which were intended to "make my life easier". I looked and was skeptical at first, because I was able to write navigation by myself,

Moreover, I was acknowledged with MVVM. However, a new project has been started, and I was delegated the task to test the Moxy + Cicerone bundle.

In this article, I would like to highlight the result, what I did like and some cases of uncomfortable bundle usage.
A bit about Moxy

Moxy is Android MVP pattern's realization. MVP is the way to delegate response in applications. Let's don't get deep into theory about the ways of MVP components interaction, furthermore, the vast majority of developers already know it. The point is that in Android, on any configuration parameter (localization, orientation and screen size, font size, etc.) changing, it's getting complicated by recreating Activity, and if this changing happens during an app working, an activity status is needed to be saved. There are inner ways to save status with the onSaveInstanceState and onRestoreInstanceState methods redefining but frequently this way affects on application architecture. Moxy developers have done their best to help us stop thinking about Activity or Fragment statuses saving. Here's a scheme:
Picture 1 - Moxy realizaion scheme
We see that it's not ordinary MVP as there's a fourth component: ViewState. It's in charge of View status saving after recreation. Also, ViewState allows to one Presenter serve many Views at the same time or by turn. As scheme shows, new View which connected the Presenter later than the first one got the same status as the first View. Sometimes it's quite handy.

At first, this scheme seems difficult. It's clear on practice. Just look at the code:
As we can see, Moxy completely follows the initial MVP pattern idea. By the correct duties allocation between View and Presenter (and correct Moxy Strategy setting as well, which about you can read in another article - the link is below), you don't need to redefine onSaveInstanceState and onRestoreInstanceState at all as all the data is saved in Presenter directly.
A few words about Cicerone

The name isn't related to ancient Roman politician. It's rather related to the old Italian word "ci-ce-ro-ne" with the meaning of "guide".

From the pattern side, navigation is business logic. But if you need to open Activity in Android, you need Context, and transferring it to the entity of business logic (presenter, viewModel, etc.) is an anti-pattern. And if you use Single Activity architecture, it needs to consider the container lifecycle to switch fragments (a reference to java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState of fragments).

The Cicerone solution resolves these problems completely. Let's look at the structure to understand the process:

Picture 2 - Navigation realization using Cicerone
Navigator - direct screens switching;

Command - the switching command which is Navigator performs;

CommandBuffer - manager which performs commands delivery to Navigator, saving as well if Navigator cannot perform them at the input moment;

NavigatorHolder - mediator between Navigation and CommandBuffer (no info at the scheme);

Router - object which generates low-level commands for the Navigator by the call of high-level methods for navigation;

Screen - high-level specific screen realization.


Initially, Cicerone has four basic transfer commands:

  • Back() - deletes the current screen from the stack and make the previous to be active;
  • BackTo(Screen) - returns to the specified screen if it's in the chain, and delete all the screens that were up front. If the specified screen not found in stack or there's a null transfer instead of screen transfer, the transfer performs to the root screen;
  • Forward(Screen) - adds a screen to the stack and make it to be active;
  • Replace(Screen) - replaces active screen to the specified.

From these commands in the Router class six basic high-level commands were formed:

  1. navigateTo(Screen) - transfer to a new screen;
  2. newScreenChain(Screen) - stack reset to the root screen and new screen opening;
  3. newRootScreen(Screen) - stack reset and root screen replacement;
  4. replaceScreen(Screen) - active screen replacement;
  5. backTo(Screen) - returning to any screen in stack;
  6. exit() - active screen exit

Both these lists can be extended for the developers' needs. In the first case, you can realize the Command interface, in the second - extend Router class.

As for transition animations adding, it needs to decide whether to realize Navigation interface or redefine in the basic SupportAppNavigator exemplar the setupFragmentTransaction method which has FragmentTransaction class exemplar as a parameter. Right this method requires to add Transition or Animation.

Have a look at the Cicerone usage example in a bundle with Moxy:
In my case I used Dagger and I didn't make Cicerone, Router, and NavigatorHolder companion as it's suggested in the Cicerone tutorial.

As we can see, Cicerone abstracts from Fragment and Activity using SupportAppScreen class. By the way, in SupportAppScreen you can transfer not only Fragment, but also Activity, ScreenKey, and parameters which can be transferred by Bundle. Our team used to create the companion method newInstance(params) which returns fragment exemplar or Intent for Activity and fills the Bundle with parameters, thus, I didn't use parameters transition by Cicerone.
Conclusion
This bundle did its best, especially with Dagger. But it's not well enough.

I couldn't extend the Presenter from the parent fragment between subsidiary fragments which were in ViewPager by Moxy. As Dagger can't implement private fields, so all the fields which are labeled with annotations @Inject or @InjectPresenter should be public. The only quick rebound I've found is to address the parentFragment, lead it to the necessary type and address the Presenter. Also, I've read about Dagger but I hadn't enough time to sort it out.

I couldn't make two or more different screen stacks with the ability to switch between them. Therefore, I had to write the whole navigation in another project. There're some issues as well if you need to make some FragmentContainers and you have to create some Navigators to switch between them.

I don't exclude the fact that I seek information badly or I want much. Anyway, such specific requirements are so rare, thus, in the vast majority of cases these libraries pay off completely.


Thanks for reading!