【KMP for OpenHarmony】-- 在鸿蒙工程中使用 Apollo-Kotlin

02 · 在鸿蒙工程中使用 Apollo-Kotlin

本文讲怎么用 :把适配好的 Apollo 引入你的 KMP 鸿蒙工程并发起请求。

假设你已按 01 · 适配 完成适配,或直接使用本文给出的坐标。

一、引入依赖

1.1 配置仓库

settings.gradle.kts 里两个 repositories 块都要加 (模板里 mavenLocal() 默认是注释掉的):

kotlin 复制代码
pluginManagement {
    repositories {
        mavenLocal()                                                      // ← 适配产物在这里
        maven("https://maven.eazytec-cloud.com/nexus/repository/maven-public/")
        gradlePluginPortal()
    }
}

dependencyResolutionManagement {
    repositories {
        mavenLocal()                                                      // ← 别忘了这个
        maven("https://maven.eazytec-cloud.com/nexus/repository/maven-public/")
    }
}

漏掉任何一处都会报 Could not resolve com.apollographql.apollo:...。

1.2 声明依赖

关键:必须放在 ohosMain,不能放 commonMain。

kotlin 复制代码
kotlin {
    sourceSets {
        commonMain.dependencies {
            implementation(compose.runtime)
            implementation(compose.foundation)
            implementation(compose.material3)
            // ❌ 不要在这里加 apollo!
        }

        val ohosMain = sourceSets.create("ohosMain").apply {
            dependsOn(commonMain.get())
        }
        ohosMain.dependencies {
            api(libs.compose.multiplatform.export)
            // ✅ 正确位置
            implementation("com.apollographql.apollo:apollo-runtime:4.3.0")
            implementation("com.apollographql.apollo:apollo-normalized-cache:4.3.0")
            implementation("com.apollographql.apollo:apollo-adapters:4.3.0")
        }
    }
}

为什么不能放 commonMain?

因为你的工程通常同时有 androidTarget,而 commonMain 必须对所有 目标成立。

Apollo 的适配产物只发布了 ohosArm64 变体,Android 侧没有 → 解析失败:

复制代码
The dependency should target platforms: [android, android, ohosArm64]
Unresolved platforms: [android, android]

判断标准:这个依赖有没有覆盖工程的所有 target?

  • 只有 ohos → 放 ohosMain
  • 全平台都有(如 kotlinx-coroutines)→ 才放 commonMain

1.3 桥接(如果 UI 在 commonMain)

如果 commonMain 里的 UI 要调用 Apollo,用 expect/actual 桥接:

kotlin 复制代码
// commonMain ------ 只声明能力
expect fun runApolloDemo(): String

// ohosMain ------ 真正调用 Apollo
actual fun runApolloDemo(): String {
    val engine = DefaultHttpEngine(timeoutMillis = 15_000)
    // ...
}

// androidMain ------ 占位(保证 commonMain 能解析)
actual fun runApolloDemo(): String = "该演示仅在 HarmonyOS 启用"

二、基础用法

2.1 发一个 HTTP 请求(用 ohos 引擎)

kotlin 复制代码
import com.apollographql.apollo.network.http.DefaultHttpEngine
import com.apollographql.apollo.network.http.get
import kotlinx.coroutines.runBlocking

fun fetchSomething(): String = runBlocking {
    val engine = DefaultHttpEngine(timeoutMillis = 15_000)
    try {
        val response = engine.get("https://jsonplaceholder.typicode.com/todos/1").execute()
        val body = response.body?.readUtf8() ?: "(empty)"
        "HTTP ${response.statusCode}\n$body"
    } finally {
        engine.close()   // 必须关闭,释放连接池
    }
}

DefaultHttpEngine() 就是我们在适配时新写的 ktor/Curl 实现(CIO 在 Kotlin/Native 上不支持 TLS)。

2.2 构造 ApolloClient

kotlin 复制代码
import com.apollographql.apollo.ApolloClient

val client = ApolloClient.Builder()
    .serverUrl("https://your-graphql-endpoint/graphql")
    .build()

如果要配拦截器、缓存等:

kotlin 复制代码
val client = ApolloClient.Builder()
    .serverUrl("https://your-endpoint/graphql")
    .normalizedCache(MemoryCacheFactory(maxSizeBytes = 10 * 1024 * 1024))
    .build()

normalizedCache 需要引入 apollo-normalized-cache(已在 1.2 中声明)。

2.3 用 BigDecimal 适配器

GraphQL 没有原生的十进制类型,Apollo 提供了 BigDecimal 适配器:

kotlin 复制代码
import com.apollographql.apollo.adapter.BigDecimal

val price = BigDecimal("12345.678")
val tax = BigDecimal("1000.322")
val total = price.add(tax)          // 13346.000
val doubled = price.multiply(BigDecimal("2"))   // 24691.356

鸿蒙上这是纯 Kotlin 实现(unscaled + scale 十进制表示),精度可靠:

kotlin 复制代码
println("$price + $tax = $total")     // 12345.678 + 1000.322 = 13346.000
println("$price * 2 = $doubled")      // 12345.678 * 2 = 24691.356

2.4 时间戳

kotlin 复制代码
import com.apollographql.apollo.mpp.currentTimeMillis

val ts = currentTimeMillis()   // 毫秒时间戳,POSIX clock_gettime 实现

三、完整可跑示例

下面这段代码在 KMPCMPOHOS 的 ohosMain 里,点按钮即可运行:

