Android Componentization: Architecture, Independent Debugging, Routing, and Communication
This article explains how to transform a large Android project into a componentized architecture by modularizing code, configuring Gradle for independent debugging, using ARouter for page navigation and service communication, handling fragment instances, distributing Application lifecycle events, and migrating legacy projects.
Background – As an Android project expands, developers face long compile times, frequent merge conflicts, unclear module responsibilities, and extensive regression testing, indicating the need for componentization.
Understanding modularization – In Android Studio, each module corresponds to a functional unit; splitting the app into multiple modules (e.g., Home, Category, Cart, My, Detail) reduces coupling and improves maintainability.
Componentization advantages – Decoupling modules into independent business components speeds up compilation, improves collaboration, and enables reusable functionality similar to third‑party libraries.
Independent debugging
Two approaches are provided:
Single‑project solution – Dynamically switch a module between com.android.application and com.android.library plugins using a flag in gradle.properties :
# gradle.properties
isModule = false if (isModule.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}Application ID and manifest are also switched based on the flag:
android {
defaultConfig {
if (isModule.toBoolean()) {
applicationId "com.hfy.componentlearning.cart"
}
}
sourceSets {
main {
if (isModule.toBoolean()) {
manifest.srcFile 'src/main/moduleManifest/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}
}Multi‑project solution – Each business component is built as a separate library module (ARR) and published to a Maven repository (private or JitPack). The host "shell" project depends on these ARR packages, keeping code isolation and allowing independent development.
ARouter for page routing
Common component adds the ARouter dependencies:
dependencies {
api 'com.alibaba:arouter-api:1.4.0'
annotationProcessor 'com.alibaba:arouter-compiler:1.2.1'
}Each activity or fragment is annotated with a route path, e.g.:
@Route(path = "/cart/cartActivity")
public class CartActivity extends AppCompatActivity { ... }Navigation is performed via:
ARouter.getInstance()
.build("/cart/cartActivity")
.withString("key1", "value1")
.navigation();Component communication via services
Export modules define service interfaces that extend IProvider . Example service interface:
public interface ICartService extends IProvider {
CartInfo getProductCountInCart();
}Implementation is annotated with a route and provides the logic:
@Route(path = CartRouterTable.PATH_SERVICE_CART)
public class CartServiceImpl implements ICartService {
@Override
public CartInfo getProductCountInCart() {
CartInfo info = new CartInfo();
info.productCount = 666;
return info;
}
@Override
public void init(Context context) { /* optional */ }
}Utility class simplifies usage:
public class CartServiceUtil {
public static void navigateCartPage(String p1, String p2) {
ARouter.getInstance().build(CartRouterTable.PATH_PAGE_CART)
.withString("key1", p1)
.withString("key2", p2)
.navigation();
}
public static ICartService getService() {
return (ICartService) ARouter.getInstance()
.build(CartRouterTable.PATH_SERVICE_CART).navigation();
}
public static CartInfo getCartProductCount() {
return getService().getProductCountInCart();
}
}Home component consumes the service without depending on the Cart module directly.
Fragment instance acquisition
Fragments are also routed via ARouter:
@Route(path = CartRouterTable.PATH_FRAGMENT_CART)
public class CartFragment extends Fragment { ... }Host activity obtains the fragment:
Fragment cartFragment = (Fragment) ARouter.getInstance()
.build(CartRouterTable.PATH_FRAGMENT_CART).navigation();
transaction.add(R.id.fl_test_fragment, cartFragment, "tag");Application lifecycle distribution
The AppLifecycle plugin collects classes annotated with @AppLifecycle that implement IApplicationLifecycleCallbacks . Example:
@AppLifecycle
public class CartApplication implements IApplicationLifecycleCallbacks {
@Override
public int getPriority() { return NORM_PRIORITY; }
@Override
public void onCreate(Context context) { /* init */ }
// other callbacks omitted
}Shell app initializes the manager in Application :
ApplicationLifecycleManager.init();
ApplicationLifecycleManager.onCreate(this);All components receive lifecycle callbacks without the shell needing to know them.
Legacy project migration
The article outlines a step‑by‑step approach: identify base, business‑base, and business components; extract common utilities into a Common module; publish each component as an ARR; handle issues such as ButterKnife R2 usage by applying the ButterKnife Gradle plugin.
Conclusion
Componentization, combined with ARouter and the AppLifecycle plugin, provides a scalable solution for large Android codebases, enabling faster builds, clear module boundaries, independent debugging, and clean inter‑module communication.
Sohu Tech Products
A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.
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.