# 鸿蒙 ArkTS 编译警告消除实战:从 5 个 WARN 到 0 警告

## 一、背景

在鸿蒙应用开发中,随着 SDK 版本迭代,部分早期 API 会被标记为废弃(deprecated),编译时产生大量警告。这些警告虽然不影响功能运行,但:

- **掩盖真正的错误信息**:大量警告中容易遗漏关键问题
- **技术债务累积**:废弃 API 可能在未来版本被移除,导致应用崩溃
- **代码质量下降**:警告是代码坏味道的信号

本文以新华字典鸿蒙应用为例,记录如何将 5 个 ArkTS 编译警告逐步清零的完整过程。

---

## 二、环境信息

| 工具 | 版本                     |
|------|------------------------|
| HarmonyOS SDK | 23(API 23)             |
| DevEco Studio | 内置 SDK                 |
| 构建工具 | hvigorw 6.24.2         |
| 开发语言 | ArkTS                  |
| 目标设备 | HarmonyOS 6.1.0(Phone) |

---

## 三、警告清单

修复前的编译输出:

```
WARN: ArkTS:WARN File: Index.ets:221:18
 'pushUrl' has been deprecated.

WARN: ArkTS:WARN File: Index.ets:230:18
 'pushUrl' has been deprecated.

WARN: ArkTS:WARN File: ARBodyPage.ets:150:16
 'back' has been deprecated.

WARN: ArkTS:WARN File: ARBodyPage.ets:167:46
 'getContext' has been deprecated.

WARN: ArkTS:WARN File: Index.ets:331:7
 Function may throw exceptions. Special handling is required.
```

| 编号 | 类型 | 废弃 API | 文件 | 行号 |
|:----:|------|----------|------|------|
| 1 | deprecated | `router.pushUrl()` | Index.ets | 221 |
| 2 | deprecated | `router.pushUrl()` | Index.ets | 230 |
| 3 | deprecated | `router.back()` | ARBodyPage.ets | 150 |
| 4 | deprecated | `getContext(this)` | ARBodyPage.ets | 167 |
| 5 | logic | 函数可能抛异常 | Index.ets | 331 |

---

## 四、修复过程

### 4.1 修复 router.pushUrl() 和 router.back()

#### 问题分析

从 API 18 开始,`router` 模块的 `pushUrl()` 和 `back()` 被标记为废弃,推荐使用 `UIContext` 中的路由方法:

```typescript
// 废弃写法
import { router } from '@kit.ArkUI';
router.pushUrl({ url: 'pages/SomePage' });
router.back();

// 推荐写法
this.getUIContext().getRouter().pushUrl({ url: 'pages/SomePage' });
this.getUIContext().getRouter().back();
```

#### SDK 源码确认

查看 SDK 类型定义文件 `@ohos.router.d.ts`,明确标注了废弃信息:

```typescript
/**
 * @deprecated since 18
 * @useinstead ohos.arkui.UIContext.Router#pushUrl
 */
function pushUrl(options: RouterOptions): Promise<void>;

/**
 * @deprecated since 18
 * @useinstead ohos.arkui.UIContext.Router#back
 */
function back(options?: RouterOptions): void;
```

而 `UIContext.Router` 中的同名方法无废弃标记:

```typescript
export class Router {
    pushUrl(options: router.RouterOptions): Promise<void>;
    pushUrl(options: router.RouterOptions, mode: router.RouterMode): Promise<void>;
    back(options?: router.RouterOptions): void;
    back(index: number, params?: Object): void;
    // ... 无 @deprecated 标注
}
```

#### 代码修改

**Index.ets** — 页面跳转

```typescript
// ❌ 修复前
import { router } from '@kit.ArkUI';

router.pushUrl({ url: 'pages/ARBodyPage' });
router.pushUrl({ url: 'pages/SystemInfoPage' });

// ✅ 修复后(移除 router 导入)
this.getUIContext().getRouter().pushUrl({ url: 'pages/ARBodyPage' });
this.getUIContext().getRouter().pushUrl({ url: 'pages/SystemInfoPage' });
```

**ARBodyPage.ets** — 返回上一页

```typescript
// ❌ 修复前
import { display, router } from '@kit.ArkUI';
router.back();

// ✅ 修复后(移除 router 导入)
import { display } from '@kit.ArkUI';
this.getUIContext().getRouter().back();
```

