ComboNative Puts Real Kotlin/Compose Code Inside Android Mini Programs, Each in Its Own Process
Let 'Mini Programs' Run Real Native Code: ComboNative Multi-Process Native Mini Program Runtime · Alpha Launch
An Android dynamic framework: Each mini program is native Kotlin/Compose, runs in an independent process, and can be independently obfuscated and hardened from the host. The framework runtime is about 347 KB, and a fully functional mini program package can be as low as 32.8 KB. A complete demo (release) containing three mini programs is only 7.74 MB.
The main library is not yet open source, but has been published to Maven Central. Alpha early access is now open, and you can directly integrate it for trial. This article is long; it is recommended to skip read by subheadings as needed: from 'Why build it' to 'How to use it' to 'Where are the boundaries', explained thoroughly at once.
TL;DR (30-second speed read)
If you don't have time to read the full article, just remember these points:
- What it is: An Android dynamic framework. Each 'mini program' is native Kotlin/Compose code, running in an independent sub-process, dynamically delivered, loaded, and governed by the host App.
- What it solves: The three deadlocks of traditional single-process pluginization—cannot independently obfuscate/harden, memory leaks are hard to cure, crashes are contagious; meanwhile, it does not have the performance and capability ceiling of JS mini programs.
- How it achieves this: Cross-process communication only goes through AIDL contracts, only transmits bytes, not class names (→ both sides independently obfuscate/harden); one process per mini program, close it and kill the process (→ no memory leaks, crashes are not contagious); build-time static generation of stub processes, 0 Hook AMS/PMS (→ stable, version-compatible).
- How lightweight: Framework runtime is about 347 KB; a fully functional ledger mini program
.cbpis only 32.8 KB; a complete demo (release) with 3 mini programs is 7.74 MB, with the framework itself accounting for <5%. - Who it's for: Controlled terminals like in-vehicle systems / cockpits / TVs / industry PDAs / Kiosks / custom ROMs, and first-party / partner ecosystems needing code asset protection + dynamic delivery of native features.
- Who it's not for: Open markets allowing anonymous arbitrary third parties to upload native code (it is not a strong sandbox); iOS / cross-platform; dynamically loading executable code from outside Google Play within Google Play.
- What you can do now: The main library is not yet open source, but four libraries are on Maven Central, the sample repository is open source, you can directly integrate and experience it, and you are welcome to participate in the Alpha public test.
🚀 Experience Immediately (Don't want to read the long article? Get started first)
| Entry | Link |
|---|---|
| 📥 Sample App Download (No build needed, install and play) | https://130.94.11.104:17558/down/9yiZ75pfO4QR.apk |
🧩 Sample Mini Program .cbp Downloads |
Mini Program ① · Mini Program ② · Mini Program ③ |
| 🔗 Sample Open Source Repository (Host + 3 native mini programs, can be independently cloned and built) | https://github.com/lnzz123/combonative-samples |
| 📚 Maven Integration | io.github.lnzz123:combonative-engine:0.1.0-alpha01 (and abi/runtime/mini-sdk) |
| 🧰 Packaging Plugin (Gradle Plugin Portal) | io.github.lnzz123.combonative-packer:0.1.0-alpha01 |
If you find it interesting, feel free to give a ⭐ Star, raise an Issue, and participate in the Alpha public test together. If you want to know 'why it was designed this way', continue reading below.
Table of Contents
- A Real Dilemma: Let's Start with a Story
- Background: Why Build ComboNative
- Its Relationship with ComboLite
- Design Goals and Core Trade-offs (including a three-way comparison)
- Core Mechanisms (Principle Level)
- Lightweight: Reproducible Size Data
- Ten-Minute Hands-On: From Host Integration to Running Your First Mini Program
- Real Security Boundaries and Applicable Scenarios
- Selection: ComboLite or ComboNative
- Frequently Asked Questions (FAQ)
- Current Status
- Early Access and Resources
〇、A Real Dilemma: Let's Start with a Story
Imagine you are building an in-vehicle system (or a smart cockpit / all-in-one machine / industry PDA, pick one). The product manager puts forward three seemingly ordinary but actually conflicting requirements:
- Must be able to dynamically deliver features. After the car is sold, 'charging maps', 'member malls', 'third-party ecosystem apps' must be able to update remotely and install on demand; it's impossible to do a full OTA every time.
- Must have native experience and capabilities. Any stutter or dropped frame on the central control screen will be magnified into 'this car is cheap'; and it needs to use underlying capabilities like Bluetooth, CAN bus, location, camera. Wrapping it in a JS container is both slow and limited everywhere.
- Must be stable and secure. The in-vehicle system runs for months without a reboot; any third-party module crash or memory leak must not affect the main HMI; partner modules cannot arbitrarily read things you don't want to give them, and your own core code cannot be easily reverse-engineered.
So you oscillate between two old paths:
- Choose JS / cross-platform mini programs? It satisfies 'dynamic delivery + sandbox security', but performance and underlying capabilities fail the test, and the product experience is dragged down by the container.
- Choose traditional native pluginization? Performance and capabilities are there, but you cannot independently harden plugins (it crashes upon obfuscation), a plugin crash can bring down the entire central control, memory usage grows over long runs—for a device that doesn't reboot for months, this is almost a time bomb. Moreover, many solutions hook system private APIs, causing anxiety with every major version upgrade.
You will find that no existing path can satisfy these three points simultaneously. This triangular contradiction of 'native vs dynamic vs isolation' is precisely the starting point for me to build ComboNative. It does not try to reinvent Android, but rather reorganizes Android's existing capabilities—using 'multi-process + static stubs + narrow contracts' to take care of these three corners at once. Let's start from the beginning.
一、Background: Why Build ComboNative
Before creating ComboNative, I maintained the previous generation single-process pluginization framework ComboLite (0 Hook / 0 Reflection, Compose-first, open-sourced and on Maven Central). ComboLite achieved a fairly complete level of 'clean loading' for single-process pluginization, but when pushed to enterprise-level, production-grade scenarios, it exposed three structural limitations rooted in the architectural foundation, difficult to cure by patching:
- Difficult to independently obfuscate and harden. The essence of single-process pluginization is that the host and plugins share the same ClassLoader and link to each other at runtime by the fully qualified class name (FQCN). Once obfuscation/hardening is enabled on either side, class names are renamed, and cross-module symbols cannot be resolved. This makes 'core code asset protection'—one of the strongest enterprise willingness-to-pay demands—basically unsolvable under this model.
- Memory leaks are hard to cure. Plugin unloading relies on manually cleaning up references (ClassLoader, resources, static holders, callbacks, etc.). Missing any link causes a leak. This is particularly fatal for devices like in-vehicle systems, set-top boxes, and industry terminals that do not reboot for long periods.
- Crashes are contagious. Plugins and the host run in the same process; an uncaught exception in one place can crash the entire application.
The conclusion is clear: these problems have little room for optimization within the single-process model and require a redesign from the process model level. ComboNative comes from this—it is not a version upgrade of ComboLite, but a next-generation runtime redesigned under the same technical lineage to address the above structural defects.
二、Its Relationship with ComboLite
| Dimension | ComboLite (Single-Process) | ComboNative (Multi-Process) |
|---|---|---|
| Model | Single-process pluginization, shared ClassLoader, cross-plugin linking by FQCN | Multi-process 'native mini program' runtime, independent process + independent ClassLoader |
| Obfuscation & Hardening | Cross-plugin linking by class name, difficult to independently obfuscate | Cross-process only via contract, no class names transmitted, host and mini programs independently obfuscate and harden |
| Crashes | Plugin crash affects host | Sub-process crash isolation + crash circuit breaker |
| Memory | Unloading relies on cleaning references, prone to leaks | Close means kill process, overall reclamation by the OS |
| Open Source | Open-sourced, on Maven Central | Main library not yet open source (on Maven, Alpha experience open) |
| Maturity | Relatively mature, can be used in production | 0.1.0-alpha01, early access stage |
Two points to clarify:
ComboLite will continue to be open source. Its clean class loading, AAR→APK packaging plugin, global class index, dependency graph, and chain restart implementations are all publicly readable, serving as a complete pluginization engineering reference. For developers who want to deeply understand the principles of dynamicization, after understanding the capability boundaries of the single-process model, they can fully implement a runtime similar to ComboNative along the 'multi-process + IPC contract' direction.
ComboNative's main library is not open source for now (considering its Alpha stage and future commercialization plans), but the framework's four libraries have been published to Maven, and a complete open-source sample repository is provided, allowing you to integrate and experience it first.
三、Design Goals and Core Trade-offs
Android dynamicization within the industry has long been a trade-off between two types of solutions:
- JS Mini Programs / Cross-Platform Containers: Sandbox security, cross-platform consistency, mature ecosystem; the cost is that UI goes through a rendering bridge, logic crosses JS↔Native boundary serialization, performance has an upper limit, and underlying capabilities are constrained by the container's opening pace.
- Traditional Native Pluginization: Performance close to native; the cost is the aforementioned problems of obfuscation, leaks, and crash contagion, and many solutions rely on hooking system private APIs like AMS/PMS.
ComboNative's design goal is to take the strengths of both: execute natively, deliver and govern like a mini program, and eliminate the above defects from the architectural level. Its core design and corresponding benefits are as follows:
| Goal | Implementation Method | Benefit |
|---|---|---|
| Independent Obfuscation & Hardening | Cross-process only transmits apiId + serialized bytes, never class names |
Host and mini programs can be obfuscated and hardened separately |
| Crash Isolation | Each mini program runs in an independent sub-process | Crashes only affect themselves, host is unaware, with circuit breaker as a fallback |
| Controllable Memory | Closing a mini program terminates its process | Memory is overall reclaimed by the OS, eliminating leak accumulation |
| No Dependency on System Private APIs | Build-time static generation of stub components and stub processes | 0 Hook AMS/PMS, all public APIs, version compatibility controllable |
The security model needs to be honestly stated: ComboNative uses controlled access + privilege interception + process isolation, not a strong sandbox. Mini programs share the same UID as the host, and native code can access underlying resources reachable by that process. Therefore, it is aimed at first-party / partners, and semi-trusted ecosystems governed by signature whitelist + review access, not open markets for anonymous arbitrary third parties. Section 7 will specifically expand on the boundaries.
Three-Way Comparison: JS Mini Programs / Traditional Pluginization / ComboNative
Putting the three paths side by side, the trade-offs are clear at a glance:
| Dimension | JS Mini Programs / Cross-Platform Containers | Traditional Native Pluginization | ComboNative |
|---|---|---|---|
| Execution Method | JS + Rendering Bridge | Native | Native Kotlin/Compose |
| Performance Ceiling | Limited by bridge and serialization | Native | Native |
| Underlying Capabilities | Constrained by container opening pace | Complete | Complete (including NDK) |
| Crash Isolation | Container-level isolation | ❌ Same-process contagion | ✅ Process-level isolation + circuit breaker |
| Memory Governance | Container reclamation | ❌ Manual cleanup, prone to leaks | ✅ Close means kill process |
| Independent Obfuscation & Hardening | Not applicable (JS) | ❌ Class-name linking, hard to obfuscate | ✅ Only transmits bytes, can independently harden |
| Hooks System APIs | No | Most require Hook AMS/PMS | ✅ 0 Hook, all public APIs |
| Security Strength | Strong sandbox | Weak | Controlled access + interception + isolation (not a strong sandbox) |
| Applicability | Open ecosystems, cross-platform | General App dynamicization | Controlled / semi-trusted terminals |
One sentence to understand this table: JS mini programs win on 'security + cross-platform', traditional pluginization wins on 'native performance', and ComboNative aims to take 'isolation + memory governance + code protection' together on the basis of 'native performance'—the cost is that it only targets controlled ecosystems with manageable sources, and trades a certain amount of cross-process overhead and resource usage.
四、Core Mechanisms (Principle Level, Not Involving Internal Implementation)
4.1 Stub Process Pool: 0 Hook Multi-Process
Android's four major components cannot be dynamically created at runtime; the system only recognizes components statically declared in the pre-packaged manifest, and android:process=":mpN" is also determined by the manifest. To allow plugin components to be scheduled by the system, traditional pluginization commonly uses Hook AMS/PMS + stub component deception—it works, but relies on system private APIs and requires re-adaptation with every major version upgrade.
ComboNative's approach is no hooking, no deceiving the system, only using public APIs, shifting the dynamism from 'runtime deception' to 'build-time slot creation':
At build time, pre-declare N groups of 'stub' components, each bound to an independent process
:mp1…:mpN; at runtime, when a mini program needs to run, pick an idle slot from the process pool and launch it; when closing, terminate the process and return the slot.
The key here is: slots are homogeneous, empty, and generic—not bound to any specific mini program, like hotel rooms, anyone can check in, and they are vacated when they leave. Stub components, process declarations, and supporting manifests are all automatically generated by the build-time Gradle plugin, requiring no manual writing by the developer.
The cost needs to be honestly stated: The maximum number of concurrently running mini programs = the number of pre-declared slots; static attributes like launchMode are unified for homogeneous slots. This is the inevitable trade-off of the 'no hooking, only public APIs' path—exchanging controllable resource overhead and flexibility constraints for long-term stability and version compatibility.
4.2 AIDL Contract: Only Transmit Bytes, Not Class Names
Multi-process solves isolation but brings communication problems. ComboNative's answer is a layer of AIDL + Parcelable contracts, with an iron rule running throughout:
Cross-process only transmits
apiId,apiVersion, and serializedpayloadbytes; does not transmitClass, fully qualified class names, or any arbitrary serializable objects.
What flows between the host and the mini program is always 'data', not 'types'. Precisely because the two ends never reference each other by class name, the host can arbitrarily obfuscate its classes, and the mini program can arbitrarily obfuscate its classes, without affecting each other—this is the fundamental reason why ComboLite cannot, but ComboNative can, achieve 'independent obfuscation and hardening'. The narrower and more stable the contract surface, the freer the evolution of both ends.
Additionally, requests carry apiVersion, allowing capabilities to evolve backward-compatibly within compatibility intervals; the host creates a communication endpoint bound to the mini program's real identity for each one, and any caller-forged identity fields will be overwritten by the real identity.
4.3 Cross-Process Proxying for Four Major Components
Mini programs can normally use Activity, Service (including foreground services), BroadcastReceiver, and ContentProvider, all carried by stub components:
- Activity is carried by the stub
ProxyActivity, supporting in-process stack navigation andstartActivityForResultresult return; - Service is carried by a per-slot proxy pool, supporting foreground services (Android 14+'s
foregroundServiceTypeis statically declared by the host); - Static Broadcasts are received by the host's main process, which then wakes up the target mini program process and forwards them;
- ContentProvider is routed by the host by authority, rewrites the URI, and forwards it to the mini program process; Cursor results are returned cross-process via
CursorWindowshared memory.
4.4 fd Bypass for Large Payloads
Binder's single transaction buffer is limited (about 1 MB and shared with concurrent transactions); inlining large data triggers TransactionTooLargeException. ComboNative handles this at the IPC boundary: when a payload exceeds 128 KiB, it automatically switches to a ParcelFileDescriptor pipe bypass (only transmitting an fd within the Binder), completely transparent to business code, requiring no manual chunking.
4.5 Access Control, Circuit Breaker, and Process Reclamation
- Installation Access Control: Parse
.cbpmetadata → signature whitelist verification → version comparison (default denies downgrades, upgrade verifies signature consistency to prevent replacement) → transactional disk write, startup is recoverable; - Crash Circuit Breaker: If the number of crashes within a sliding window reaches a threshold, the mini program is automatically disabled, and can be re-enabled with one click later;
- Process Health: Binder death detection monitors sub-process liveness; a task sentinel reliably reclaims the process and slot when the recent task is removed.
Architecture Overview
五、Lightweight: Reproducible Size Data
Data caliber: 0.1.0-alpha01, release / R8 obfuscated. All numbers can be reproduced with one click in the open-source sample repository (see Section 11 for commands).
5.1 Framework Runtime: About 347 KB
| Module | AAR Size | Role |
|---|---|---|
combonative-abi |
66.9 KB | Contract layer (AIDL + serialization models) |
combonative-engine |
202.4 KB | Host main process engine: scheduling, API bridge, installation access control |
combonative-runtime |
53.7 KB | Sub-process runtime: dex loading, stub components, resource loading |
combonative-mini-sdk |
23.9 KB | Mini program development surface (provided by host at runtime) |
| Total | ≈ 347 KB |
The packaging plugin
combonative-packer(165 KB) is not counted—it only runs at build time and does not enter the runtime APK.
5.2 Single Mini Program Package: As Low as 32.8 KB
| Mini Program | Form | .cbp (release) |
.cbp (debug) |
|---|---|---|---|
miniapp-ledgerbook |
Pure logic + lightweight UI (ledger) | 32.8 KB | 44.8 KB |
miniapp-minihub |
Pure Compose UI | 97.7 KB | 141.7 KB |
miniapp-imagelab |
Includes NDK native image processing | 429.0 KB | 497.0 KB |
A fully functional ledger mini program is only 32.8 KB. ⚠️ But 'tens of KB' should not be taken as a constant indicator for all mini programs—size depends on code volume, UI complexity, whether NDK is included, etc. The above are measured values for specific examples, specific versions, and a specific caliber.
Why can it be so small? The core is 'not re-packaging what the host already has': mini programs use compileOnly for the framework SDK (provided by the host at runtime, not entering .cbp), and do not re-package common dependencies like Compose / AndroidX that the host already possesses. The .cbp only contains its own unique code and resources. It's like moving into a fully furnished apartment—you only need to bring your own luggage.
5.3 Host Whole Package: 7.74 MB, Framework Accounts for Less Than 5%
| Build Type | Host APK Size |
|---|---|
| release (R8) | 7.74 MB |
| debug | 11.02 MB |
The vast majority of this 7.74 MB is conventional dependencies like Compose / Material3 / AndroidX (which any Compose application must carry). The four framework runtime AARs total about 347 KB, accounting for less than 5%. In other words, ComboNative adds almost no 'weight' to the host; the overall package size is mainly determined by your own business UI dependencies.
六、Ten-Minute Hands-On: From Host Integration to Running Your First Mini Program
The following code is all usage of the public SDK (not internal implementation of the main library) and can be operated against the open-source sample repository. Prerequisites: JDK 17, AGP 8.12+, Kotlin 2.2+, minSdk 24 / compileSdk 36.
6.1 Configure the Host (One-Time)
Host App module build.gradle.kts:
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("org.jetbrains.kotlin.plugin.compose")
id("io.github.lnzz123.combonative-packer") version "0.1.0-alpha01"
}
dependencies {
implementation("io.github.lnzz123:combonative-engine:0.1.0-alpha01")
}
comboNativeHost {
miniApps {
enabled.set(true) // Automatically package mini programs into assets/miniapps/
buildType.set(com.combonative.packer.PackageBuildType.DEBUG)
dir.set("miniapps")
}
slots {
enabled.set(true)
count.set(3) // Maximum number of concurrently running mini programs
servicesPerSlot.set(2)
}
}
Root project build.gradle.kts declares the mini programs to package and the signature:
plugins { id("io.github.lnzz123.combonative-packer") version "0.1.0-alpha01" }
combonativePacker {
modules { module(":my-miniapp") }
signing {
keystorePath.set("/path/to/your.jks")
keystorePassword.set("..."); keyAlias.set("..."); keyPassword.set("...")
}
}
Initialize the engine with the generated slot list (Application.onCreate):
class HostApp : Application() {
override fun onCreate() {
super.onCreate()
ComboNative.initialize(
application = this,
config = ComboNativeConfig(
slots = com.combonative.generated.GeneratedProcessSlots.SLOTS,
hostApiProviders = listOf(/* see 6.3 */),
enforceSignature = false, // Can be turned off during debugging; must be true in production
),
)
}
}
Stub components are injected into the merged manifest by the packer, no manual writing needed; the host manifest only needs to declare
android:name=".HostApp"and its own launcher.
6.2 Develop a Mini Program (Standard Android Library)
// :my-miniapp/build.gradle.kts
android {
namespace = "com.example.mymini" // ← This is the mini program's unique ID
compileSdk = 36
defaultConfig { minSdk = 24 }
buildFeatures { compose = true }
}
dependencies {
compileOnly("io.github.lnzz123:combonative-mini-sdk:0.1.0-alpha01") // Golden rule: compileOnly
implementation("androidx.compose.material3:material3:<version>")
}
Entry class inherits MiniApp, the base class is UI-neutral:
class MyMiniApp : MiniApp() {
override fun onCreateView(context: Context): View =
ComposeView(context).apply {
setContent { MiniTheme { Text("Hello from my first MiniApp!") } }
}
}
Manifest metadata (reuses standard manifest fields: package name = unique ID, label = display name, versionCode/Name = version):
<manifest android:versionCode="1" android:versionName="1.0.0">
<application android:label="My First Mini Program">
<meta-data android:name="combonative.miniapp.entryClass"
android:value="com.example.mymini.MyMiniApp" />
<meta-data android:name="combonative.miniapp.hostApis"
android:value="account.getToken;ui.toast" /> <!-- Requested host APIs, semicolon separated -->
</application>
</manifest>
6.3 (Optional) Host Exposes Capabilities to Mini Programs
The capability catalog is entirely defined by the host; the framework does not have built-in ones:
// Host side
private class AccountTokenProvider : HostApiProvider {
override val apiId = "account.getToken"
override fun handle(request: HostApiRequest) =
HostApiResponse(HostApiResponse.ResultCode.OK, "token-for-${request.callerMiniId}".toByteArray())
}
// Mini program side
val token = hostApi.invoke("account.getToken", apiVersion = 1).payload?.decodeToString()
Authorization is divided into three levels: NORMAL / DANGEROUS (requires user authorization) / HOST_SIGNATURE (only available to same-signature callers).
6.4 Build, Install, Run
./gradlew :<your host module>:assembleDebug # Automatically injects .cbp into host assets
unzip -l <host APK path> | grep -i cbp # Verify .cbp is in the APK
In the host, release → install → launch:
ComboNative.installFromPackage(file, forceOverwrite = true) // Force overwrite during debugging
ComboNative.launch(activity, "com.example.mymini") // miniId = package name
Production semantics should use
ComboNative.showInstallConfirm(context, file)to bring up an installation confirmation page (displaying name/version/requested APIs/signature risks), and only write to disk after user confirmation.
After running: The system launches the :mp0 sub-process to render the UI; in the system's 'Recent Tasks', the host and the mini program each occupy a card; swiping away the mini program card, the task sentinel reliably reclaims that sub-process.
At this point, you have run through the complete loop—you will find that developing a mini program is almost no different from developing a regular Android Library module.
七、Real Security Boundaries and Applicable Scenarios
Rather than claiming security is omnipotent, it's better to clearly state 'what it can do and what it cannot do'.
7.1 Three-Layer Security Model
- Controlled Access: Signature whitelist (directly reject if not on the whitelist), version verification (default denies downgrades, upgrade verifies signature consistency to prevent replacement), installation confirmation page transparently displays requested permissions;
- Privilege Interception: The only channel for a mini program to access capabilities is the host API bridge. Each call sequentially checks 'Is the API registered → Does the version match → Is it declared in the manifest → Is it authorized → Audit → Dispatch'; Authorization is divided into three levels:
NORMAL/DANGEROUS/HOST_SIGNATURE, whereHOST_SIGNATUREmakes certain capabilities only open to self-signed mini programs; Identity is bound by the server according to the real identity and cannot be forged; - Process Isolation: Crash isolation (sub-process crash does not affect the host), crash circuit breaker (automatically disable after repeated crashes), process reclamation (Binder death detection + task sentinel).
7.2 Boundaries That Must Be Honestly Stated
- Same UID, not strong isolation. The mini program process shares the same UID as the host; native code can access underlying resources reachable by that process. Process isolation guarantees stability isolation (crashes/memory/lifecycle), not an OS-level security sandbox; the API whitelist can block 'privileged calls through the API bridge', but cannot block deliberate native privilege escalation.
- Therefore, not suitable for anonymous third-party open markets. If you want to 'allow any anonymous developer on the internet to upload native modules and run them directly', you need an OS-level strong sandbox (independent UID / seccomp / SELinux), which is not ComboNative's positioning. Its value premise is controllable sources.
- Also: Android-only; not suitable for dynamically loading executable code from outside Google Play within Google Play.
7.3 Suitable and Not Suitable
✅ Suitable for: Controlled terminals like in-vehicle systems / smart cockpits, smart TVs, industry PDAs, Kiosks, vending machines; custom ROMs, controlled sideloading platforms; first-party / partner ecosystems needing code asset protection and dynamic delivery of native features; semi-trusted mini program ecosystems governed by signature whitelist + review access.
❌ Not suitable for now: Open markets where anonymous arbitrary third parties upload native modules; scenarios requiring iOS / cross-platform; scenarios dynamically loading executable code from outside Google Play within Google Play.
A few examples of why scenarios fit: In-vehicle systems run for long periods without reboot, process isolation + close-to-reclaim cures leaks, a single module crash does not affect the main HMI; OTA only needs to deliver a few tens of KB .cbp, no full package upgrade needed; custom ROMs build a semi-trusted ecosystem with a signature whitelist, first-party modules are independently hardened to protect code assets.
八、Selection: ComboLite or ComboNative
| Dimension | ComboLite (Single-Process) | ComboNative (Multi-Process) |
|---|---|---|
| Call Overhead | Direct in-process call, extremely low | Cross-process IPC, serialization/Binder overhead |
| Obfuscation & Hardening | Difficult to independently obfuscate | Independently obfuscate and harden |
| Crash Impact | Affects host | Isolation + circuit breaker |
| Memory | Unloading prone to leaks | Close means kill process, reclamation |
| Concurrency/Resources | Not limited by slots, more memory-efficient | Limited by slot count, higher resident memory |
| Maturity | Relatively mature, open source | Alpha, main library not yet open source |
One-sentence judgment method: What do you fear most, 'performance overhead' or 'isolation and code protection'?
- Consumer-grade Apps, need maturity and stability, fear overhead, many modules → ComboLite;
- Controlled terminals, need independent hardening / crash isolation / no memory leaks, controllable sources → ComboNative.
For existing ComboLite users: No need to rush to migrate; it will continue to be maintained as open source. But if you have already hit the walls of 'want to harden but it crashes upon obfuscation / memory continuously grows / one plugin crash brings down the entire App', that is precisely the problem ComboNative aims to solve from the architectural level—you can first use the open-source sample to evaluate whether the 'cross-process overhead' is acceptable, then decide to adopt.
九、Frequently Asked Questions (FAQ)
Q1: Multi-process sounds heavy. One process per mini program, can the memory handle it? A: Processes do have a resident memory cost, which is exactly why the design of 'concurrency limit = slot count' exists—you pre-declare an appropriate number of slots based on device resources, rather than opening processes infinitely. What you get in return is 'close means kill process, overall memory reclamation', which is actually more stable for long-running devices. Reduce the slot count when resources are constrained; compared to single-process, it trades controllable memory for isolation and no leaks, a clear trade-off.
Q2: Will the overhead of cross-process IPC slow down the mini program? A: UI rendering and business logic all execute natively within the mini program's own process, not crossing a bridge; only 'calling host capabilities' triggers a single IPC. That is, overhead only occurs at explicit capability call points, not every frame or every interaction. Large data also has the fd bypass as a fallback. For scenarios with extremely frequent interactions and high latency sensitivity, refer to the selection advice in Section 8.
Q3: Can mini programs use Compose? Can they use NDK / .so?
A: All can. Mini programs are native Android Libraries; Compose, traditional Views, Kotlin/Java, NDK native code are all supported. The sample miniapp-imagelab includes NDK image processing.
Q4: Can .cbp be installed directly like an APK?
A: No, and it shouldn't be. .cbp is a loading format specific to the ComboNative host; it can only be installed, loaded, and run by the host. The system will not treat it as an independent App. This is by design.
Q5: The main library is not open source. Will I be 'locked in' if I integrate it? A: The framework's four libraries are published on Maven Central and can be depended on normally; the sample repository is fully open source, and the integration methods, DSL, and APIs are all public. Additionally, the previous generation ComboLite is fully open source, with principles and implementations readable. You can clearly see 'how to integrate, how to use'; there is no black-box lock-in.
Q6: How is it different from Google's official Dynamic Feature Module? A: Official dynamic modules are aimed at Google Play distribution, delivered by Play at install time/on demand, still run in the same process, within the same App, do not provide process isolation or independent hardening, and depend on the Play channel. ComboNative is aimed at controlled terminals / self-distribution, provides process-level isolation and independent hardening, and is self-governed by the host at runtime, not dependent on Play.
Q7: It's Alpha now, can it go into production? A: It is recommended to first trial it on non-critical paths or internal projects, evaluate it, and welcome participation in the public test feedback. APIs may still adjust during the Alpha phase; the process towards 1.0 will aim to converge and provide migration notes.
Q8: Does it support iOS / cross-platform? A: No, ComboNative is Android-only. If you need cross-platform, consider JS / cross-platform container solutions.
十、Current Status
- Version:
0.1.0-alpha01; - Four runtime libraries published to Maven Central, packaging plugin published to Gradle Plugin Portal;
- Sample repository is open source, containing a host demo + three mini programs, covering Compose, pure logic, and NDK forms;
- Complete Chinese and English documentation (Quick Start / Host Integration / Packaging / Core API / Components / Architecture / Security / Troubleshooting / Performance and Size).
If you find issues, feel free to raise an Issue (with a minimal reproduction), or contact me through the channels at the end of the article. Your feedback will directly influence what it looks like heading towards 1.0.
十一、Early Access and Resources
The main library is not yet open source, but has been uploaded to Maven, allowing direct integration and experience. You are welcome to trial it in real projects, provide feedback on issues and needs, and help me polish it to production readiness.
- 📥 Sample App Download (Experience directly without building): https://130.94.11.104:17558/down/9yiZ75pfO4QR.apk
- 🧩 Sample Mini Program
.cbpDownloads:- Mini Program ①: https://130.94.11.104:17558/down/Yr9SIaPMT9iN.cbp
- Mini Program ②: https://130.94.11.104:17558/down/NVMqj90cn5rh.cbp
- Mini Program ③: https://130.94.11.104:17558/down/GvQU2OjLEcEJ.cbp
- 🔗 Sample Open Source Repository (Host App + three native mini programs, can be independently cloned and built): https://github.com/lnzz123/combonative-samples
- 🧩 ComboLite (Previous generation, open source, can be studied / referenced): https://github.com/lnzz123/ComboLite
- 📚 Maven Coordinates:
io.github.lnzz123:combonative-engine:0.1.0-alpha01(andabi/runtime/mini-sdk) - 🧰 Packaging Plugin (Gradle Plugin Portal):
io.github.lnzz123.combonative-packer:0.1.0-alpha01
One-click reproduction of the size data in the article:
git clone --recurse-submodules https://github.com/lnzz123/combonative-samples.git
cd combonative-samples
export ANDROID_HOME=/path/to/android-sdk
./gradlew :host-demo:assembleRelease # Host whole package
./gradlew buildAllReleaseMiniApps # Each mini program .cbp (release / R8)
ls -la build/outputs/mini-app-packages/release/*.cbp
If this 'native + multi-process + independently hardenable' approach fits your scenario, welcome to try it, raise Issues, and give a Star; if you are more interested in understanding the principles behind it, or even building your own, you are also welcome to start reading from the open-source ComboLite. Every piece of your feedback will directly influence what it looks like heading towards 1.0.
📌 All data in this article is based on
0.1.0-alpha01, release/R8 caliber measured values, and may change with version evolution; APIs may still adjust during the Alpha phase.
Top 1 from juejin.cn, machine-translated. The original thread is authoritative.
Thumbs up