在这里插入图片描述

摘要:一个为 Android 和 iOS 设计的三方库,硬搬进鸿蒙到底值不值得?本文记录了我把 Kotlin Multiplatform 生态里的好库适配到 ArkTS 的这趟心路:适配不是逐行翻译,而是在语义层不动、在引擎层搭桥。文中以网络库 net-lite 为例,给出公开接口定义与 ArkTS 侧引擎实现的完整代码,说明"语义不变、引擎替换"的关键点,也聊了 ArkTS 严格模式下那些看不见的规矩,以及开源协作让这件事不再是一个人的孤军。

目录

一、深夜的那行红色报错

做跨平台开发这几年,我越来越多地把时间花在一件"搬东西"的事上——把那些为别处设计的好库,搬进鸿蒙的房间。这篇心迹,想说说这趟搬运里,那些卡住又通了的瞬间。

深夜,IDE 里又跳出一行红色报错。我盯着 ArkTS 严格模式给的那句提示,第无数次问自己:把一个为 Android 和 iOS 设计的三方库,硬搬进鸿蒙,到底值不值得。

二、为什么非要自己搬

我日常和 Kotlin Multiplatform、Compose Multiplatform 打交道。这套工具链最迷人的地方,是"一次书写,多端运行"。可当它遇见鸿蒙,遇见 ArkTS 这门更克制、更讲规矩的语言,很多原本顺滑的假设突然就卡住了。

社区里明明有现成的好库——管网络的、管状态的、发本地通知的——它们只是没为鸿蒙准备原生实现。等官方适配?不如自己上手。

三、适配不是翻译,是搭桥

慢慢摸到门道之后,我发现适配不是把代码逐行翻译过去,而是在语义层不动、在引擎层搭一座桥。

库的公开接口是真的接口,我只要在 ArkTS 这边把"引擎"重新实现一遍,让它去调用系统真实的 API:

  • 网络交给系统网络栈;
  • 通知交给 NotificationKit;
  • 状态交给 ArkTS 自己的响应式机制。

这就像给一位只会说外语的客人,配一个懂本地话的翻译——客人要表达的意思不变,落地的口音换了。

库的公开接口(语义不变)

ArkTS 侧引擎实现

系统网络栈

NotificationKit

ArkTS 响应式机制

光说"搭桥"还是太抽象,拿一个网络库来落地会更清楚。假设社区里有个叫 net-lite 的库,它对外只承诺三件事:发请求、拿响应、出错时给个明确的异常。这三件事就是"语义",无论底层是 OkHttp 还是鸿蒙的系统网络栈,调用方看到的都该是同一副面孔。

先看库的公开接口定义——这部分几乎原样保留,只把 Kotlin 的写法换成 ArkTS 能接受的显式声明:

// 库的公开接口:语义层,跨平台保持一致
// 关键点一:接口签名不变,调用方无需感知底层实现

export interface NetRequest {
  url: string;
  method: string;
  headers: Record<string, string>;
  body?: string;
}

export interface NetResponse {
  status: number;
  headers: Record<string, string>;
  body: string;
}

export interface NetError {
  code: number;
  message: string;
}

// 对外暴露的唯一入口,语义与 Kotlin 版本完全一致
export interface NetClient {
  request(req: NetRequest): Promise<NetResponse>;
}

接着是 ArkTS 侧的引擎实现。这里才是真正"换口音"的地方:接口没动,但内部把请求交给了鸿蒙的系统网络栈 @ohos.net.http。

// ArkTS 侧引擎实现:引擎层,替换为鸿蒙系统网络栈
// 关键点二:只替换引擎,不改语义

import http from '@ohos.net.http';
import { BusinessError } from '@ohos.base';
import { NetClient, NetRequest, NetResponse, NetError } from './NetTypes';

