# WMRouter: the page routing and service loading framework behind four Meituan apps

> An Apache licensed Android routing framework from Meituan that turns page jumps into URI matches, with interceptors, degradation policies and a ServiceLoader for cross module calls.

**meituan/WMRouter** — WMRouter is an Android routing framework based on a component-oriented design, with flexible functionality and ease of use.

- Repository: https://github.com/meituan/WMRouter
- Website: https://tech.meituan.com/meituan_waimai_android_open_source_routing_framework.html
- Stars: 2,369 · Forks: 343
- Language: Java
- License: Apache-2.0
- Published: 2026-10-07 · Updated: 2026-10-07 · Language: en
- Canonical page: https://hysenlabs.com/projects/meituan-wmrouter

## Turning page jumps into URI matches

WMRouter's core idea is that a page jump should be a URI rather than a direct reference to an Activity class. A destination registers a URI, and navigation code asks for a URI. That inversion is what makes remote configuration possible: a server can push a link and the client resolves it without a new release.

The matching rules are the substance of the framework. It supports multiple schemes, hosts and paths, and it supports regular expression matching for URIs, so a pattern rather than an exact string can route a whole family of links. Page configuration can be registered dynamically from Java code or automatically through annotations, which is the path that keeps configuration next to the page it describes.

Around matching sits a set of per page options that are usually the reason a team adopts a router at all. Global and local interceptors can run before a jump and do synchronous or asynchronous work, which is how a login check or a location lookup happens once in code instead of in every Activity that needs it. A single jump can also carry Intent extras, Intent flags, a transition animation, or a completely custom start operation for the cases where `startActivity` is not what you want. Page exported control keeps specific screens from being reachable from outside the app.

## Degradation policies and analytics hooks on every navigation

Two features stand out because they address production pain rather than developer convenience. The first is degradation, configurable globally and per page. When a jump fails, because a server is down, because a client crash left the target in a bad state, or for any other reason the framework can tell you about, it can open a fallback page instead. The README frames this as protecting critical features and giving users a usable message rather than a dead end, which is the correct goal for a navigation layer that sits underneath everything.

The second is the listener, which can be registered for a single jump or globally. The listed use case is analytics: page navigation is one of the most common business actions in an app, and if every jump goes through one framework, instrumentation can be attached in one place through a global completion listener instead of being sprinkled across call sites. The README also suggests monitoring and reporting failed jumps, so that the same hook that counts successes can surface the ones that break in production.

There is also a specific mechanism for A/B tests and dynamic configuration. A custom interceptor can return a `UriResult` of 301 for a matching URI, which redirects the navigation, so a remotely delivered set of routes can point the same URI at a different group of pages. Dynamic configuration and experimentation are treated as routing concerns here, rather than as something bolted on downstream.

## ServiceLoader for calling code across modules by interface

The second module is separate from routing but shipped in the same framework, and it exists for a different problem: module to module calls inside one app. WMRouter's ServiceLoader is modelled on Java's `java.util.ServiceLoader`, following the same SPI idea that JDBC drivers and other pluggable systems use, but the README says it is more capable than the JDK version.

The capabilities listed are: automatic configuration through annotations, retrieving all implementations of an interface or one specific implementation by key, retrieving a Class object or a live instance, construction through a no argument constructor, a Context constructor, or a custom Factory or Provider, singleton management, and method invocation. The practical effect is decoupling. A module declares an interface, another module provides an implementation, and neither needs a compile time dependency on the other, which is what makes a large multi module codebase reorganizable.

The README ties this to the same underlying goal as the router: componentization. A componentized Android build with dozens of modules and some shared code, similar in shape to the food delivery app's own structure, hits a specific set of problems around code reuse, dependency injection and compilation. URI dispatch and ServiceLoader are both offered as answers to that, and the optional adapter directories in the repository, including `adapter-rxjava3` and `adapter-kotlin-coroutines`, suggest the return types can be adapted to whatever concurrency model your modules already use.

## A compile time processor that the README claims costs little

Annotation based routing only pays off if the annotation processor is fast, because it runs on every build. The README's claim is specific: an optimized Gradle plugin with a small effect on compilation time. It also lists compile time and runtime configuration checks that catch conflicting or incorrect configuration, plus debugging support for finding problems early.

The repository layout backs up the compilation story. There is a `compiler/` directory for the annotation processor, a `WmPlugin/` directory for the Gradle plugin, and `interfaces/` for the shared interfaces that ServiceLoader implementations are looked up against. Publishing is split into its own `publish` module and there is a `gradle_mvn_push.gradle` script, so the artifacts are meant to be consumed as Maven coordinates from multiple modules rather than as source.

