How WeChat Re‑engineered Its Android Architecture for Scalability
This article chronicles the evolution of WeChat's Android architecture from a simple layered design through multi‑process and modular restructurings, explains why a major refactor was needed, and details the new communication, module, and code‑boundary strategies that enable faster development, better isolation, and a lightweight WeChat nano demo.
WeChat Android Architecture History
When WeChat Android was first created, it used a common layered architecture. This simple and clear design has been retained to this day, representing the v1.x era of WeChat architecture.
Figure 1 – Architecture evolution
In the v2.x era, rapid business growth exposed delayed message notifications and a memory‑leak problem in WebView on Android versions prior to 2.3. The growing code base, memory usage, and APK size increased resource consumption, causing the WeChat process to be reclaimed by the system. Consequently, WeChat switched to a multi‑process architecture: a dedicated communication process kept long‑living connections stable, and a separate WebView process isolated the memory‑leak issue.
Figure 2 – Architecture diagram
Why Refactor Again
After more than two years of the previous architecture, the number of Gradle modules kept increasing, supporting large features such as Moments, Shake, and Nearby People. The original architecture worked well, but code continued to bloat. Core libraries libnetscene and libplugin grew without limit, and many business‑related classes were forced into a centralized base class, making it unreadable. Lack of compile‑time isolation caused module boundaries to degrade, and tools to limit erroneous dependencies could not fully prevent the damage.
Figure 3 – Gradual architecture changes
When hardware teams requested a lightweight version of WeChat for a photo‑album product, the existing monolithic codebase could not be easily trimmed, prompting a full refactor.
Reshaping Modularity
Changing Communication Method
The original Event‑bus was suitable for one‑to‑many broadcasts but became painful for complex data exchange, leading developers to bypass it and place shared code into the base library, causing its bloat.
We replaced the Event‑bus with an SDK‑style interface approach. Each module now provides an SDK containing interfaces and data structures, which other modules consume at compile time.
Figure 4 – Register interface
Figure 5 – Access interface
To make SDK exposure even easier, we introduced the “.api” convention: rename any file that should be exposed from .java to .api. The Gradle build scripts are adjusted accordingly.
Figure 6 – “.api” conversion
Gradle settings files are updated to recognize the new file type (see Figure 7).
Figure 7 – settings.gradle & build.gradle
Redesigning Modules
We eliminated the “code sink” of the base library by moving previously sunk code back into its owning module. The new mmkernel layer is split into three core parts: CoreAccount, CoreNetwork, and CoreStorage, each handling account state, network callbacks, and storage lifecycle respectively.
Figure 10 – mmkernel structure
The original module lifecycle only had “Account init” and “Account logout”. We extended it to cover the whole application start‑up and shutdown, allowing each module to define a Plugin class with three phases: dependency() – declare other plugins this one depends on. configure() – initialize data, register services, insert BootTask s. execute() – run the actual startup logic.
Figure 12 – Plugin initialization phases
During dependency() a full dependency tree is generated, distinguishing compile‑time type dependencies from runtime logical dependencies.
Figure 14 – Dependency tree
The configure() phase traverses the tree to set up data, register IService implementations, and insert BootTask s.
Figure 15 – Configure phase
In the execute() phase each Plugin (also a BootTask) runs its logic according to the ordered tree, ensuring a deterministic start‑up sequence.
Figure 16 – BootTask execution
Constraining Code Boundaries
Compile‑time isolation is the only reliable way to keep code boundaries intact. Beyond separating projects, we introduced a fine‑grained “pins” project structure that allows sub‑projects inside a module, each defined by a project.properties file that lists its compile‑time dependencies.
Figure 17 – Pins project example
A custom code‑check tool runs during compilation to forbid illegal references outside the declared dependencies.
Figure 18 – Code‑check
The pins approach reduces the number of tiny modules, improves incremental Gradle compilation speed, and enforces strict module boundaries.
Trade‑offs and Choices
We evaluated plugin/sandbox solutions (Atlas, Small, DroidPlugin, DynamicApk) that provide dynamic loading and strong isolation. While dynamic plugins are useful for hot‑patches and rapid feature rollout, WeChat’s workflow does not require frequent independent releases, and the added complexity would outweigh the benefits. Therefore we kept the pure modular approach and rely on Tinker for hot‑patching when needed.
Beyond Code, Inside Architecture
To maintain code quality we introduced a “module owner” system. Each module has a designated owner responsible for its code, design, and public APIs, and who reviews any changes made by others. This improves ownership, encourages proactive refactoring, and raises overall code‑review rates.
Final
Refactoring the whole architecture cannot be done in a single sprint; it proceeds incrementally through “split → gray release → merge back” cycles. The result is a lightweight WeChat nano demo (≈3.5 MB, ~10 % of the full app, 25 % of memory usage) that proves the modular design works.
We hope the shared experience helps other teams improve modularity, enforce boundaries, and keep their Android codebases healthy.
WeChat Client Technology Team
Official account of the WeChat mobile client development team, sharing development experience, cutting‑edge tech, and little‑known stories across Android, iOS, macOS, Windows Phone, and Windows.
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.
