跪拜 Guibai
← All articles
Architecture · Design Patterns · Android

ComboNative Puts Real Kotlin/Compose Code Inside Android Mini Programs, Each in Its Own Process

By jctech ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Android teams building for in-vehicle systems, kiosks, TVs, or custom ROMs have long faced a deadlock: JS-based mini programs are safe but slow and limited, while native plugins leak memory, crash the host, and break when obfuscated. ComboNative breaks that deadlock by running native code in separate processes with a narrow IPC contract, making independent hardening and clean process teardown practical for the first time in a lightweight, no-hook package.

Summary

ComboNative is a new Android dynamic framework that treats each mini program as a native Kotlin/Compose module running in its own sub-process. It solves three structural problems that plague single-process pluginization: independent obfuscation and hardening become possible because cross-process communication transmits only bytes and API IDs, never class names; crash isolation is guaranteed by the process boundary, with a circuit breaker that disables a misbehaving mini program; and memory leaks are eliminated because closing a mini program simply kills its process, letting the OS reclaim everything.

The runtime achieves this without hooking AMS or PMS. A build-time Gradle plugin generates a fixed pool of stub components and stub processes, and the host assigns an idle slot when launching a mini program. The trade-off is a hard cap on concurrent mini programs equal to the number of pre-declared slots, and a small per-process memory overhead.

A complete demo with three mini programs—a ledger app, a Compose UI demo, and an NDK image processor—clocks in at 7.74 MB in release, with the framework itself accounting for under 5%. The project is in alpha; four runtime libraries are on Maven Central, the sample repository is open source, and the main engine library is available as a prebuilt dependency.

Takeaways
Each mini program is a standard Android Library written in Kotlin/Compose, compiled into a .cbp package and loaded by the host at runtime.
Mini programs run in independent sub-processes assigned from a build-time-generated pool of stub slots; the maximum concurrency equals the slot count.
Cross-process communication uses AIDL and transmits only apiId, apiVersion, and serialized byte payloads—never class names—so host and mini programs can be independently obfuscated and hardened.
Closing a mini program terminates its process, eliminating the manual reference cleanup that causes memory leaks in single-process pluginization.
Crash isolation is process-level; a sliding-window crash counter disables a faulty mini program automatically.
The framework adds about 347 KB to a host APK; a functional ledger mini program compresses to 32.8 KB, and a full demo with three mini programs is 7.74 MB in release.
Payloads over 128 KiB automatically switch to a ParcelFileDescriptor pipe bypass to avoid TransactionTooLargeException.
Installation enforces a signature whitelist, version checks, and transactional disk writes; host APIs are gated by a three-tier permission model (NORMAL, DANGEROUS, HOST_SIGNATURE).
Mini programs share the host's UID, so the security model is controlled-access, not a strong OS-level sandbox; it is unsuitable for open markets with anonymous third-party native code.
The main engine library is not open source but is published on Maven Central; the sample repository and four runtime libraries are fully open and buildable.
Conclusions

By shifting dynamism from runtime hooking to build-time stub generation, ComboNative sidesteps the entire AMS/PMS compatibility minefield that has made traditional pluginization fragile across Android versions.

The 'bytes not class names' IPC rule is the architectural linchpin: it decouples the host and mini program build pipelines completely, which is what makes independent obfuscation and hardening possible where single-process approaches fail.

The slot-pool design is a deliberate trade of flexibility for stability—capping concurrency and unifying launchMode across slots in exchange for zero system-API hooks and predictable resource usage.

ComboNative's security model is honest about its limits: same UID means it cannot stop a malicious native module from accessing process-reachable resources, so it explicitly targets controlled, semi-trusted ecosystems rather than open app markets.

The framework's size efficiency comes from treating the host as a shared runtime: mini programs use compileOnly for the SDK and inherit Compose/AndroidX from the host, so .cbp files carry only differential code.

Concepts & terms
Stub process pool
A fixed set of empty Android components and processes declared at build time by a Gradle plugin. At runtime, the host assigns an idle stub slot to a mini program, avoiding any need to hook AMS or PMS to create components dynamically.
AIDL contract (bytes-not-class-names)
The IPC rule that cross-process calls carry only an apiId, apiVersion, and serialized byte payload. Because class names are never exchanged, the host and mini program can be obfuscated independently without breaking symbol resolution.
fd bypass
When an IPC payload exceeds 128 KiB, ComboNative automatically switches from inlining data in the Binder transaction to passing a ParcelFileDescriptor, streaming the data through a pipe to avoid TransactionTooLargeException.
Crash circuit breaker
A sliding-window counter that tracks mini program crashes; if the count exceeds a threshold, the mini program is automatically disabled to prevent repeated restart loops, with a manual re-enable option.
.cbp package
The ComboNative binary package format for a mini program. It is a stripped-down archive containing only the mini program's own dex and resources, relying on the host for the framework SDK and shared dependencies like Compose.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