**SystemInfoPage.ets** — 返回上一页

```typescript
// ❌ 修复前
import { router } from '@kit.ArkUI';
router.back();

// ✅ 修复后(移除 router 导入)
this.getUIContext().getRouter().back();
```

#### 修复原理

| 概念 | 说明 |
|------|------|
| `this` | 在 `@Component` struct 中,`this` 指向当前组件实例 |
| `getUIContext()` | 获取当前组件所在的 UI 上下文,返回 `UIContext` 对象 |
| `getRouter()` | 从 `UIContext` 获取路由管理器,返回 `Router` 实例 |
| `pushUrl()` / `back()` | 调用 `Router` 实例上的方法,与全局 `router` 功能一致 |

**为什么推荐 UIContext 方式?**

- **上下文安全**:全局 `router` 依赖隐式上下文,多实例场景可能出错
- **生命周期绑定**:UIContext 路由与组件生命周期绑定,避免页面销毁后操作路由
- **类型安全**:通过实例方法调用,编译器能更好地检查类型

---

### 4.2 修复 getContext(this)

#### 问题分析

`getContext(this)` 从 API 18 开始废弃,推荐使用 `UIContext.getHostContext()`:

```typescript
// 废弃写法
import { common } from '@kit.AbilityKit';
let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;

// 推荐写法
let context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
```

#### SDK 源码确认

```typescript
// UIContext.d.ts
export class UIContext {
    /**
     * Obtains the host context of this UI ability.
     * @returns { Context | undefined } Context object.
     */
    getHostContext(): Context | undefined;
}
```

#### 代码修改

**ARBodyPage.ets** — 权限请求中获取上下文

```typescript
// ❌ 修复前
async requestPermission(): Promise<void> {
  let atManager = abilityAccessCtrl.createAtManager();
  let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
  let permissions: Array<Permissions> = ['ohos.permission.CAMERA'] as Array<Permissions>;
  atManager.requestPermissionsFromUser(context, permissions, (err, result) => {
    // ...
  });
}

// ✅ 修复后
async requestPermission(): Promise<void> {
  let atManager = abilityAccessCtrl.createAtManager();
  let context: common.UIAbilityContext =
    this.getUIContext().getHostContext() as common.UIAbilityContext;
  let permissions: Array<Permissions> = ['ohos.permission.CAMERA'] as Array<Permissions>;
  atManager.requestPermissionsFromUser(context, permissions, (err, result) => {
    // ...
  });
}
```

#### getHostContext vs getContext 对比

| 维度 | `getContext(this)` | `this.getUIContext().getHostContext()` |
|------|-------------------|---------------------------------------|
| 返回类型 | `Context` | `Context \| undefined` |
| 上下文来源 | 隐式查找 | 显式从 UI 上下文获取 |
| 废弃状态 | ⚠️ API 18 废弃 | ✅ 推荐使用 |
| 空值安全 | 无 | 需处理 `undefined` |

> **注意**:`getHostContext()` 返回 `Context | undefined`,需要用 `as` 断言或空值检查。在组件 `onAppear` 之后调用时,上下文一定存在,因此 `as` 断言是安全的。

---

### 4.3 修复 "Function may throw exceptions"

#### 问题分析

这不是废弃警告,而是 ArkTS 编译器的**异常安全检查**。当 `catch` 块中调用了可能抛异常的函数时,编译器会提示需要特殊处理。

```typescript
// ❌ 问题代码
try {
  formProvider.openFormManager(want);
} catch (error) {
  // openToast 本身也可能抛异常,但未处理
  promptAction.openToast({ message: (error as BusinessError).message });
}
```

#### 代码修改

```typescript
// ✅ 修复后:内层也加 try-catch
try {
  formProvider.openFormManager(want);
} catch (error) {
  try {
    promptAction.openToast({ message: (error as BusinessError).message });
  } catch (e) {
    console.error(`openToast failed: ${(e as BusinessError).message}`);
  }
}
```

#### 为什么 openToast 也可能抛异常?

`promptAction.openToast()` 的底层实现涉及 IPC 调用(跨进程通信),以下场景可能失败:

- 系统资源不足
- 应用已进入后台,UI 上下文失效
- 参数格式异常

因此编译器要求开发者显式处理这种可能性。

---

