一、插件安装

# IDE 插件市场(推荐)
Android Studio → Settings → Plugins → Marketplace → 搜索 "ovCompose"
// Gradle 插件配置 (settings.gradle.kts)
pluginManagement {
    repositories {
        mavenCentral()
        google()
        gradlePluginPortal()
        // 腾讯 Maven 仓库
        maven("https://mirrors.tencent.com/nexus/repository/maven-public/")
    }
}

二、创建项目

方式一:使用 IDE 模板(推荐)

  1. File → New → New Project

  2. 选择 "ovCompose Multiplatform" 模板

  3. 配置项目:

    • Project name: MyOvComposeApp

    • Package name: com.example.myapp

    • Platforms: ☑ Android ☑ iOS ☑ HarmonyOS

    • UI Framework: Compose Multiplatform (默认)

  1. 点击 Finish,等待 Gradle 同步(约 5-10 分钟)

方式二:命令行创建

# 使用 ovCompose CLI(需先安装)
npx @tencent/ovcompose-cli create myapp --platforms=android,ios,harmony

cd myapp
./gradlew clean build

三、项目结构详解

myapp/
├── composeApp/                      # 共享 Compose 代码
│   ├── src/
│   │   ├── commonMain/kotlin/      # 跨平台代码(UI + 逻辑)
│   │   │   ├── App.kt              # 应用入口
│   │   │   ├── ui/
│   │   │   │   ├── screens/        # 页面级 Composable
│   │   │   │   ├── components/     # 可复用组件
│   │   │   │   └── theme/          # 主题配置
│   │   │   ├── viewmodel/          # 共享 ViewModel
│   │   │   └── data/               # 数据层(Repository、API)
│   │   ├── androidMain/kotlin/     # Android 特定代码(极少)
│   │   ├── iosMain/kotlin/         # iOS 特定代码(桥接)
│   │   └── harmonyMain/kotlin/     # 鸿蒙特定代码(桥接)
│   └── build.gradle.kts
├── androidApp/                      # Android 壳工程
│   └── src/main/
│       └── MainActivity.kt          # 启动 ovCompose
├── iosApp/                          # iOS Xcode 工程
│   ├── iosApp.xcodeproj
│   └── ContentView.swift            # 桥接 SwiftUI
├── harmonyApp/                      # 鸿蒙 DevEco 工程
│   ├── entry/src/main/ets/
│   │   └── pages/Index.ets          # 桥接 ArkTS
│   └── build-profile.json5
└── build.gradle.kts                 # 根构建配置

四、核心开发流程

1. 配置共享模块 (composeApp/build.gradle.kts)

plugins {
    kotlin("multiplatform")
    id("org.jetbrains.compose")
    id("org.jetbrains.kotlin.plugin.compose")
    id("com.tencent.ovcompose") version "1.0.2"  // ovCompose 核心插件
}

kotlin {
    // Android 目标
    androidTarget {
        compilations.all {
            kotlinOptions {
                jvmTarget = "17"
            }
        }
    }
    
    // iOS 目标
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach { iosTarget ->
        iosTarget.binaries.framework {
            baseName = "ComposeApp"
            isStatic = true  // ovCompose 推荐静态链接
        }
    }
    
    // 鸿蒙目标(ovCompose 扩展)
    harmonyOS {
        // 使用 KuiklyBase 引擎作为渲染后端
        engine = KuiklyBaseEngine(
            // 可选:自定义渲染参数
            renderMode = RenderMode.GPU_ACCELERATED,
            enableInspector = true  // 开发时启用布局检查
        )
        
        // 鸿蒙 API 级别
        compileSdk = 5
        minSdk = 5
    }

    sourceSets {
        val commonMain by getting {
            dependencies {
                // Compose 基础
                implementation(compose.runtime)
                implementation(compose.foundation)
                implementation(compose.material3)  // Material Design 3
                implementation(compose.ui)
                implementation(compose.components.resources)
                implementation(compose.components.uiToolingPreview)
                
                // ovCompose 鸿蒙适配库
                implementation("com.tencent.ovcompose:core:1.0.2")
                implementation("com.tencent.ovcompose:navigation:1.0.2")
                implementation("com.tencent.ovcompose:material3-harmony:1.0.2")
                
                // KMP 生态
                implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1")
                implementation("io.ktor:ktor-client-core:3.1.0")
                implementation("media.kamel:kamel-image:1.0.3")  // 图片加载
            }
        }
        
        val harmonyMain by getting {
            dependencies {
                // 鸿蒙特定依赖
                implementation("com.tencent.ovcompose:harmony-bridge:1.0.2")
            }
        }
    }
}

