How Alipay’s MyTab Mastered Three‑Platform One‑Code Refactor Using KMP
This article details Alipay’s technical journey of refactoring the MyTab feature across Android, iOS, and HarmonyOS using Kotlin Multiplatform, covering architectural shifts, performance bottlenecks, stability fixes, and the resulting gains in code reuse and development efficiency.
Background and Motivation
HarmonyOS introduced a three‑platform mobile ecosystem (Android, iOS, HarmonyOS). To simplify cross‑platform maintenance, Alipay’s terminal technology team selected Kotlin Multiplatform (KMP) as a single‑code solution for the MyTab feature and began a pilot migration.
Project Refactor Overview
The team adopted Jetpack Compose as the UI framework. Compose’s state‑driven, strong MVVM paradigm aligns with developers familiar with Vue, Flutter, or SwiftUI, but required a shift from the legacy MVC codebase.
Business Logic Analysis
MyTab’s functionality consists of two tightly coupled concerns: product shape (business rules) and visual presentation. Over many releases, the code accumulated version‑specific skin logic, large‑font adaptations, and interference between old and new UI paths.
Skin logic added special cases for particular versions.
Merchant version introduced large‑font adaptations.
Legacy and new UI logic overlapped, causing mutual interference.
Design‑Pattern Refactor
The original MVC layers had blurred boundaries, reducing maintainability. Switching to Compose’s state‑driven MVVM provided two key benefits:
Strict data‑UI separation: UI components render only from immutable State objects, preventing accidental use of model state.
State‑driven UI: The UI is a deterministic visual mapping of the current state, guaranteeing consistent rendering regardless of navigation path.
Compose Business‑Model Abstraction
A dedicated ViewModel layer was introduced to host shared view logic. Version‑specific adaptations were moved to the Model layer (business logic) and the View layer (UI specifics). This decoupling shifted most core logic into ViewModel, dramatically improving maintainability.
Challenges and Solutions
1. Frame‑Rate Drop
iPhone devices support a 120 Hz ProMotion refresh rate, while the KMP build initially rendered at 60 Hz, causing noticeable lag. Enabling the high‑refresh mode restored smooth scrolling.
Performance degradation was also caused by excessive recomposition of large @Composable functions. Two mitigation strategies were applied:
Split monolithic @Composable functions into smaller units, especially extracting code that does not depend on ScrollableState.
Prevent hidden over‑recomposition by ensuring only the composables that observe mutable State are recomposed.
Example illustrating a subtle over‑recomposition problem:
class MyModel() {
var field1: Int = 0;
}
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(text = "Hello $name!", modifier = modifier)
val model = remember { MyModel() }
val count = remember { mutableStateOf(0) }
Column(modifier = Modifier.padding(top = 100.dp)) {
Button(onClick = { count.value++ }) { Text("Button") }
SubItem(lambdaParam = { print("$model") })
SubItem1(count.value)
}
}Observations: SubItem1 recomposes normally because it reads the changing count state. SubItem also recomposes even though it does not read count; its lambda captures an unstable variable, causing hidden recomposition.
2. Stability Issues
Snapshot Exception : Occurred when background threads modified State during a recomposition cycle. The fix was to confine all State updates to the main thread and audit asynchronous writes.
Platform‑API ANR on HarmonyOS : A deadlock arose from Kotlin‑Native’s global lock on Companion object initialization combined with JS‑runtime calls via runBlocking. The lock chain caused circular waiting, freezing the main thread.
Resolution steps:
Avoid invoking platform APIs during Companion object initialization.
Wrap all platform calls in suspend functions and never use runBlocking on background threads.
Refactor Benefits
After addressing the above challenges, the three‑platform KMP version of MyTab was fully released, deprecating the legacy codebase. A single shared codebase now powers Android, iOS, and HarmonyOS, delivering higher development efficiency, consistent business logic, and improved code quality.
Future Outlook
Remaining issues such as occasional rendering failures on specific ROMs and rare Metal pipeline blocks indicate further polishing is required before the framework can be considered a “super‑app” development platform. The team plans to expand KMP adoption and share the learned practices with the broader community.
References
Android official documentation on Compose stability: https://developer.android.com/develop/ui/compose/performance/stability
Alipay Experience Technology
Exploring ultimate user experience and best engineering practices
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.