## 五、修复结果

修复后的编译输出:

```
> hvigor BUILD SUCCESSFUL in 1 s 166 ms
```

**0 警告,0 错误!**

| 指标 | 修复前 | 修复后 |
|------|:------:|:------:|
| WARN | 5 | **0** |
| ERROR | 0 | 0 |
| 废弃 API 调用 | 4 处 | **0** |
| 异常未处理 | 1 处 | **0** |

---

## 六、API 迁移速查表

| 废弃 API | 推荐替换 | 废弃版本 | 说明 |
|----------|---------|---------|------|
| `router.pushUrl()` | `this.getUIContext().getRouter().pushUrl()` | API 18 | 页面跳转 |
| `router.back()` | `this.getUIContext().getRouter().back()` | API 18 | 返回上一页 |
| `router.replaceUrl()` | `this.getUIContext().getRouter().replaceUrl()` | API 18 | 页面替换 |
| `router.clear()` | `this.getUIContext().getRouter().clear()` | API 18 | 清空路由栈 |
| `getContext(this)` | `this.getUIContext().getHostContext()` | API 18 | 获取上下文 |
| `promptAction.openToast()` | 需 try-catch 包裹 | - | 异常安全 |

---

## 七、最佳实践

### 7.1 UIContext 路由模式

在 `@Component` struct 中,推荐统一使用 UIContext 方式操作路由:

```typescript
@Component
struct MyPage {
  // 封装路由工具方法(可选)
  private navigateTo(url: string): void {
    this.getUIContext().getRouter().pushUrl({ url: url })
      .catch((err: BusinessError) => {
        console.error(`导航失败: ${err.message}`);
      });
  }

  private goBack(): void {
    this.getUIContext().getRouter().back();
  }

  build() {
    Button('跳转').onClick(() => this.navigateTo('pages/NextPage'))
    Button('返回').onClick(() => this.goBack())
  }
}
```

### 7.2 异常安全编码

ArkTS 编译器会检查所有可能抛异常的函数调用。编写代码时:

1. **所有可能抛异常的函数**都要用 `try-catch` 包裹
2. **catch 块中**不要调用其他可能抛异常的函数(或再包一层 try-catch)
3. **Promise 链**中用 `.catch()` 处理异常

```typescript
// ✅ 规范写法
try {
  someApi.thatMayThrow();
} catch (error) {
  try {
    promptAction.showToast({ message: '操作失败' });
  } catch (e) {
    console.error('Toast also failed');
  }
}

// ✅ Promise 写法
this.getUIContext().getRouter().pushUrl({ url: 'pages/Next' })
  .then(() => { console.info('导航成功'); })
  .catch((err: BusinessError) => { console.error(`导航失败: ${err.message}`); });
```

### 7.3 定期清理警告

建议在 CI/CD 流程中加入警告检查:

```bash
# 构建并检查是否有 WARN
hvigorw assembleHap 2>&1 | grep "WARN" && echo "存在编译警告,请修复!" && exit 1
```

---

## 八、总结

鸿蒙 SDK 的 API 迭代速度快,废弃标记是引导开发者使用更安全、更规范 API 的重要机制。本文从 5 个编译警告出发,通过三个维度的修复:

| 维度 | 修复方法 | 涉及 API |
|------|---------|---------|
| **路由废弃** | `router.*` → `UIContext.getRouter().*` | pushUrl, back |
| **上下文废弃** | `getContext(this)` → `UIContext.getHostContext()` | getContext |
| **异常安全** | 嵌套 try-catch | promptAction.openToast |

最终实现 **0 警告、0 错误** 的干净构建。

核心原则:
- **废弃即迁移**:不要等到 API 被移除才处理
- **上下文优先**:UIContext 是鸿蒙推荐的应用内通信方式
- **异常必处理**:ArkTS 编译器强制异常安全,嵌套 try-catch 是常见解法

---

## 项目地址

本文所有代码已开源:[https://gitcode.com/jianguoxu/myapplication](https://gitcode.com/jianguoxu/myapplication)

---

## 参考资料

- [HarmonyOS router API 参考](https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-basic-components-navigation-V5)
- [HarmonyOS UIContext API 参考](https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-arkui-uicontext-V5)
- [HarmonyOS promptAction API 参考](https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-promptaction-V5)
Logo

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

更多推荐