// ovCompose 专用配置
ovCompose {
    // 鸿蒙资源映射
    harmonyResources {
        copyFrom(commonMain.resources)
        // 自动转换 Compose 资源为鸿蒙资源格式
        autoConvertSvg = true
    }
    
    // 性能优化
    buildOptimization {
        enableR8ForHarmony = true  // 鸿蒙端代码压缩
        shrinkSkia = true          // 裁剪 Skia 库(减少 3-5MB)
    }
}

2. 编写跨平台 UI 入口

// composeApp/src/commonMain/kotlin/App.kt
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.*
import com.tencent.ovcompose.navigation.OvNavHost
import com.tencent.ovcompose.navigation.rememberOvNavigator

@Composable
fun App() {
    // ovCompose 提供的跨平台导航器
    val navigator = rememberOvNavigator()
    
    MaterialTheme(
        colorScheme = if (isSystemInDarkTheme()) darkColorScheme() else lightColorScheme()
    ) {
        OvNavHost(
            navigator = navigator,
            startDestination = "/home"
        ) {
            // 定义路由
            composable("/home") { HomeScreen() }
            composable("/user/{id}") { backStackEntry ->
                val userId = backStackEntry.arguments?.getString("id")!!
                UserDetailScreen(userId)
            }
            composable("/settings") { SettingsScreen() }
        }
    }
}

3. 开发具体页面

// composeApp/src/commonMain/kotlin/ui/screens/HomeScreen.kt
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.tencent.ovcompose.ui.pullrefresh.*

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeScreen(
    viewModel: HomeViewModel = koinViewModel()
) {
    val uiState by viewModel.uiState.collectAsState()
    val pullRefreshState = rememberPullRefreshState(
        refreshing = uiState.isLoading,
        onRefresh = { viewModel.refresh() }
    )
    
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("ovCompose 示例") },
                actions = {
                    IconButton(onClick = { /* 搜索 */ }) {
                        Icon(Icons.Default.Search, contentDescription = "搜索")
                    }
                }
            )
        },
        floatingActionButton = {
            ExtendedFloatingActionButton(
                onClick = { /* 新建 */ },
                icon = { Icon(Icons.Default.Add, null) },
                text = { Text("新建") }
            )
        }
    ) { padding ->
        Box(
            modifier = Modifier
                .padding(padding)
                .pullRefresh(pullRefreshState)
        ) {
            LazyColumn(
                modifier = Modifier.fillMaxSize(),
                contentPadding = PaddingValues(16.dp),
                verticalArrangement = Arrangement.spacedBy(12.dp)
            ) {
                items(
                    items = uiState.items,
                    key = { it.id }
                ) { item ->
                    ItemCard(
                        item = item,
                        onClick = { /* 点击处理 */ }
                    )
                }
            }
            
            // 下拉刷新指示器(ovCompose 已适配鸿蒙)
            PullRefreshIndicator(
                refreshing = uiState.isLoading,
                state = pullRefreshState,
                modifier = Modifier.align(Alignment.TopCenter)
            )
        }
    }
}

