Introduction (with links to all articles)In the water article I already wrote that the planned client for the project should be the Android client: accessible to a large audience, light, functional, beautiful, fast (not an app, but a dream!). If everything is clear with the grounds for choosing a platform, then how to implement all the listed requirements on the basis of it - it was clear that not everything was clear.
Previously, the development of Android was not engaged, therefore, quite valuable sources of information for me were:
')
After studying the specified sources of questions with architecture of Android and interaction of their components did not remain. However, one important question remains: what will be the structure of the application itself? A couple of examples and prototypes showed that with the growth of the functional, everything quickly began to turn into a "noodle":
- The logic of working with Android objects (Activity, Preferences, TextView ....) Was mixed with business logic;
- Storage objects figured in the interface building code;
- Unit testing turned into hell because of the need to work with native Android objects and replace them with Robolectric instances;
- Verification of the asynchronous code was possible only on the device or the emulator (on the principle: “launched-checked-repeated”).
It became clear that you need to take a step back and look around: Android is not the first year, there are people who have been developing code for a long time under this platform, there are large developing projects - respectively, there is where to get information about well-established practices.
The main criteria for finding a good architecture for an Android application were:
- easy testability of the developed code and its components - easily tested code is easy to develop and change without fear of creating a bug or “dumping” the application;
- weak connectivity of components, in which parts of an application / components can be developed by different developers without the need for super-intensive interaction (at least for a while).
The search led me to an interesting video on YouTube:
“We are writing a test code” (recording a speech by Evgeny Matsyuk (a) from the Mobius mobile development conference) (there was A LOT OF IT!), Which described what I needed. For implementation, it was necessary to explore some additional resources and tools:
The development of a prototype with these practices together with the study of RxJava took a lot of time, but after some time the first prototype was ready. A distinctive feature of it was the terrible number of created interfaces and classes when adding new screens: 3 interfaces and 3 classes (Activity / Fragment and its interface, Presenter and its interface, Interactor and its interface) - a classic example of overengineering. Formally, nothing has changed to the current moment, but I suppose this is the other side of the benefits. But on the output we get an easily tested application with a weakly related structure.
Implementation
I will cite the components of Clean Architecture for refreshment in my memory from the
article on the Habr “Delusion of Clean Architecture” .

Each Android component and element of the selected architecture is presented in the following table:
Class | Level | Implemented Interfaces | Purpose |
Implement Activity / Fragment (XXXX_Activity / XXXX_Fragment) | Ui | I_XXXX_View | Actual implementation of the action with Android elements: changing properties, getting callbacks, starting services, working with the Android API |
XXXX_PresenterImpl | Ui | I_XXXX_Presenter | View Level Coordination, View Logic - I_XXXX_View, I_XXXX_Interactor Interface Method Calls |
XXXX_InteractorImpl | Business / Use Cases | I_XXXX_Interactor | Implementing the core logic of the application, calling the methods of the I_XXXX_Repository interfaces |
XXXX_RepositoryImpl | Data / Repository | I_XXXX_Repository | Realization of direct interaction with data sources, external API, Android network and database, ContentProviders, etc. |
Organization of interaction
The interaction of components and data transfer is organized taking into account the fact that a user of any Android application receives more data than it enters. Respectively:
- signaling to deeper layers goes through normal synchronous calls (press the / scroll / enter data button -> call the method);
- receiving data from the lower layers is organized through asynchronous Rx-streams (received a call -> sent data with the results);
- synchronized data retrieval is minimized (most in initialization code and in other auxiliary and rare screens).
Package Organization
In the original article, Fernando Cejas proposed 2 options for organizing “by levels” and “by functionality”; I developed a combined approach for myself:
- First by level (ui, data, business)
- In “ui” on the main screens “news_watcher”, “news_tape”, etc.
- In "data" and "business" - on the main entities "news_header", "news_article", etc.
An interesting feature was that the number of Interactors became equal to the “number of main screens” + “number of entities”: there are often situations when you need to organize clever data acquisition (for example, with a combination from different sources) and copy this code into every interactor where he needed was completely reluctant. In this case, taking into account the fact that Interactor is used in a single copy - they can store a certain state important for the execution of the method, I implemented it as follows: Interactors of screens, turn to Interactors of entities for corresponding methods (which leads to the appearance of delegating methods in Interactor screens
Initialization
- Activity / Fragment:
- Android is being created (non-singletone, w / scope)
- initialized in View methods # onCreate () (with completion in Fragment # onViewCreated () for Fragment)
- Presenter inside is assigned to the c / o Dagger2
- Presenter initialization inside is performed in the specified methods (View # onCreate (), with completion in Fragment # onViewCreated () for Fragment)
- Presenter:
- created via Dagger2 (non-singletone, w / scope)
- View is assigned by View itself, via Presenter # bindView ()
- initialized in the method Presenter # initializePresenter (), called View (because initialization must be done at the right moment, after initializing View)
- Interactor inside is assigned / initialized by c / s Dagger2
- Interactor-> Presenter communication is created in the Presenter # initializePresenter () method (another Interactor method for Rx initialization)
- Interactor:
- created through Dagger2 (singletone, w / o scope)
- initialized through Dagger2 (Interactor # initializeInteractor)
- The repository is assigned / initialized internally by the Dagger2.
- Repository:
- created through Dagger2 (singletone, w / o scope)
- initialized through Dagger2 (Repository # initializeRepository)
Testing Approaches
In terms of testing, nothing revolutionary:
- UI level - JUnit + Mockito + Robolectric
- Business Level - JUnit + Mockito
- Data Level - JUnit + Mockito
Thanks for attention!