|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: "Flutter 插件适配鸿蒙:阿里云 HTTPDNS SDK 集成实践" |
| 4 | +date: 2026-01-04 11:00:00 +0800 |
| 5 | +categories: flutter harmonyos |
| 6 | +tags: flutter harmonyos httpdns aliyun plugin |
| 7 | +--- |
| 8 | + |
| 9 | +随着 HarmonyOS NEXT 的发展,越来越多的 Flutter 应用需要适配鸿蒙平台。本文记录了将阿里云 HTTPDNS Flutter 插件 (`aliyun_httpdns`) 适配到 HarmonyOS 的完整过程,包括遇到的问题和解决方案。 |
| 10 | + |
| 11 | +## 背景 |
| 12 | + |
| 13 | +阿里云 HTTPDNS 是一款基于 HTTP 协议的域名解析服务,能够有效防止 DNS 劫持、提升解析速度。官方已提供 Android、iOS 和 HarmonyOS 原生 SDK,但 Flutter 插件尚未支持 HarmonyOS 平台。 |
| 14 | + |
| 15 | +## 技术架构 |
| 16 | + |
| 17 | +``` |
| 18 | +┌─────────────────────────────────────────────────────┐ |
| 19 | +│ Flutter App │ |
| 20 | +├─────────────────────────────────────────────────────┤ |
| 21 | +│ aliyun_httpdns (Dart) │ |
| 22 | +│ MethodChannel │ |
| 23 | +├──────────┬──────────────┬───────────────────────────┤ |
| 24 | +│ Android │ iOS │ HarmonyOS │ |
| 25 | +│ Kotlin │ Swift │ ArkTS │ |
| 26 | +├──────────┼──────────────┼───────────────────────────┤ |
| 27 | +│ 阿里云 │ 阿里云 │ 阿里云 │ |
| 28 | +│ SDK │ SDK │ @aliyun/httpdns │ |
| 29 | +└──────────┴──────────────┴───────────────────────────┘ |
| 30 | +``` |
| 31 | + |
| 32 | +## 实现步骤 |
| 33 | + |
| 34 | +### 1. 配置 pubspec.yaml |
| 35 | + |
| 36 | +在插件的 `pubspec.yaml` 中添加 HarmonyOS 平台支持: |
| 37 | + |
| 38 | +```yaml |
| 39 | +flutter: |
| 40 | + plugin: |
| 41 | + platforms: |
| 42 | + android: |
| 43 | + package: com.aliyun.ams.httpdns |
| 44 | + pluginClass: AliyunHttpDnsPlugin |
| 45 | + ios: |
| 46 | + pluginClass: AliyunHttpDnsPlugin |
| 47 | + ohos: |
| 48 | + package: com.aliyun.ams.httpdns |
| 49 | + pluginClass: AliyunHttpDnsPlugin |
| 50 | +``` |
| 51 | +
|
| 52 | +### 2. 创建 ohos 目录结构 |
| 53 | +
|
| 54 | +``` |
| 55 | +ohos/ |
| 56 | +├── src/main/ |
| 57 | +│ ├── ets/components/plugin/ |
| 58 | +│ │ └── AliyunHttpDnsPlugin.ets |
| 59 | +│ └── module.json5 |
| 60 | +├── oh-package.json5 |
| 61 | +├── build-profile.json5 |
| 62 | +├── hvigorfile.ts |
| 63 | +└── index.ets |
| 64 | +``` |
| 65 | + |
| 66 | +### 3. 配置依赖 (oh-package.json5) |
| 67 | + |
| 68 | +```json5 |
| 69 | +{ |
| 70 | + "name": "aliyun_httpdns", |
| 71 | + "version": "1.0.0", |
| 72 | + "description": "Aliyun HTTPDNS Flutter plugin for HarmonyOS", |
| 73 | + "main": "index.ets", |
| 74 | + "author": "", |
| 75 | + "license": "Apache-2.0", |
| 76 | + "dependencies": { |
| 77 | + "@aliyun/httpdns": "^1.2.2" |
| 78 | + } |
| 79 | +} |
| 80 | +``` |
| 81 | + |
| 82 | +### 4. 实现插件核心代码 |
| 83 | + |
| 84 | +```typescript |
| 85 | +import { FlutterPlugin, FlutterPluginBinding } from '@ohos/flutter_ohos/...'; |
| 86 | +import { httpdns, HttpDnsConfig, IHttpDnsService, IpType } from '@aliyun/httpdns'; |
| 87 | + |
| 88 | +export default class AliyunHttpDnsPlugin implements FlutterPlugin, MethodCallHandler { |
| 89 | + private httpDnsService: IHttpDnsService | null = null; |
| 90 | + |
| 91 | + private async initialize(args: ESObject, result: MethodResult): Promise<void> { |
| 92 | + // 解析参数 |
| 93 | + let accountId: string | undefined; |
| 94 | + if (args instanceof Map) { |
| 95 | + if (args.has('accountId')) accountId = args.get('accountId') as string; |
| 96 | + } else { |
| 97 | + if (args['accountId']) accountId = args['accountId'] as string; |
| 98 | + } |
| 99 | + |
| 100 | + // 配置服务 |
| 101 | + const config: HttpDnsConfig = { useHttps: true }; |
| 102 | + httpdns.configService(accountId, config); |
| 103 | + |
| 104 | + // 获取服务实例 |
| 105 | + const service = await httpdns.getService(accountId); |
| 106 | + this.httpDnsService = service; |
| 107 | + result.success(true); |
| 108 | + } |
| 109 | + |
| 110 | + private async resolveHost(args: ESObject, result: MethodResult): Promise<void> { |
| 111 | + const hostname = args['hostname'] as string; |
| 112 | + const dnsResult = await this.httpDnsService.getHttpDnsResultAsync(hostname, IpType.Both); |
| 113 | + result.success({ |
| 114 | + "ipv4": dnsResult?.ipv4s || [], |
| 115 | + "ipv6": dnsResult?.ipv6s || [] |
| 116 | + }); |
| 117 | + } |
| 118 | +} |
| 119 | +``` |
| 120 | + |
| 121 | +## 遇到的问题与解决方案 |
| 122 | + |
| 123 | +### 问题 1:MissingPluginException |
| 124 | + |
| 125 | +**现象**:调用插件方法时抛出 `MissingPluginException` |
| 126 | + |
| 127 | +**原因**:HarmonyOS Flutter 插件未自动注册 |
| 128 | + |
| 129 | +**解决方案**:在 Example 应用的 `GeneratedPluginRegistrant.ets` 中手动注册: |
| 130 | + |
| 131 | +```typescript |
| 132 | +import AliyunHttpDnsPlugin from 'aliyun_httpdns'; |
| 133 | + |
| 134 | +export default class GeneratedPluginRegistrant { |
| 135 | + static registerWith(bindingBase: FlutterPluginBinding) { |
| 136 | + bindingBase.getFlutterEngine().getPlugins().add(new AliyunHttpDnsPlugin()); |
| 137 | + } |
| 138 | +} |
| 139 | +``` |
| 140 | + |
| 141 | +### 问题 2:ArkTS 类型检查错误 |
| 142 | + |
| 143 | +**现象**:`Record<string, any>` 类型报错 |
| 144 | + |
| 145 | +**原因**:ArkTS 严格模式不允许 `any` 类型 |
| 146 | + |
| 147 | +**解决方案**:使用 `ESObject` 替代 `any`,并使用 `instanceof Map` 检测参数类型: |
| 148 | + |
| 149 | +```typescript |
| 150 | +private async initialize(args: ESObject, result: MethodResult): Promise<void> { |
| 151 | + if (args instanceof Map) { |
| 152 | + // Map 类型处理 |
| 153 | + } else { |
| 154 | + // Object 类型处理 |
| 155 | + } |
| 156 | +} |
| 157 | +``` |
| 158 | + |
| 159 | +### 问题 3:字节码 HAR 兼容性问题 |
| 160 | + |
| 161 | +**现象**:编译报错 `Specification Limit Violation` |
| 162 | + |
| 163 | +**原因**:阿里云 SDK 的 `.har` 文件需要特定编译配置 |
| 164 | + |
| 165 | +**解决方案**:在 `build-profile.json5` 中启用: |
| 166 | + |
| 167 | +```json5 |
| 168 | +{ |
| 169 | + "app": { |
| 170 | + "products": [{ |
| 171 | + "buildOption": { |
| 172 | + "strictMode": { |
| 173 | + "useNormalizedOHMUrl": true |
| 174 | + } |
| 175 | + } |
| 176 | + }] |
| 177 | + } |
| 178 | +} |
| 179 | +``` |
| 180 | + |
| 181 | +### 问题 4:参数解析为 undefined |
| 182 | + |
| 183 | +**现象**:`hostname` 和 `ipType` 参数为 `undefined` |
| 184 | + |
| 185 | +**原因**:Flutter 传递的参数可能是 `Map` 对象,而非普通 `Object` |
| 186 | + |
| 187 | +**解决方案**:同时处理两种情况: |
| 188 | + |
| 189 | +```typescript |
| 190 | +if (args instanceof Map) { |
| 191 | + if (args.has('hostname')) hostname = args.get('hostname') as string; |
| 192 | +} else { |
| 193 | + if (args['hostname']) hostname = args['hostname'] as string; |
| 194 | +} |
| 195 | +``` |
| 196 | + |
| 197 | +### 问题 5:同步解析返回空结果 |
| 198 | + |
| 199 | +**现象**:`getHttpDnsResultSyncNonBlocking` 首次调用返回空 |
| 200 | + |
| 201 | +**原因**:非阻塞方法在缓存未命中时立即返回空结果 |
| 202 | + |
| 203 | +**解决方案**:改用异步方法 `getHttpDnsResultAsync`: |
| 204 | + |
| 205 | +```typescript |
| 206 | +const dnsResult = await this.httpDnsService.getHttpDnsResultAsync(hostname, ipType); |
| 207 | +``` |
| 208 | + |
| 209 | +## 平台差异处理 |
| 210 | + |
| 211 | +| 功能 | Android | iOS | HarmonyOS | |
| 212 | +|------|---------|-----|-----------| |
| 213 | +| accountId 类型 | int/String | Int | String | |
| 214 | +| useHttps 配置 | setHttpsRequestEnabled | setHTTPSRequestEnabled | configService.useHttps | |
| 215 | +| 日志开关 | HttpDnsLog.enable() | setLogEnabled() | httpdns.enableHiLog() | |
| 216 | + |
| 217 | +## 总结 |
| 218 | + |
| 219 | +本次适配工作涉及: |
| 220 | +- Flutter 插件 HarmonyOS 平台配置 |
| 221 | +- ArkTS 类型系统适配 |
| 222 | +- 阿里云 HTTPDNS SDK 集成 |
| 223 | +- 跨平台 API 差异处理 |
| 224 | + |
| 225 | +HarmonyOS Flutter 插件开发与 Android/iOS 有一些差异,主要体现在: |
| 226 | +- 类型系统更严格(ArkTS) |
| 227 | +- 参数传递方式不同(Map vs Object) |
| 228 | +- SDK API 略有差异 |
| 229 | + |
| 230 | +通过本文的实践,相信读者可以更顺利地适配其他 Flutter 插件到 HarmonyOS 平台。 |
0 commit comments