Mobile Development 32 min read

Android Componentization: Architecture, Implementation, and Best Practices

This article explains why large Android projects need componentization, describes the concepts of modularization and componentization, presents a detailed component architecture, and provides step‑by‑step solutions for independent debugging, routing, inter‑component communication, fragment retrieval, application lifecycle distribution, and migration of legacy projects, complete with Gradle and ARouter code examples.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Android Componentization: Architecture, Implementation, and Best Practices

1. Background

As a project grows, compilation becomes slow, merge conflicts increase, and responsibilities become unclear, indicating the need for componentization.

2. Understanding Componentization

2.1 Modularization

In Android Studio, a module corresponds to a logical feature; splitting the app into multiple modules reduces coupling but still leaves high inter‑module dependencies such as page navigation, data passing, and method calls.

Componentization solves these high‑coupling problems by turning each business module into an independent business component .

2.2 Componentization Advantages & Architecture

Benefits include faster compilation, improved collaboration, and reusable functionality. The architecture consists of a shell project , common component , basic component , business base component , and business component . The shell integrates all components, while business components have no direct dependencies on each other.

3. Independent Debugging of Components

Two schemes are provided:

Single‑project scheme : All modules reside in one Gradle project; switch a component between com.android.application and com.android.library plugins using a flag in gradle.properties ( isModule ).

Multi‑project scheme : Each business component is a separate Gradle project (library module) that can be built and run independently.

Example of the flag configuration:

//gradle.properties
# Component independent debugging switch
isModule = false

Build‑script snippet to apply the appropriate plugin:

if (isModule.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

Dynamic configuration of applicationId and AndroidManifest.xml is also handled based on the flag.

4. Page Routing

ARouter is used for decoupled navigation. Dependencies are added in the common component:

dependencies {
    api 'com.alibaba:arouter-api:1.4.0'
    annotationProcessor 'com.alibaba:arouter-compiler:1.2.1'
}

Example of a component activity annotated for routing:

@Route(path = "/cart/cartActivity")
public class CartActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cart);
    }
}

Navigation from another component:

ARouter.getInstance()
        .build("/cart/cartActivity")
        .withString("key1", "value1")
        .withString("key2", "value2")
        .navigation();

5. Component Communication via Services

Expose services through an export_cart module. Define an interface extending IProvider :

public interface ICartService extends IProvider {
    CartInfo getProductCountInCart();
}

Implement the service and annotate it with a route:

@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 for other components:

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();
    }
}

6. Fragment Retrieval

Fragments are also obtained via ARouter:

@Route(path = CartRouterTable.PATH_FRAGMENT_CART)
public class CartFragment extends Fragment { /* ... */ }

In another component:

Fragment cartFragment = (Fragment) ARouter.getInstance()
        .build(CartRouterTable.PATH_FRAGMENT_CART)
        .navigation();
transaction.add(R.id.container, cartFragment, "tag");
transaction.commit();

7. Application Lifecycle Distribution

The AppLifecycle plugin distributes Application callbacks to components that implement IApplicationLifecycleCallbacks and are annotated with @AppLifecycle . Example component:

@AppLifecycle
public class CartApplication implements IApplicationLifecycleCallbacks {
    @Override
    public int getPriority() { return NORM_PRIORITY; }
    @Override
    public void onCreate(Context context) {
        Log.i("CartApplication", "onCreate");
    }
    // other callbacks omitted for brevity
}

Shell Application triggers the manager:

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        ApplicationLifecycleManager.init();
        ApplicationLifecycleManager.onCreate(this);
    }
    @Override
    public void onTerminate() { ApplicationLifecycleManager.onTerminate(); }
    @Override
    public void onLowMemory() { ApplicationLifecycleManager.onLowMemory(); }
    @Override
    public void onTrimMemory(int level) { ApplicationLifecycleManager.onTrimMemory(level); }
}

8. Migrating Legacy Projects

Steps include identifying base, business‑base, and business components; extracting reusable libraries into a Common module; publishing components as AARs; and gradually refactoring code to depend on export_xxx modules for navigation and services.

Common Issues

ButterKnife in library modules requires the ButterKnife Gradle plugin and the use of R2 instead of R :

buildscript {
    dependencies {
        classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.3'
    }
}
apply plugin: 'com.jakewharton.butterknife'

After applying the plugin, replace @BindView(R.id.xxx) with @BindView(R2.id.xxx) .

9. Conclusion

The article covered the motivation, architecture, and practical solutions for Android componentization, including independent debugging, routing, service communication, fragment access, application lifecycle handling, and legacy migration, with concrete Gradle and ARouter code snippets.

Demo Links

Shell project: https://github.com/hufeiyang/ComponentLearning

AppLifecycle plugin: https://github.com/hufeiyang/Android-AppLifecycleMgr

ModularizationandroidGradleMavencomponentizationARouter
Sohu Tech Products
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.