@Composable
private fun ItemCard(
    item: Item,
    onClick: () -> Unit
) {
    Card(
        onClick = onClick,
        modifier = Modifier.fillMaxWidth()
    ) {
        Row(
            modifier = Modifier
                .padding(16.dp)
                .fillMaxWidth(),
            horizontalArrangement = Arrangement.spacedBy(12.dp)
        ) {
            // 异步图片(ovCompose 自动适配鸿蒙图片加载)
            AsyncImage(
                model = item.imageUrl,
                contentDescription = null,
                modifier = Modifier.size(64.dp),
                placeholder = painterResource(Res.drawable.placeholder)
            )
            
            Column {
                Text(
                    text = item.title,
                    style = MaterialTheme.typography.titleMedium
                )
                Text(
                    text = item.description,
                    style = MaterialTheme.typography.bodyMedium,
                    color = MaterialTheme.colorScheme.onSurfaceVariant
                )
            }
        }
    }
}

4. 编写 ViewModel(共享逻辑层)

// composeApp/src/commonMain/kotlin/viewmodel/HomeViewModel.kt
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch

class HomeViewModel(
    private val repository: ItemRepository
) : ViewModel() {
    
    private val _uiState = MutableStateFlow(HomeUiState())
    val uiState: StateFlow<HomeUiState> = _uiState.asStateFlow()
    
    init {
        loadData()
    }
    
    fun refresh() {
        loadData(forceRefresh = true)
    }
    
    private fun loadData(forceRefresh: Boolean = false) {
        viewModelScope.launch {
            _uiState.update { it.copy(isLoading = true) }
            
            try {
                val items = repository.getItems(forceRefresh)
                _uiState.update {
                    it.copy(
                        items = items,
                        isLoading = false,
                        error = null
                    )
                }
            } catch (e: Exception) {
                _uiState.update {
                    it.copy(
                        isLoading = false,
                        error = e.message
                    )
                }
            }
        }
    }
}

data class HomeUiState(
    val items: List<Item> = emptyList(),
    val isLoading: Boolean = false,
    val error: String? = null
)

5. 平台端桥接代码

Android 端
// androidApp/src/main/java/MainActivity.kt
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import com.example.myapp.composeApp.App

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        
        // 标准 CMP 启动方式
        setContent {
            App()
        }
    }
}
iOS 端(Swift 桥接)
// iosApp/iosApp/ContentView.swift
import SwiftUI
import ComposeApp