export class HarmonyNetClient implements NetClient {
  // 语义不变:入参 NetRequest,出参 Promise<NetResponse>
  async request(req: NetRequest): Promise<NetResponse> {
    const httpRequest = http.createHttp();

    try {
      // 引擎替换:真正干活的是系统网络栈,而不是 OkHttp
      const resp = await httpRequest.request(req.url, {
        method: this.mapMethod(req.method),
        header: req.headers,
        extraData: req.body,
        connectTimeout: 10000,
        readTimeout: 10000
      });

      // 把系统返回的原始结构,翻译回库约定的 NetResponse
      return {
        status: resp.responseCode as number,
        headers: resp.header as Record<string, string>,
        body: resp.result as string
      };
    } catch (e) {
      // 语义不变:错误也统一收敛成 NetError,调用方无需区分平台
      const err = e as BusinessError;
      const netError: NetError = {
        code: err.code,
        message: err.message
      };
      throw netError;
    } finally {
      // 系统资源要显式释放,这是 ArkTS 侧多出来的一步
      httpRequest.destroy();
    }
  }

  // 方法名映射:库的语义 -> 系统枚举,属于引擎层的适配细节
  private mapMethod(method: string): http.RequestMethod {
    switch (method.toUpperCase()) {
      case 'POST':
        return http.RequestMethod.POST;
      case 'PUT':
        return http.RequestMethod.PUT;
      case 'DELETE':
        return http.RequestMethod.DELETE;
      default:
        return http.RequestMethod.GET;
    }
  }
}

把这段代码和上面的流程图对照着看,桥的两端就很清楚了:

  • 语义不变:NetClient、NetRequest、NetResponse 这些类型一个没改,业务代码里 client.request(...) 的写法在 Kotlin 和 ArkTS 上完全一样;
  • 引擎替换:HarmonyNetClient 内部换成 @ohos.net.http,请求真正落到系统网络栈;
  • 适配细节收敛在引擎层:方法名映射、超时参数、destroy() 释放资源,这些平台差异全被关在实现类里,不会漏到调用方。

这就是我说的"搭桥":桥面(接口)保持原样,桥墩(引擎)按当地的地基重新打。换一座桥,路还是那条路。

四、难的是那些看不见的规矩

难的是那些看不见的规矩。ArkTS 严格模式下,对象字面量不能随手当类型用,未知类型被直接禁掉,构造参数属性声明也不被允许。一行在 Kotlin 里天经地义的代码,常常要拆成具名接口、改写成显式写法。

有那么几个晚上,我和一个图片解码的字节对齐问题较劲到凌晨,最后发现只是少做了一次 buffer 的切片。那种挫败,和忽然通了的爽快,中间只隔着一行代码。

五、房间亮了

当 demo 工程终于在模拟器上跑出 BUILD SUCCESSFUL,当那几个适配过的库真正在鸿蒙设备上响应点击、弹出通知、拉回数据,我忽然觉得这间房间亮了。

开源世界那么大,而我往鸿蒙的房间里,又搬进了一件趁手的家具。

六、这件事不该是一个人的孤军

最让我安心的是,这件事不该是一个人的孤军。我把适配的代码、踩过的坑、写下的说明,都公开在 AtomGit 上。有人复用,有人指正,有人顺着我的路接着走。

鸿蒙的生态,就是这样一个个被搬进来的房间,慢慢连成的社区。

七、总结

回头看这趟搬运,真正难的从来不是把代码从一种语言抄成另一种语言,而是想清楚哪些东西必须原样保留、哪些东西可以彻底换掉。

  • 语义是桥面,必须稳:库的公开接口、类型签名、错误约定,这些是调用方唯一能看见的东西,跨平台就该保持一致。NetClient 在 Kotlin 和 ArkTS 上是同一副面孔,业务代码才不用为平台写两套。
  • 引擎是桥墩,可以换:底层是 OkHttp 还是 @ohos.net.http,调用方并不关心。把请求交给系统网络栈、把通知交给 NotificationKit、把状态交给 ArkTS 的响应式机制,换的是地基,不是路。
  • 差异要收敛在实现层:方法名映射、超时参数、destroy() 释放资源,这些平台特有的细节全部关在引擎实现类里,一旦漏到调用方,桥就白搭了。
  • 规矩要提前摸清:ArkTS 严格模式禁掉了对象字面量当类型、未知类型、构造参数属性声明这些写法,早点把接口拆成具名声明,比事后返工省力得多。
  • 别一个人扛:适配的代码、踩过的坑、写下的说明公开在 AtomGit 上,有人复用、有人指正,房间才会一间间连成社区。

如果你手上也有一个想搬进鸿蒙的库,不妨先从它的公开接口看起——把语义钉死,再去实现引擎。桥搭好了,路自然就通了。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