Mobile Development 27 min read

How TheRouter Simplifies Android Modular Routing with APT and ASM

This article explains why a routing framework is essential for Android apps, analyzes TheRouter's source code, and shows how it uses annotation processing and ASM bytecode injection to handle page navigation, parameter injection, and cross‑module method calls in a modular architecture.

Huolala Tech
Huolala Tech
Huolala Tech
How TheRouter Simplifies Android Modular Routing with APT and ASM

Why Use a Routing Framework

Routing is indispensable in modern Android development, especially for enterprise apps, because it decouples the strong dependency on Intent for page navigation and reduces cross‑team coupling.

Modular Development Pain Points

Unreasonable module dependencies lead to high coupling, difficult maintenance, and long compile times.

Cross‑module navigation and access are cumbersome.

Why Choose TheRouter

TheRouter offers many proven advantages; see the official comparison table.

TheRouter is maintained by a dedicated team, used in Huolala products, and receives timely fixes and continuous optimization.

TheRouter Source Code Analysis

We examine how TheRouter solves modular development pain points by focusing on core features such as page navigation, route map generation, and parameter injection.

Page Navigation Implementation

Without a routing framework, navigation between modules requires explicit Intent construction or reflection, which is error‑prone and hard to maintain.

Intent intent = new Intent(MainActivity.this, TestActivity.class);
startActivity(intent);

A more flexible approach stores mappings in a collection:

HashMap<String, String> activityMap = new HashMap<>();
activityMap.put("xx://xxx.xxx.xxx", "com.therouter.demo.shell.TestActivity");
Intent intent3 = new Intent();
intent3.setComponent(new ComponentName(MainActivity.this, activityMap.get("xx://xxx.xxx.xxx")));
startActivity(intent3);

TheRouter simplifies this to two lines:

Add @Route(path = BusinessAPathIndex.INJECT_TEST4) to the target activity.

Call

TheRouter.build(BusinessAPathIndex.INJECT_TEST4).navigation();

The navigation method looks up the URL in ROUTER_MAP, retrieves the RouteItem, creates an Intent with the target class, and starts the activity.

@JvmOverloads
fun navigation(ctx: Context?, fragment: Fragment?, requestCode: Int, ncb: NavigationCallback? = null) {
    // ...
    val match = matchRouteMap(matchUrl)
    if (match != null) {
        // invoke interceptors, build intent, start activity
    } else {
        // callback onLost
    }
}

Route Map Generation via APT

TheRouter uses Annotation Processing (APT) to scan @Route annotations at compile time, creates RouteItem objects, and generates a class that populates ROUTER_MAP with these items.

class TheRouterAnnotationProcessor : AbstractProcessor() {
    override fun process(set: Set<TypeElement>?, roundEnv: RoundEnvironment): Boolean {
        val routeList = parseRoute(roundEnv)
        genRouterMapFile(routeList)
        return true
    }
}

The generated class contains a static addRoute() method that registers each route:

public static void addRoute() {
    RouteItem item1 = new RouteItem("http://kymjs.com/business_a/testinject", "com.therouter.demo.shell.TestInjectActivity", "", "");
    RouteMapKt.addRouteItem(item1);
    // ...
}

Injecting the Route Map with ASM

During the build, TheRouter's Gradle plugin applies the therouter plugin, which runs TheRouterTransform. This transform scans all class files, finds the generated RouterMap__TheRouter__* classes, and inserts calls to addRoute() into the empty initDefaultRouteMap() method of TheRouterServiceProvideInjecter using ASM bytecode injection.

public final class TheRouterServiceProvideInjecter {
    public static final void initDefaultRouteMap() {
        try { RouterMap__TheRouter__2107448941.addRoute(); } catch (Exception e) { e.printStackTrace(); }
        try { RouterMap__TheRouter__546452950.addRoute(); } catch (Exception e2) { e2.printStackTrace(); }
    }
}

Parameter Passing (Autowired)

TheRouter provides @Autowired to inject fields automatically. The APT processor collects @Autowired fields, creates AutowiredItem objects, and generates a class with an autowiredInject method that uses parsers to set field values from the intent or fragment arguments.

public static void autowiredInject(MainActivity target) {
    for (AutowiredParser parser : TheRouter.getParserList()) {
        Integer intValue = parser.parse("int", target, new AutowiredItem("int", "intValue", 0, "", "com.therouter.demo.shell.MainActivity", "intValue", false, ""));
        if (intValue != null) target.intValue = intValue;
        // ... other fields
    }
}

The injection is triggered by TheRouter.inject(this), which internally calls the generated autowiredInject method.

Cross‑Module Method Calls via ServiceProvider

To call methods across modules without direct dependencies, developers define an interface in a base module, implement it in a feature module, and annotate a static provider method with @ServiceProvider. APT generates a ServiceProvider__TheRouter__* interceptor that returns the implementation when TheRouter.get(IUserService.class) is called.

@ServiceProvider
public static IUserService test() {
    return new IUserService() {
        @Override
        public String getUserInfo() { return "这是用户信息"; }
    };
}

The generated interceptor implements com.therouter.inject.Interceptor and performs a type check to return the correct service instance.

public <T> T interception(Class<T> clazz, Object... params) {
    if (IUserService.class.equals(clazz) && params.length == 0) {
        IUserService impl = Test.test();
        return (T) impl;
    }
    return null;
}

During initialization, ASM injects calls to privateAddInterceptor(new ServiceProvider__TheRouter__...()) into the empty trojan() method of TheRouterServiceProvideInjecter, registering the interceptor in the router's interceptor list.

public static final void trojan() {
    try { TheRouter.getRouterInject().privateAddInterceptor(new ServiceProvider__TheRouter__526662154()); } catch (Exception e) { e.printStackTrace(); }
    // ... other providers
}

Summary

TheRouter combines annotation processing (APT/KSP) and ASM bytecode injection to generate route maps, handle navigation, inject parameters with @Autowired, and provide cross‑module service access via @ServiceProvider. This architecture decouples modules, simplifies page jumps, and reduces boilerplate in large Android projects.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

modularizationAndroidDependencyInjectionASMAPTTheRouter
Huolala Tech
Written by

Huolala Tech

Technology reshapes logistics

0 followers
Reader feedback

How this landed with the community

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.