kotlin 复制代码
package com.goway.kmpcmpohos

import com.apollographql.apollo.ApolloClient
import com.apollographql.apollo.adapter.BigDecimal
import com.apollographql.apollo.mpp.currentTimeMillis
import com.apollographql.apollo.network.http.DefaultHttpEngine
import com.apollographql.apollo.network.http.get
import kotlinx.coroutines.runBlocking
import okio.IOException

actual fun runApolloDemo(): String = buildString {
    // ① mpp-utils 的 ohos actual
    appendLine("① currentTimeMillis() = ${currentTimeMillis()}")

    // ② adapters 的 ohos BigDecimal
    val a = BigDecimal("12345.678")
    val b = BigDecimal("1000.322")
    appendLine("② BigDecimal: $a + $b = ${a.add(b)} ; $a * 2 = ${a.multiply(BigDecimal("2"))}")

    // ③ runtime 可构建
    appendLine(runCatching {
        ApolloClient.Builder().serverUrl("https://jsonplaceholder.typicode.com/todos/1").build()
        "③ ApolloClient 构建成功"
    }.getOrElse { "③ ApolloClient 构建失败:${it.message}" })

    // ④ 真实 HTTP 请求
    append(httpEngineDemo())
}

private fun httpEngineDemo(url: String = "https://jsonplaceholder.typicode.com/todos/1"): String = try {
    runBlocking {
        val engine = DefaultHttpEngine(timeoutMillis = 15_000)
        try {
            val r = engine.get(url).execute()
            "④ HTTP ${r.statusCode} ← $url\n${(r.body?.readUtf8() ?: "").take(280)}"
        } finally {
            engine.close()
        }
    }
} catch (e: IOException) {
    "④ 网络异常(引擎已正常构建):${e.message}"
} catch (e: Throwable) {
    "④ 请求失败:${e::class.simpleName}: ${e.message}"
}

UI 侧(commonMain):

kotlin 复制代码
@Composable
private fun ApolloDemoCard() {
    var result by remember { mutableStateOf<String?>(null) }
    var running by remember { mutableStateOf(false) }
    val scope = rememberCoroutineScope()

    Card(modifier = Modifier.fillMaxWidth()) {
        Column(modifier = Modifier.padding(12.dp)) {
            Text("Apollo-Kotlin(清单 #1)", style = MaterialTheme.typography.titleMedium)
            Button(
                enabled = !running,
                onClick = {
                    running = true
                    // 不要在 onClick 里直接 runBlocking:网络请求会阻塞 UI 线程,
                    // 触发 APP_INPUT_BLOCK / THREAD_BLOCK_3S,应用被系统强杀(闪退)。
                    scope.launch {
                        val text = withContext(Dispatchers.Default) {
                            runCatching { runApolloDemo() }
                                .getOrElse { "异常:${it.message}" }
                        }
                        result = text
                        running = false
                    }
                },
            ) { Text(if (running) "运行中..." else "运行 Apollo 演示") }
            result?.let { Text(it, style = MaterialTheme.typography.bodySmall) }
        }
    }
}

四、注意事项

项 说明
依赖位置 必须在 ohosMain,放 commonMain 会因 Android 缺变体而失败
mavenLocal() settings.gradle.kts 两处都要开
网络权限 module.json5 需声明 ohos.permission.INTERNET
HTTPS 鸿蒙默认校验证书;自签证书需额外配置
engine.close() 用完必须关,否则连接池泄漏
协程 execute() 是 suspend,需在协程中调用;禁止在 UI 线程 runBlocking,否则输入阻塞会被系统强杀

加网络权限

harmonyApp/entry/src/main/module.json5:

json5 复制代码
{
  "module": {
    "requestPermissions": [
      { "name": "ohos.permission.INTERNET" }
    ]
  }
}

不加会报 Network is unreachable 或直接抛异常。

下一篇:03 · 排障 ------ 常见报错速查。

相关推荐
正在走向自律4 小时前
HarmonyOS 手机/平板远程控制 Windows 电脑实践指南
智能手机·电脑·harmonyos·todesk·远程工具
释厄6234 小时前
AI 军备竞赛悖论——为 AI 卖游戏但 AI 需要游戏母场景
人工智能·游戏·kotlin
熊猫钓鱼>_>5 小时前
鸿蒙 PC 三方库适配复盘:libfvad 1.0 —— 把 WebRTC VAD 做成能装进采集链路的静态库
服务器·开发语言·华为·harmonyos·openharmony·适配·鸿蒙pc
aqi006 小时前
AI编程让鸿蒙开发电子书APP更加快捷
android·华为·harmonyos·鸿蒙·harmony
D e re k ZH6 小时前
保序回归:从 PAVA 到可并堆的单调约束优化
c++·算法·数据挖掘·回归·kotlin
熊猫钓鱼>_>6 小时前
鸿蒙PC高分屏与键鼠交互适配:DPI缩放、右键菜单一次讲清
开发语言·笔记·华为·ai编程·harmonyos·鸿蒙·鸿蒙心迹
Goway_Hui7 小时前
【KMP for OpenHarmony】Cache4k 0.14.0 鸿蒙适配
华为·kotlin·harmonyos
SZYSK8 小时前
OpenHarmony 上读卡、推理和双屏刷新抢 CPU:核验机与消费机的调度差
物联网·算法·harmonyos·1024程序员节