From Monolithic Android App to Modular Architecture: Lessons and Strategies
This article recounts the evolution of the Class Optimization Master Android project—from a dual‑end, no‑design prototype to an MVP‑based implementation and finally a modularized architecture—highlighting pain points, solutions, and practical steps for improving code boundaries, build times, and reusable modules.
These past years, the Class Optimization Master project grew from zero to a multi‑million‑user platform, evolving its architecture to address business pain points. The following is a retrospective of the practices and lessons learned.
Dual‑End Merge
Before I joined in late 2015, the predecessor "Xiwoxuejia" had separate teacher and parent apps for notifications, assignments, voting, and IM. When the business shifted toward classroom reviews, the decision was made to merge the two ends into a single application.
No‑Design Phase
Starting in late 2015, the new project "Class Optimization Master" began with a small team (2 Android developers) and a simple MVC pattern that extracted network and database logic without much design consideration. Rapid feature addition (classroom, review, broadcast, contacts, IM, sharing, payment, etc.) quickly exposed the drawbacks of this approach, as activities became bloated and role‑specific functionality (teacher vs. parent) led to tangled inheritance hierarchies.
MVP Phase
Introducing MVP addressed two major issues: oversized activities and code reuse. By moving most responsibilities to presenters and defining interfaces, the granularity and boundaries of code improved. However, as the team and product grew, MVP revealed new challenges: reliance on developer discipline for interface contracts, long compile times due to a single massive module, and an urgent need for reusable common features such as payment, sharing, utilities, analytics, and networking.
Key pain points identified:
Code boundaries can be broken;
Compilation time keeps increasing, reducing development efficiency;
Common modules need to be reused across multiple projects.
Modularization Phase
After research, modularization was chosen as a suitable solution to the identified pain points. The following questions needed answers:
How to handle business‑related code in BaseActivity lifecycle?
How can independent modules access application context, BuildConfig constants, and other variables?
How to manage resources?
How to enable inter‑module navigation?
How does the main module communicate with other modules?
How to improve development efficiency?
How to handle code boundaries and dependencies?
How to Solve the Issues
1) BaseActivity business code : Register a lifecycle callback in the Application to handle it.
registerActivityLifecycleCallback(new Application.ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (activity instanceof CareTitleBaseActivity) {
addToActivityList(activity);
if (!mIMInited) {
initIM();
}
}
if (activity instanceof BaseActivity) {
if (savedInstanceState != null && !isNecessaryPermissionsGranted()) {
CareLauncherActivity.goToCareLauncherActivityInNewTask(sApplication);
}
}
}
@Override
public void onActivityStarted(Activity activity) {
if (activity instanceof CareTitleBaseActivity) {
if (PassSession.getInstance().getLoginUser() == null) {
gotoAccountVerifyAndClearActivities();
}
}
}
});2) Accessing context and constants in independent modules : Inject constants during application initialization and use an interface provided by the business module to obtain variable values.
BaseModuleConfig.getInstance().init(sApplication, BuildConfig.DEMO, BuildConfig.DEBUG,
BuildConfig.FLAVOR, BuildConfig.APPLICATION_ID, BuildConfig.VERSION_NAME); DbManager.getInstance().setOnDBInitListener(() -> PassSession.getInstance().getLoginUser());3) Resource management : Place common resources in a shared module, keep module‑specific resources separate, and follow naming conventions to avoid conflicts.
4) Inter‑module navigation : Use a routing mechanism so modules remain decoupled and can be compiled independently.
5) Communication between main and business modules : Choose between an event bus or interface calls based on the scenario.
6) Improving development efficiency :
On‑demand compilation : Decouple heavy modules (e.g., IM, video processing) so they are excluded from builds when not needed, reducing compile time.
"Puppet" app : Create a lightweight app module that depends only on the base module for rapid feature development; once finished, integrate it back into the main project.
7) Handling code boundaries and dependencies : After refactoring, upper layers depend only on lower layers, preventing lower‑layer code from referencing upper‑layer logic.
Approach to Modular Refactoring
The overall strategy consists of three steps:
Base module : Extract business‑related logic from BaseActivity and utilities into a base module, providing the foundation for other modules.
Partial modularization : Prioritize independent or highly reusable modules for early modularization, then gradually refactor remaining modules.
Establish a common module repository : Publish reusable modules to an internal Maven repository, allowing multiple projects to depend on them.
After implementing these steps, the previously identified pain points were largely mitigated, as illustrated below.
Expectations and Reflections
Transitioning from a single project to a multi‑module architecture, and from a design‑less approach to a pain‑point‑driven one, marks a significant step forward, yet many challenges remain. This article serves both as a stage‑wise summary of past work and an invitation for discussion and guidance from experienced engineers.
Architecture has no absolute good or bad; it is only suitable or unsuitable for a given context. Decisions should be based on project specifics and encountered pain points.
The road is long, but perseverance leads to progress.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
