Android 返回一个文件路径,HarmonyOS 却返回一个 URI,插件到底该返回什么?

文章封面

前两篇只有通信壳子。Dart 能调 ArkTS 了,但调什么?真正的业务逻辑还没接。

这篇接一个真正的系统能力,比如文件选择。Android 有文件选择,iOS 有文件选择,HarmonyOS 也有。但它们的 API 长得很像,用起来却完全不一样。

这时候才意识到:翻译 API 不等于迁移插件。

一、先想清楚:API 名字像,语义就一样吗

先找一个看起来很像的 API:文件选择。

Android:返回文件路径 String。
iOS:返回文件 URL。
HarmonyOS:返回文件 URI。

平台返回值
Android/data/file.txt
iOSfile:///data/file.txt
HarmonyOSfile://com.example.app/data/file.txt

看起来都是"文件位置",但格式完全不一样。Dart 层怎么统一?

二、能力映射不是语法翻译

很多人以为:Android 的 API 名换成 HarmonyOS 的 API 名,参数对应一下,就完了。

不对。API 名字相似,不代表语义一样。

对比AndroidHarmonyOS
文件选择ACTION_OPEN_DOCUMENTfilePicker
权限READ_EXTERNAL_STORAGEohos.permission.READ_FILE
返回值UriURI
生命周期Activity 结果回调Want 结果

名字都叫"文件选择",但实现细节、权限、返回值、回调方式全不一样。

三、插件层要做语义对齐

插件层的工作不是"翻译",是"对齐"。

Dart 层给用户一个统一的接口:选文件,返回文件路径。

插件层在 HarmonyOS 这边:调用 HarmonyOS 的文件选择 API,拿到 URI,转成 Dart 层要的格式,返回。

这段代码解决什么问题: 文件选择插件实现。
文件: ohos/src/main/ets/FilePickerPlugin.ets
用途: 文件选择能力
接入位置: 插件实现

import { filePicker } from '@kit.CoreFileKit';

class FilePickerPlugin {
  async pickFile(): Promise<string> {
    // 调用 HarmonyOS 文件选择
    let options = new filePicker.FileSelectOptions();
    options.fileType = ['document'];
    
    let result = await filePicker.open(options);
    
    // 拿到 URI,转成 Dart 层要的格式
    let uri = result.result[0].uri;
    return uri;  // 返回 file://... 格式
  }
}

能力映射图

四、错误码为什么要统一

不同平台的错误码也不一样。Android 有 Android 的错误码,HarmonyOS 有 HarmonyOS 的错误码。

Dart 层用户怎么知道出了什么错?插件层要统一错误码。

错误Android 码HarmonyOS 码统一码
用户取消CANCELEDUSER_CANCELEDcanceled
文件不存在NOT_FOUNDFILE_NOT_EXISTnot_found
权限拒绝PERMISSION_DENIEDPERMISSION_DENIEDpermission_denied

插件层把各平台的错误码,映射成 Dart 层统一的错误码。

五、几个容易踩的坑

第一个坑:Android 返回 path,HarmonyOS 却返回 URI。Dart 层用户拿到的格式不一样。

第二个坑:不同平台错误码语义不同。用户不知道为什么出错。

第三个坑:Dart API 为了兼容旧平台写死数据结构。新平台的新能力用不上。

第四个坑:HarmonyOS 特有能力被最低公共能力限制。为了兼容,把 HarmonyOS 的新特性砍了。

第五个坑:原生对象直接塞给 Channel。原生对象不能序列化,传输报错。

测试效果图

这次做能力接入最大的体会是:插件移植不是"API 翻译",是"语义对齐"。

真正做的时候,最容易忽略的不是怎么调 API,而是怎么把不同平台的差异藏在插件层里,给 Dart 层用户一个统一的接口。

Logo

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

更多推荐