struct ContentView: View {
    var body: some View {
        // ovCompose 提供的 UIViewController 包装
        OvComposeViewControllerRepresentable {
            // 可配置启动参数
            initialRoute: "/home"
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
鸿蒙端(ArkTS 桥接)
// harmonyApp/entry/src/main/ets/pages/Index.ets
import { OvComposeEngine } from '@tencent/ovcompose-harmony'
import { router } from '@kit.ArkUI'

@Entry
@Component
struct Index {
  @State engineReady: boolean = false
  
  aboutToAppear() {
    // 初始化 ovCompose 引擎
    // 内部复用 KuiklyBase 运行时
    OvComposeEngine.getInstance().initialize({
      // 指向 ComposeApp 模块
      moduleName: "ComposeApp",
      entryPoint: "AppKt.App",
      
      // 生命周期绑定
      lifecycleOwner: this,
      
      // 性能配置
      renderConfig: {
        useGPU: true,
        enableVSync: true,
        maxFPS: 120
      },
      
      // 调试配置(开发阶段)
      debugConfig: {
        enableLayoutInspector: true,
        showPerformanceOverlay: false
      },
      
      // 初始化完成回调
      onReady: () => {
        this.engineReady = true
      }
    })
  }

  aboutToDisappear() {
    // 释放引擎资源
    OvComposeEngine.getInstance().destroy()
  }

  build() {
    Stack() {
      if (this.engineReady) {
        // ovCompose 渲染视图组件
        OvComposeView()
          .width('100%')
          .height('100%')
          // 鸿蒙系统手势配置
          .gesture(
            GestureGroup(
              GestureMode.Parallel,
              SwipeGesture({ direction: SwipeDirection.Left })
                .onAction((event) => {
                  // 处理侧滑返回
                  router.back()
                })
            )
          )
      } else {
        // 加载中
        LoadingProgress()
          .width(50)
          .height(50)
          .color(Color.Blue)
      }
    }
    .width('100%')
    .height('100%')
  }
}

五、鸿蒙特定功能适配

1. 调用鸿蒙原生 API(expect/actual)

// commonMain - 声明期望
expect class PlatformVibrator {
    fun vibrate(durationMs: Long)
    fun cancel()
}

// harmonyMain - 鸿蒙实现
import ohos.vibrator.vibrator
import ohos.sensor.agent.CategoryOrientation

actual class PlatformVibrator actual constructor() {
    actual fun vibrate(durationMs: Long) {
        // 调用鸿蒙系统振动 API
        vibrator.start({
            type: "time",
            duration: durationMs
        })
    }
    
    actual fun cancel() {
        vibrator.stop()
    }
}

2. 处理鸿蒙生命周期

// harmonyMain/kotlin/HarmonyLifecycle.kt
import com.tencent.ovcompose.harmony.lifecycle.*

@Composable
fun rememberHarmonyLifecycle(): HarmonyLifecycleOwner {
    val lifecycle = rememberHarmonyLifecycleOwner()
    
    DisposableEffect(lifecycle) {
        // 监听鸿蒙页面生命周期
        val observer = HarmonyLifecycleEventObserver { event ->
            when (event) {
                HarmonyLifecycle.Event.ON_SHOW -> { /* 页面显示 */ }
                HarmonyLifecycle.Event.ON_HIDE -> { /* 页面隐藏 */ }
                HarmonyLifecycle.Event.ON_DESTROY -> { /* 页面销毁 */ }
            }
        }
        
        lifecycle.addObserver(observer)
        onDispose {
            lifecycle.removeObserver(observer)
        }
    }
    
    return lifecycle
}

六、构建与运行

1. 同步项目

./gradlew clean :composeApp:generateHarmonySources
./gradlew build

2. 运行各端

# Android
./gradlew :androidApp:installDebug

# iOS(需 Xcode 15+)
./gradlew :composeApp:assembleIOSBinaries
# 然后在 Xcode 中打开 iosApp.xcodeproj 运行

# 鸿蒙(需 DevEco Studio 5.0+)
./gradlew :composeApp:assembleHarmonyRelease
# 使用 DevEco Studio 打开 harmonyApp 目录,点击 Run

3. 鸿蒙真机/模拟器调试

# 安装 HAP 包到鸿蒙设备
hdc install entry/build/outputs/hap/release/entry-release-signed.hap

# 查看日志(类似 adb logcat)
hdc hilog | grep ovCompose

七、性能优化建议

优化项

配置

效果

Skia 裁剪

shrinkSkia = true

包体积 -3~5MB

R8 压缩

enableR8ForHarmony = true

代码体积 -30%

图片格式

使用鸿蒙 HSP 格式

内存占用 -20%

懒加载

LazyColumn + key

滚动流畅度提升

状态管理

使用 rememberSaveable

页面恢复速度优化

八、常见问题排查

问题

原因

解决

鸿蒙端白屏

Skia 初始化失败

检查 GPU 加速配置,模拟器需关闭 GPU

中文显示方框

字体未适配

添加 implementation("com.tencent.ovcompose:harmony-fonts:1.0.2")

网络请求失败

鸿蒙权限未配置

在 module.json5 中添加 INTERNET 权限

路由跳转卡顿

主线程阻塞

使用 withContext(Dispatchers.Default) 处理数据

内存泄漏

生命周期未绑定

确保使用 rememberHarmonyLifecycleOwner()

总结

ovCompose 的开发体验与标准 Compose Multiplatform 几乎一致,主要差异在于:

配置层:添加 harmonyOS { } 目标和 ovCompose 插件

桥接层:鸿蒙端使用 ArkTS 初始化 OvComposeEngine

运行时:底层复用 KuiklyBase 的鸿蒙原生渲染能力

对于熟悉 CMP 的开发者,迁移成本极低 —— 主要是增加鸿蒙构建配置和桥接代码,业务逻辑和 UI 代码完全复用。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