Demonstration code is separated the way you would expect from a framework that has to prove cross module behaviour: `demoapp/`, plus `demokotlin/` as a Kotlin consumer, and `demolib1/` and `demolib2/` as two separate libraries that presumably talk to each other through the framework rather than directly. `debug` is a separate directory for its debugging support, and `router-result-adapter/` handles adapting jump results.

## Five years of no releases, but commits in 2026

Here is the contradiction worth naming plainly, because it changes how you should evaluate the project. The repository badge advertises release 1.2.1, and the release history confirms it: v1.2.0 shipped on 2019-03-30 and v1.2.1 on 2021-06-07, five years before the latest push recorded in the repository metadata, which is 2026-04-02. The v1.2.1 release also has an empty body while v1.2.0 has real notes, and that older entry describes adding `FragmentTransactionUriRequest` support, changing the annotation processor so `@RouterPage` annotated Fragments get a `FragmentTransactionHandler` automatically, and fixing URI dispatch path mismatches.

So both facts are true at once: there has been no tagged release since mid 2021, and the repository has been pushed to as recently as April 2026. A reader should treat the code as maintained and the published Maven coordinates as stale. If you depend on this from a Gradle build, check whether a newer coordinate than 1.2.1 is actually being resolved, because the releases page will not tell you.

Two smaller mismatches point the same way. The project is described in English as a Java framework and the metadata reports Java, yet the tree contains Kotlin and RxJava adapters and a Kotlin demo, so expect to read Kotlin if you use those paths. The badge links in the README point at a `master` branch while the project is normally consumed from `main`, which is a small sign the README has not been touched in a while. There is a separate CHANGELOG.md, so the change history may be more current than the releases page.

## The migration note that will bite you first

One line in the README should be read before anything else. Because JCenter went offline, the Maven group changed starting from version 1.2.1, moving from `com.sankuai.waimai.router` to `io.github.meituan-dianping`. Any dependency declaration still using the old coordinates will not resolve, and the fix is a group name change rather than anything to do with the API.

The framework also carries a licence worth noting for an Android developer: Apache 2.0, which is permissive and includes an explicit patent grant. The metadata and the badge both agree on that point, so there is no ambiguity about the terms.

What the README does not do is show you code. The full design and usage documentation lives in `docs/user-manual.md`, and the repository has a `docs/` directory to hold it. This is a Chinese language document set, which is the main practical hurdle for adoption outside Meituan's own teams: the README is Chinese, the manual is Chinese, and the Meituan engineering blog post linked as background is also Chinese. The demo modules are the fastest way to see working configuration if you do not read Chinese, since they are compiled code you can open in an IDE.

## Conclusion

WMRouter solves a problem that only appears once an Android app has many modules, many deep link entry points and analytics on every screen transition. If you are building one app with ten screens, plain Intent extras are the better answer. If you are building the fourth generation of a large app and want to route every navigation through a URI so links can be pushed remotely, the interceptors, fallback policies and compile time checks are the reason to adopt it rather than write your own table. Start from docs/user-manual.md, register pages with the annotation processor, and decide early whether you want the ServiceLoader module as well.

## FAQ

### What problem does WMRouter solve in an Android app?

It centralizes page navigation behind URI matching instead of direct Intent references to Activity classes, so pages can be reached by a link and navigation logic such as login or location checks can run once in an interceptor. It ships a second module, ServiceLoader, for calling code across app modules by interface.

### Why did the Maven group ID for WMRouter change?

Because JCenter shut down, the group moved from com.sankuai.waimai.router to io.github.meituan-dianping starting with version 1.2.1. If your dependency declaration still uses the old coordinates it will not resolve, and changing the group name is the whole fix.

### Is WMRouter still maintained?

There is a gap between the two signals. The most recent tagged release is v1.2.1 from 2021-06-07, while GitHub reports a push on 2026-04-02. Treat the source as active but check what version your build actually resolves, since the releases page has not moved in five years.

### Which apps use WMRouter?

The README lists four products from Meituan as users: Meituan Waimai, Meituan Waimai Business, Meituan itself and Dianping. That makes it a production framework with a very large real codebase behind it, which is also why the API is more extensive than a minimal router.

## Sources

- [License: Apache-2.0](https://github.com/meituan/WMRouter/blob/master/LICENSE)
- [meituan/WMRouter on GitHub](https://github.com/meituan/WMRouter)
- [Project website](https://tech.meituan.com/meituan_waimai_android_open_source_routing_framework.html)
- [README](https://github.com/meituan/WMRouter/blob/master/README.md)
- [Releases](https://github.com/meituan/WMRouter/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/meituan-wmrouter
