Android Application Architecture
This article reviews the evolution of Android app architecture over several years, describing the shortcomings of a simple two‑layer design, the adoption of RxJava and MVP patterns, and the resulting benefits and remaining challenges for building maintainable mobile applications.
The Android development ecosystem evolves rapidly, with new tools, libraries, Support Library and Play Services updates appearing weekly, making it essential to continuously adapt architectural approaches.
For over three years the author and the Ribot Team have built Android applications, during which their architecture has matured from a simple two‑layer structure to a more sophisticated, RxJava‑driven design.
Former Architecture
The early codebase (circa 2012) used a basic two‑layer structure: a Data Layer handling REST API and persistence, and a View Layer responsible for displaying data.
APIProvider used URLConnection and AsyncTask to fetch data, while CacheProvider accessed SharedPreferences and SQLite. The View Layer ended up handling many responsibilities, leading to deep callback nesting ("Callback Hell") and making Activities/Fragments large and hard to maintain.
Problems of the old approach
Activities and Fragments became bulky and difficult to maintain.
Excessive nested callbacks produced unreadable code.
Unit testing was challenging because most logic resided in UI components.
RxJava‑driven new architecture
After two years of incremental improvements (adding helper classes, switching APIProvider to Volley), the team adopted RxJava in 2014 to eliminate callback nesting. RxJava allows asynchronous streams, operators for transformation, filtering, and merging of data.
The new design still separates Data Layer and View Layer. The Data Layer now contains a DataManager and several helper classes:
PreferencesHelper – reads/writes SharedPreferences .
DatabaseHelper – handles SQLite operations.
Retrofit services – replace Volley for REST calls and natively support RxJava.
Helper class methods return Observable s. DataManager orchestrates these observables, applying RxJava operators to merge, filter, and transform data, thereby reducing the work required in Activities/Fragments.
Example DataManager method:
public Observable loadTodayPosts() { return mRetrofitService.loadPosts() .concatMap(new Func1 () { @Override public Observable call(List apiPosts) { return mDatabaseHelper.savePosts(apiPosts); } }) .filter(new Func1 () { @Override public Boolean call(Post post) { return isToday(post.date); } }); }
View components simply subscribe to the returned observable, and received posts are displayed in a RecyclerView or similar UI element.
An Event Bus enables Data Layer to broadcast events that any View Layer component can observe, such as logout notifications.
Why this architecture is better
RxJava observables and operators eliminate nested callbacks.
DataManager takes over responsibilities from the View Layer, making Activities/Fragments lightweight.
Moving code to DataManager and helpers simplifies unit testing and encourages test‑friendly design.
Remaining issues
In large, complex projects DataManager can become bloated and hard to maintain.
Although View Layer components are lighter, they still must manage RxJava subscriptions, error handling, etc.
Integrating MVP
To further improve separation of concerns, the team introduced a Presenter layer (or MVVM). The former Data Layer is renamed Model . The Presenter loads data from the Model, subscribes to DataManager observables, handles schedulers and subscriptions, and provides the prepared data to the View.
The View component (mMvpView) implements the MvpView interface, exposing methods such as showError() and showProgressIndicator() , and forwards user interactions to the Presenter.
For a complete MVP example, see the Android Boilerplate project on GitHub and Ribot’s architecture guide.
Benefits of MVP
Activities/Fragments become very lightweight, focusing only on UI updates and event handling.
Mocking the View Layer enables easy unit testing, making the architecture test‑friendly.
If DataManager grows too large, logic can be moved to the Presenter to alleviate the problem.
Remaining challenges with MVP
In very large codebases, a single DataManager can still become a maintenance burden.
In conclusion, while this architecture is not a silver bullet, it provides a solid foundation for building maintainable Android applications. Continuous learning and adaptation remain essential as the Android ecosystem evolves.
Beike Product & Technology
As Beike's official product and technology account, we are committed to building a platform for sharing Beike's product and technology insights, targeting internet/O2O developers and product professionals. We share high-quality original articles, tech salon events, and recruitment information weekly. Welcome to follow us.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.