AI 修仙功法 —— 鸿蒙 NEXT ArkTS 修仙主题 AI 对话应用开发全解析应用


AI 修仙功法 —— 鸿蒙 NEXT ArkTS 修仙主题 AI 对话应用开发全解析应用
第一章 开篇:修仙与鸿蒙的缘起
大道三千,殊途同归。当古老的修仙传说遇上现代的鸿蒙操作系统,一段全新的修炼之旅就此展开。
1.1 缘起:何为"AI 修仙"
在传统修仙小说中,修士通过吐纳天地灵气、炼化法宝、感悟大道来提升修为。而在这个万物互联的数字时代,"修仙"被赋予了全新的含义——通过 AI 技术赋能应用,让冰冷的代码拥有"灵性",让沉默的设备能够"开口说话"。
本文将以一个完整的 HarmonyOS NEXT ArkTS 项目为蓝本,深入剖析如何构建一个"AI 修仙"主题的对话应用。这个项目虽然以一个 TTS(文本转语音)功能为切入点,但它的架构设计、权限管理、生命周期控制和模块封装,无一不体现着"修炼"的智慧。
1.2 修炼环境:HarmonyOS NEXT 6.1.1(API 24)
本项目基于 HarmonyOS NEXT 6.1.1 版本,对应的 SDK 版本为 6.1.0(23),采用 Stage 模型 进行开发。Stage 模型是 HarmonyOS 从 API 9 开始主推的 Ability 框架,它相较于旧的 FA(Feature Ability)模型,具有以下核心优势:
| 特性 | 传统 FA 模型 | Stage 模型 |
|---|---|---|
| 组件化 | 弱 | 强,Ability 作为独立模块 |
| 生命周期 | 简单但粗粒度 | 精细化管理 |
| 进程模型 | 多进程较复杂 | 天然支持多进程 |
| 首选项存储 | 需自行实现 | 内置 Preferences |
| 后台任务 | 受限 | 成熟的 WorkScheduler 机制 |
本项目的 Stage 模型配置体现在 build-profile.json5 中:
{
"apiType": "stageMode",
"buildOptionSet": [
{
"name": "release",
"arkOptions": {
"obfuscation": {
"ruleOptions": {
"enable": false,
"files": ["./obfuscation-rules.txt"]
}
}
}
}
],
"targets": [
{ "name": "default" },
{ "name": "ohosTest" }
]
}
修炼要点:Stage 模型下的 apiType: "stageMode" 声明了整个应用以 Stage 方式运行,这是鸿蒙 NEXT 推荐的现代化架构风格。
1.3 功法概览:项目结构
完整的项目目录结构如下(省略构建产物和非关键文件):
s222222/
├── AppScope/
│ ├── app.json5 # 应用级配置
│ └── resources/
│ ├── base/
│ │ ├── element/string.json # 应用名字符串资源
│ │ └── media/ # 应用图标资源
├── entry/
│ ├── src/main/
│ │ ├── ets/
│ │ │ ├── pages/Index.ets # 主页面(UI + 交互逻辑)
│ │ │ ├── entryability/EntryAbility.ets # Ability 生命周期
│ │ │ ├── entrybackupability/EntryBackupAbility.ets # 备份扩展
│ │ │ └── TTSManager.ets # TTS 引擎管理器(核心功法)
│ │ ├── module.json5 # 模块配置(权限、入口等)
│ │ └── resources/ # 模块级资源
│ ├── build-profile.json5 # 模块构建配置
│ └── oh-package.json5 # 模块依赖声明
├── build-profile.json5 # 项目级构建配置
├── oh-package.json5 # 项目级依赖管理
├── hvigor/
│ └── hvigor-config.json5 # 构建工具配置
└── local.properties # 本地环境配置
这个结构体现了鸿蒙应用的标准分层:AppScope(宗门层级)→ Module(修炼洞府)→ Ability(功法心法)。每一层各司其职,共同构成完整的应用体系。
第二章 筑基:Module 配置与权限修炼
2.1 Module 配置详解(module.json5)
module.json5 是鸿蒙应用中最关键的路引,它定义了模块的诸多核心属性。让我们逐条解析这个"修炼法门":
{
"module": {
"name": "entry", // 模块名称,唯一标识
"type": "entry", // 模块类型:entry(主入口)| feature(特性模块)
"description": "$string:module_desc",// 模块描述,引用字符串资源
"mainElement": "EntryAbility", // 主 Ability 入口
"deviceTypes": ["phone"], // 支持的设备类型
"deliveryWithInstall": true, // 随安装交付
"installationFree": false, // 是否支持免安装运行
"pages": "$profile:main_pages", // 页面路由配置
"abilities": [ // Ability 注册列表
{
"name": "EntryAbility", // Ability 名称
"srcEntry": "./ets/entryability/EntryAbility.ets", // 源文件路径
"description": "$string:EntryAbility_desc",
"icon": "$media:layered_image", // 图标(支持分层图标)
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon", // 启动窗口图标
"startWindowBackground": "$color:start_window_background", // 启动背景色
"exported": true, // 是否可被其他应用启动
"skills": [ // 意图过滤器
{
"entities": ["entity.system.home"],
"actions": ["ohos.want.action.home"]
}
]
}
],
"extensionAbilities": [ // 扩展 Ability
{
"name": "EntryBackupAbility",
"srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets",
"type": "backup", // 扩展类型:备份
"exported": false
}
],
"requestPermissions": [ // 运行时权限声明
{
"name": "ohos.permission.MICROPHONE", // 麦克风权限
"reason": "$string:permission_microphone_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always" // 始终可用
}
},
{
"name": "ohos.permission.INTERNET", // 网络权限
"reason": "$string:permission_internet_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
}
]
}
}
2.2 权限修炼之道
权限是鸿蒙安全体系的核心。本项目申请了两个关键权限:
2.2.1 麦克风权限(MICROPHONE)
{
"name": "ohos.permission.MICROPHONE",
"reason": "用于语音识别功能采集麦克风音频,进行语音转文字。"
}
虽然在当前代码中主要演示的是 TTS(文字→语音),但项目中已经预留了语音识别的能力接口。麦克风权限是用户敏感权限,需要满足以下条件:
- 在
module.json5中声明 - 在首次使用时通过弹窗征得用户同意
- 提供清晰的权限用途说明
鸿蒙 NEXT 的权限管理采用了细粒度运行时授权机制,用户可以在"设置→应用→权限管理"中随时开关每一项权限。
2.2.2 网络权限(INTERNET)
{
"name": "ohos.permission.INTERNET",
"reason": "用于联网获取或发送与语音能力相关的数据。"
}
本项目使用的 TTS 引擎支持在线模式(online: 1),这意味着语音合成请求需要通过互联网发送到服务端进行处理。因此网络权限是必需的。
2.3 页面路由配置
// entry/src/main/resources/base/profile/main_pages.json
{
"src": ["pages/Index"]
}
这是页面的路由表,定义了应用有哪些页面以及它们的路由路径。当前只有一个页面 pages/Index,对应 Index.ets 组件。在 Stage 模型中,页面路由通过 $profile:main_pages 引用,实现配置与代码的解耦。
第三章 心法:EntryAbility 生命周期修炼
3.1 Ability 概述
在鸿蒙的世界里,Ability 是应用的基本组成单元,类似于 Android 的 Activity 和 iOS 的 ViewController。Stage 模型下的 UIAbility 负责管理应用窗口和页面生命周期。
3.2 完整生命周期解析
// entry/src/main/ets/entryability/EntryAbility.ets
import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
const DOMAIN = 0x0000; // 日志域(0x0000~0xFFFF,建议业务使用0x0000)
export default class EntryAbility extends UIAbility {
// 阶段一:创建阶段
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
try {
// 设置颜色模式:跟随系统
this.context.getApplicationContext()
.setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
} catch (err) {
hilog.error(DOMAIN, 'testTag',
'Failed to set colorMode. Cause: %{public}s', JSON.stringify(err));
}
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
}
// 阶段二:销毁阶段
onDestroy(): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy');
}
// 阶段三:窗口创建阶段(核心)
onWindowStageCreate(windowStage: window.WindowStage): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
// 加载页面内容
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
hilog.error(DOMAIN, 'testTag',
'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
return;
}
hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
});
}
// 阶段四:窗口销毁阶段
onWindowStageDestroy(): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
// 阶段五:切到前台
onForeground(): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground');
}
// 阶段六:切到后台
onBackground(): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground');
}
}
3.3 生命周期的"修炼六境"
| 阶段 | 回调方法 | 类比修仙 | 说明 |
|---|---|---|---|
| 开光 | onCreate |
筑基成功 | Ability 初次创建,初始化基础状态 |
| 融合 | onWindowStageCreate |
神识外放 | 窗口舞台就绪,加载页面内容 |
| 元婴 | onForeground |
元婴出窍 | Ability 来到前台,开始交互 |
| 化神 | onBackground |
神识内敛 | Ability 切到后台,释放非关键资源 |
| 渡劫 | onWindowStageDestroy |
肉身重塑 | 窗口销毁,清理窗口级资源 |
| 飞升 | onDestroy |
飞升上界 | Ability 完全销毁,释放所有资源 |
重要实践:在 onCreate 中,我们设置颜色模式为 COLOR_MODE_NOT_SET(跟随系统),这体现了鸿蒙对系统一致性体验的追求——应用不应强迫用户使用某种配色方案,而应尊重系统设置。
3.4 扩展能力:备份恢复
// EntryBackupAbility.ets
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit';
const DOMAIN = 0x0000;
export default class EntryBackupAbility extends BackupExtensionAbility {
async onBackup() {
hilog.info(DOMAIN, 'testTag', 'onBackup ok');
await Promise.resolve();
}
async onRestore(bundleVersion: BundleVersion) {
hilog.info(DOMAIN, 'testTag',
'onRestore ok %{public}s', JSON.stringify(bundleVersion));
await Promise.resolve();
}
}
BackupExtensionAbility 是鸿蒙提供给应用的数据备份恢复能力。它允许应用在用户更换设备或重置设备后,将关键数据(如用户设置、对话历史)恢复到新设备上。这就像是修士的"元神备份"——即使肉身毁灭,神识仍可重生。
第四章 法宝:TTS 引擎管理器(核心功法)
4.1 功法总纲
TTSManager 是整个项目的核心,封装了鸿蒙 @kit.CoreSpeechKit 中的文本转语音引擎。它像一件精心炼制的法宝,包含了"驱动(引擎创建)→ 注灵(参数配置)→ 施法(语音播报)→ 收法(停止控制)→ 温养(资源释放)"的完整流程。
import { textToSpeech } from '@kit.CoreSpeechKit';
import { BusinessError } from '@ohos.base';
4.2 法宝淬炼:类的属性与构造函数
export class TTSManager {
// ─── 核心法宝 ───
private tts: textToSpeech.TextToSpeechEngine | null = null;
// ─── 状态修为 ───
private playing: boolean = false;
private paused: boolean = false;
private userStopped: boolean = false;
private currentText: string = '';
// ─── 传音符(回调) ───
private onPlayingStateChange: (playing: boolean, paused: boolean) => void = () => {};
private onLogChange: (log: string) => void = () => {};
constructor(
onPlayingStateChange?: (playing: boolean) => void,
onLogChange?: (log: string) => void
) {
if (onPlayingStateChange) {
// 适配旧版本回调接口:只传递 playing 状态
this.onPlayingStateChange = (playing, paused) => {
onPlayingStateChange(playing);
};
}
if (onLogChange) {
this.onLogChange = onLogChange;
}
this.initTTS(); // 淬炼开始!
}
}
设计思考:构造函数接受两个可选的回调参数,通过依赖注入的方式与 UI 层解耦。这种设计使得 TTSManager 可以在单元测试中独立运行,也便于将来替换 UI 层(如从 ArkUI 切换到 React Native)。
4.3 驱动注灵:创建 TTS 引擎
private initTTS(): void {
const params: textToSpeech.CreateEngineParams = {
language: 'zh-CN', // 语言:中文普通话
person: 0, // 发音人:默认发音人
online: 1 // 模式:在线合成(1=在线,0=离线)
};
textToSpeech.createEngine(
params,
(err: BusinessError, engine: textToSpeech.TextToSpeechEngine) => {
if (err || !engine) {
this.updateLog(`创建TTS引擎失败: ${err?.message}`);
return;
}
this.tts = engine;
this.setListener(); // 设置监听器
}
);
}
参数详解
| 参数 | 可选值 | 含义 | 修炼心得 |
|---|---|---|---|
language |
'zh-CN', 'en-US', 'ja-JP' 等 |
合成语言 | 中文用 zh-CN,语速语调更自然 |
person |
0, 1, 2 等 |
发音人编号 | 不同编号对应不同音色,0 通常为标准女声 |
online |
0(离线), 1(在线) |
引擎模式 | 在线音质更好、支持更多音色,但需要网络 |
修炼提醒:在线模式(online: 1)意味着应用必须拥有 ohos.permission.INTERNET 权限。如果用户拒绝授权,引擎将创建失败。在实际产品中,应当准备离线模式作为降级方案。
4.4 神识链接:设置监听器
private setListener(): void {
if (!this.tts) return;
const listener: textToSpeech.SpeakListener = {
// ── 开始播报 ──
onStart: (id, rsp) => {
if (!this.paused) {
this.userStopped = false; // 正常开始,清除停止标记
}
this.updatePlaying(true, false);
this.updateLog('开始播报');
},
// ── 播报完成 ──
onComplete: (id, rsp) => {
if (rsp && rsp.type === 1) { // type=1 表示正常结束
this.updateLog('播报完成');
this.updatePlaying(false, false);
}
},
// ── 播报停止 ──
onStop: (id, rsp) => {
this.updatePlaying(false, false);
},
// ── 音频数据回调(当前未使用) ──
onData: (id, audio, rsp) => {
// audio 为 AudioInputStream,可用于自定义播放
},
// ── 播报错误 ──
onError: (id, code, msg) => {
this.updateLog(`TTS错误(${code}): ${msg}`);
this.updatePlaying(false, false);
}
};
this.tts.setListener(listener);
}
状态管理剖析:
userStopped 和 paused 两个标志位是本法宝的精妙之处。它们区分了三种停止场景:
| 场景 | userStopped |
paused |
行为 |
|---|---|---|---|
| 用户主动停止 | true |
false |
停止播放,重置状态 |
| 用户暂停 | false |
true |
停在当前位置,可恢复 |
| 播报自然结束 | false |
false |
正常清理状态 |
这种设计在将来扩展暂停/恢复功能时尤为重要——我们可以通过 paused 状态判断是继续播报还是重新开始。
4.5 施法之道:speak 方法
public speak(text: string): void {
if (!this.tts) {
this.updateLog('TTS未就绪');
return;
}
this.userStopped = false;
this.paused = false;
this.currentText = text;
const filtered = this.filterText(text); // 先净化文本
const speakParams: textToSpeech.SpeakParams = {
requestId: 'tts_' + Date.now() // 唯一请求 ID
};
try {
this.tts.speak(filtered, speakParams);
} catch (e) {
this.updateLog(`播报失败: ${JSON.stringify(e)}`);
}
}
关键点:
- 前置校验:如果引擎未就绪,直接返回并记录日志,避免空指针异常。
- 状态重置:每次新的播报都重置
userStopped和paused,确保每次施法都是"全新开始"。 - 文本净化:调用
filterText方法过滤特殊字符(见下节)。 - 唯一请求 ID:
requestId使用时间戳保证每次播报的唯一性,方便多播报并发场景下的追踪。 - 异常捕获:
try-catch包围speak调用,防止未预期的运行时错误导致应用崩溃。
4.6 净魔咒:文本过滤
private filterText(text: string): string {
return text
// 移除 Emoji 表情符号(😀-🙏 区域)
.replace(/[\u{1F600}-\u{1F64F}]/gu, '')
// 移除 Emoji 符号(🗾-🧾 区域)
.replace(/[\u{1F300}-\u{1F5FF}]/gu, '')
// 移除交通符号(🚀-🏳️ 区域)
.replace(/[\u{1F680}-\u{1F6FF}]/gu, '')
// 移除杂项符号(☀-⛿ 区域)
.replace(/[\u{2600}-\u{26FF}]/gu, '')
// 移除补充符号区域
.replace(/[\u{1F900}-\u{1F9FF}]/gu, '')
// 移除文字表情(:-), :(, ;) 等)
.replace(/[:;=][)(\]>DPdpO0\-]+/g, '')
// 合并多余空白字符
.replace(/[\s]+/g, ' ');
}
为什么需要文本净化?
TTS 引擎对特殊字符(尤其是 Emoji 和颜文字)的支持并不完善,直接传入会导致以下问题:
- Emoji 被转语音为乱码字符
- 颜文字中的符号可能触发引擎的误解析
- 连续的空白字符导致语速异常
这个"净魔咒"通过 Unicode 范围匹配,精准地移除了 6 大区域的 Emoji 字符和常见的颜文字模式,确保送入引擎的文本是"纯净"的。
4.7 收法之功:停止与状态管理
public stop(): void {
try {
this.userStopped = true; // 标记为用户主动停止
this.paused = false; // 清除暂停状态
this.updatePlaying(false, false);
this.tts?.stop(); // 可选链,安全调用
this.updateLog('已停止播报');
} catch (e) {
this.updateLog(`停止失败: ${JSON.stringify(e)}`);
}
}
优雅的停止:
- 使用可选链
this.tts?.stop()而非if (this.tts),更加简洁 - 先更新状态再调用引擎停止,确保 UI 立即响应
try-catch兜底,防止引擎内部异常传染到 UI 层
4.8 温养之道:资源释放
public shutdown(): void {
if (this.tts) {
try {
this.tts.shutdown(); // 关闭引擎
} catch (e) {
console.error(`关闭TTS引擎失败: ${JSON.stringify(e)}`);
}
this.tts = null; // 释放引用,便于 GC 回收
}
}
修炼要义:shutdown 方法必须在页面销毁时调用(对应 Index.ets 中的 aboutToDisappear 生命周期)。如果不释放 TTS 引擎,它会持续占用系统资源,导致内存泄漏和耗电增加。这就像修士在渡劫后需要闭关温养,避免"道基受损"。
4.9 法宝全貌
完整回顾 TTSManager 的核心方法与属性:
| 方法/属性 | 类别 | 作用 | 调用时机 |
|---|---|---|---|
initTTS() |
private | 创建并初始化 TTS 引擎 | 构造函数中 |
setListener() |
private | 绑定播报状态回调 | 引擎创建成功后 |
speak(text) |
public | 开始播报 | 用户点击"朗读"按钮 |
stop() |
public | 停止播报 | 用户点击"停止朗读"按钮 |
filterText(text) |
private | 净化文本,移除特殊字符 | speak() 内部 |
isPlaying() |
public | 查询是否正在播报 | UI 层状态绑定 |
isPaused() |
public | 查询是否暂停 | 扩展用 |
getLog() |
public | 获取最新的日志信息 | 调试和显示用 |
shutdown() |
public | 释放引擎资源 | 页面销毁时 |
第五章 御剑:Index 主页面构建
5.1 页面布局总览
主页面 Index.ets 是整个应用的"飞剑"——它御剑飞行,串联了用户交互和 TTS 引擎。我们逐层解析这把飞剑的构造。
import { TTSManager } from '../TTSManager';
@Entry
@Component
struct Index {
@State currentText: string = '你好,欢迎使用HarmonyOS TTS功能!';
@State isSpeaking: boolean = false;
@State logText: string = '';
private ttsManager: TTSManager = new TTSManager(
(playing) => { this.isSpeaking = playing; },
(log) => { this.addLog('TTS', log); }
);
// ...
}
5.2 状态变量的"三花聚顶"
| 状态变量 | 类型 | 初始值 | 作用 | 类比修仙 |
|---|---|---|---|---|
currentText |
string |
默认欢迎语 | 双向绑定 TextArea 的输入文本 | 丹田(存储灵气) |
isSpeaking |
boolean |
false |
驱动按钮文字和颜色的变化 | 神识(感知状态) |
logText |
string |
空字符串 | 显示日志信息 | 识海(记录历程) |
@State 装饰器是 ArkTS 的响应式核心。当这些变量的值发生变化时,ArkUI 会自动重新渲染相关组件,无需手动操作 DOM。这就像修士的"天人感应"——身体状态一变化,天地灵气自然响应。
5.3 构建 UI:飞剑的七段剑诀
第一段:剑诀起手——标题
Text('TTS文本转语音演示')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ top: 20, bottom: 20 })
通过链式调用设置字体大小(24fp)、粗细(加粗)和边距。这是 ArkUI 组件开发的典型风格——声明式、链式、直观。
第二段:剑气凝形——文本输入区
TextArea({ placeholder: '请输入要朗读的文本', text: this.currentText })
.height(120)
.width('90%')
.borderRadius(8)
.borderColor('#CCCCCC')
.borderWidth(1)
.onChange((value: string) => { this.currentText = value; })
.margin({ bottom: 20 })
TextArea 属性详解:
placeholder:占位符文本,在输入框为空时显示text:绑定this.currentText,实现双向数据绑定.onChange():监听文本变化,实时更新状态- 圆角、边框、宽高等样式属性
修炼心得:宽度设为 '90%' 而非固定 px,使组件自适应不同屏幕尺寸——这是响应式设计的基本功。
第三段:御剑诀——按钮行
Row() {
Button(this.isSpeaking ? '停止朗读' : '朗读')
.onClick(() => {
if (this.isSpeaking) {
this.stopSpeaking();
} else {
this.speakText();
}
})
.backgroundColor(this.isSpeaking ? '#FF6B6B' : '#40E0D0')
.fontColor(Color.White)
.borderRadius(8)
.width(120)
.height(40)
.margin({ right: 10 })
Button('清空日志')
.onClick(() => { this.logText = ''; })
.backgroundColor('#FFA500')
.fontColor(Color.White)
.borderRadius(8)
.width(120)
.height(40)
}
.margin({ bottom: 20 })
交互设计亮点:
- 状态驱动的按钮文字:通过三元运算符
this.isSpeaking ? '停止朗读' : '朗读'动态切换按钮文字,实现"同一个按钮,两种功能"。 - 颜色标识:
- 朗读状态 → 红色(
#FF6B6B),直观传达"停止"的含义 - 空闲状态 → 青色(
#40E0D0),传达"开始/就绪"的含义 - 清空日志 → 橙色(
#FFA500),传达"操作/提醒"的含义
- 朗读状态 → 红色(
Row容器:将两个按钮水平排列,通过margin({ right: 10 })保持间距。
第四段:识海开——日志标题
Text('日志信息')
.fontSize(16)
.fontWeight(FontWeight.Medium)
.alignSelf(ItemAlign.Start)
.margin({ left: '5%', bottom: 10 })
alignSelf(ItemAlign.Start) 让文本在父容器中左对齐,覆盖父容器 Column 的默认居中对齐。
第五段:显神通——日志内容显示
Text(this.logText)
.width('90%')
.height(150)
.borderRadius(8)
.borderColor('#CCCCCC')
.borderWidth(1)
.padding(10)
.fontSize(14)
.maxLines(10)
.textAlign(TextAlign.Start)
技术细节:
maxLines(10):限制最多显示 10 行,防止日志过多撑满屏幕textAlign(TextAlign.Start):左对齐文本padding(10):内边距,让文本不紧贴边框
第六段:归元——根容器
.width('100%')
.height('100%')
.padding(20)
根 Column 容器占据全屏宽高,整体添加 20vp 的内边距,让内容不会贴边。
5.4 剑法招式:交互逻辑
speakText —— 朗读功法
speakText() {
if (!this.currentText.trim()) {
this.addLog('朗读失败', '没有可朗读的内容');
return;
}
this.ttsManager.speak(this.currentText);
this.addLog('开始朗读',
`开始朗读文本,长度: ${this.currentText.length}字符`);
}
防御性编程:在调用 TTS 之前,先检查文本是否为空(trim() 去除首尾空白),避免空串播报。
stopSpeaking —— 停止功法
stopSpeaking() {
if (this.isSpeaking) {
this.ttsManager.stop();
this.addLog('停止朗读', '已停止朗读');
}
}
双重检查:外部有 if (this.isSpeaking),即使 UI 状态不同步,也不会误调用。
addLog —— 日志记录
addLog(tag: string, message: string) {
const time = new Date().toLocaleTimeString();
this.logText = `[${time}] [${tag}] ${message}\n` + this.logText;
}
设计特点:
- 每条日志包含时间戳、标签和消息
- 新日志追加到开头(
... + this.logText),最新日志始终在最上方 - 使用
toLocaleTimeString()获取本地时间格式
5.5 收功归元:页面销毁
aboutToDisappear() {
this.ttsManager.shutdown();
}
aboutToDisappear 是 ArkTS 的生命周期钩子,类似于 Angular 的 ngOnDestroy 或 Vue 的 onUnmounted。在这里释放 TTS 引擎资源,防止内存泄漏。
第六章 渡劫:错误处理与边界情况
6.1 修仙路上的三大心魔
心魔一:引擎初始化失败
表现:TTS 引擎因网络问题、系统资源不足或用户拒绝权限而创建失败。
应对:
textToSpeech.createEngine(params, (err, engine) => {
if (err || !engine) {
this.updateLog(`创建TTS引擎失败: ${err?.message}`);
return; // 优雅降级
}
// ...
});
修炼心得:通过回调中的 err 对象获取详细的错误信息(err.message),记录日志供开发者排查。用户界面则通过 addLog 展示错误,并在后续操作中通过 if (!this.tts) 检查阻止功能调用。
心魔二:空文本播报
表现:用户未输入任何内容就点击"朗读"按钮。
应对:
speakText() {
if (!this.currentText.trim()) {
this.addLog('朗读失败', '没有可朗读的内容');
return;
}
// ...
}
心魔三:引擎运行时异常
表现:speak() 或 stop() 调用时引擎内部抛出异常。
应对:
try {
this.tts.speak(filtered, speakParams);
} catch (e) {
this.updateLog(`播报失败: ${JSON.stringify(e)}`);
}
6.2 边界情况修炼手册
| 场景 | 预期行为 | 代码保障 |
|---|---|---|
| 快速连击"朗读" | 仅播报最新文本 | 每次 speak() 重置状态,覆盖前一次 |
| 播报中快速点"停止"又点"朗读" | 正常停止后重新播报 | stop() 同步执行后,speak() 重新开始 |
| 页面关闭时正在播报 | 引擎正常关闭 | aboutToDisappear → shutdown() |
| 输入含大量 Emoji 的文本 | 自动过滤后播报 | filterText() 的 Unicode 正则 |
| 网络断连后使用在线模式 | 引擎创建失败,记录日志 | 初始化回调中的错误处理 |
| 清空日志后立即查看 | 日志为空 | this.logText = '' 直接重置状态 |
第七章 合体:构建配置深度解析
7.1 项目级构建配置
// build-profile.json5(项目根目录)
{
"app": {
"signingConfigs": [],
"products": [
{
"name": "default",
"signingConfig": "default",
"targetSdkVersion": "6.1.0(23)", // 目标 SDK 版本
"compatibleSdkVersion": "6.1.0(23)",// 兼容 SDK 版本
"runtimeOS": "HarmonyOS",
"buildOption": {
"strictMode": {
"caseSensitiveCheck": true,
"useNormalizedOHMUrl": true
}
}
}
],
"buildModeSet": [
{ "name": "debug" },
{ "name": "release" }
]
},
"modules": [
{
"name": "entry",
"srcPath": "./
更多推荐


所有评论(0)