鸿蒙生态AI应用开发实战:基于ArkTS构建百科问答助手,适配鸿蒙PC与移动终端


引言
在人工智能与移动互联网深度融合的今天,AI问答类应用已经成为用户获取知识的重要途径。随着华为鸿蒙生态的持续发展和鸿蒙PC的发布,开发者面临着新的机遇——如何构建一款既能在鸿蒙手机上流畅运行,又能充分利用鸿蒙PC大屏优势的高质量AI应用?
本文将详细介绍一款基于ArkTS语言开发的AI百科问答助手应用,该应用不仅完美适配鸿蒙移动终端,还针对鸿蒙PC进行了深度优化,同时预留了Flutter框架迁移路径。我们将从项目架构设计、核心功能实现、性能优化等多个维度,深入剖析这款应用的技术亮点和开发实践。
一、项目概览:打造鸿蒙生态的AI知识问答新体验
1.1 应用定位与核心价值
本应用是一款面向鸿蒙生态的AI百科问答助手,旨在为用户提供便捷、准确、智能的知识查询服务。应用融合了现代UI设计理念与先进的AI技术,实现了以下核心价值:
- 全场景适配:同时支持鸿蒙手机、平板和PC设备,一次开发,多端运行
- 智能问答:基于内置Mock知识库,覆盖20+热门科技话题,离线即可使用
- 本地持久化:提问历史自动保存,随时回顾学习内容
- 极简界面:问题输入区与问答结果区分隔清晰,操作直观
- 流畅体验:加载状态提示、平滑滚动,打造良好交互
1.2 技术栈选择
在技术栈选择上,我们经过了深入的调研和权衡:
| 维度 | 技术选型 | 选型理由 |
|---|---|---|
| 开发语言 | ArkTS | 鸿蒙原生语言,性能优异,生态支持完善 |
| 框架 | HarmonyOS SDK | 鸿蒙官方SDK,提供丰富的UI组件和系统能力 |
| 状态管理 | @State | 轻量级响应式状态管理方案,适合单页面应用 |
| 数据存储 | @ohos.data.preferences | 轻量级键值存储,适合历史记录持久化 |
| UI设计 | Material Design | 现代设计语言,跨平台一致性体验 |
1.3 项目架构
应用采用单文件架构,所有代码集中在Index.ets中,结构清晰,易于维护:
entry/src/main/ets/pages/Index.ets
├── 接口定义(QARecord、MockEntry)
├── 状态管理(@State装饰器)
├── Mock知识库(22条百科知识)
├── 生命周期方法(aboutToAppear)
├── 业务逻辑(提问、清空、历史管理)
├── 数据持久化(loadHistory、saveHistory)
└── UI渲染(build方法)
二、核心技术实现:从接口定义到状态管理
2.1 接口定义
应用定义了两个核心接口,严格遵循ArkTS语法规范,禁止使用any类型:
interface QARecord {
question: string;
answer: string;
timestamp: number;
}
interface MockEntry {
keyword: string;
answer: string;
}
设计亮点:
- 明确的数据结构,便于类型检查和维护
- 支持JSON序列化/反序列化,便于本地存储
QARecord包含时间戳,支持历史记录排序和展示
2.2 状态管理
应用仅使用@State装饰器实现轻量级状态管理:
@State question: string = ''; // 当前输入的问题
@State answer: string = ''; // AI返回的答案
@State isLoading: boolean = false; // 加载状态
@State history: QARecord[] = []; // 历史记录列表
@State errorText: string = ''; // 错误提示文本
状态管理优势:
- 响应式更新:状态变化自动触发UI更新
- 轻量级:无需引入复杂的状态管理库
- 直观易懂:状态与UI组件直接绑定
2.3 Mock知识库
应用内置了22条百科知识,覆盖人工智能、计算机、互联网、区块链、量子计算等热门科技话题:
private readonly MOCK_ANSWERS: MockEntry[] = [
{ keyword: '人工智能', answer: '人工智能(Artificial Intelligence,简称AI)是计算机科学的一个分支...' },
{ keyword: '计算机', answer: '计算机是一种能够按照程序运行,自动、高速处理海量数据的现代化智能电子设备...' },
{ keyword: '互联网', answer: '互联网(Internet)是指21世纪之初网络与网络之间所串连成的庞大网络...' },
// ... 更多知识条目
];
Mock方案优势:
- 离线可用:无需网络连接即可运行
- 快速响应:本地匹配,毫秒级响应
- 便于截图:内容固定,截图展示效果稳定
- 预留扩展:可随时接入真实AI服务
2.4 数据持久化
应用使用@ohos.data.preferences实现本地数据持久化:
private async saveHistory(): Promise<void> {
if (!this.context) {
return;
}
try {
let prefs = await this.getPreferences();
let stringList: Array<string> = [];
for (let i = 0; i < this.history.length; i++) {
let record = this.history[i];
stringList.push(JSON.stringify(record));
}
await prefs.put(this.STORAGE_KEY, JSON.stringify(stringList));
await prefs.flush();
} catch (error) {
console.error('保存历史记录失败');
}
}
private async loadHistory(): Promise<void> {
if (!this.context) {
return;
}
try {
let prefs = await this.getPreferences();
let jsonStr: string = await prefs.get(this.STORAGE_KEY, '') as string;
if (jsonStr.length > 0) {
let stringList: Array<string> = JSON.parse(jsonStr) as Array<string>;
let records: QARecord[] = [];
for (let i = 0; i < stringList.length; i++) {
let str: string = stringList[i];
let data: QARecord = JSON.parse(str) as QARecord;
records.push(data);
}
this.history = records;
}
} catch (error) {
console.error('加载历史记录失败');
}
}
持久化设计要点:
- 字符串数组存储:避免直接存储对象数组导致的类型问题
- Context注入:通过
getContext(this)获取上下文 - 异常处理:完善的try-catch,保证应用稳定性
- 单例模式:Preferences实例缓存,避免重复创建
三、UI开发:打造极简而不简单的用户界面
3.1 界面布局设计
应用界面分为上下两个区域:
问题输入区:
- 标题栏(蓝色背景,白色文字)
- 输入标签
- TextInput输入框
- 错误提示文本
- 提问按钮和清空按钮
问答结果区:
- 结果标题和历史记录计数
- 加载状态提示
- AI回答内容展示
- 历史记录列表(按时间倒序排列)
3.2 核心UI组件
应用严格遵守约束,仅使用系统原生组件:
build() {
Column() {
// 标题栏
Row() {
Text('AI百科问答助手')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
}
.width('100%')
.height(56)
.padding({ left: 16, right: 16 })
.backgroundColor('#1976D2')
// 输入区
Column() {
Text('输入问题')
.fontSize(16)
.fontColor('#333333')
.margin({ top: 16, left: 16, right: 16, bottom: 8 })
TextInput({ placeholder: '请输入你想了解的知识点...', text: this.question })
.width('100%')
.height(56)
.fontSize(15)
.padding({ left: 12, right: 12 })
.backgroundColor('#FFFFFF')
.borderRadius(8)
.borderWidth(1)
.borderColor('#E0E0E0')
.margin({ left: 16, right: 16 })
.onChange((value: string) => {
this.question = value;
})
// 按钮区域
Row() {
Button('提问')
.width('45%')
.height(44)
.fontSize(16)
.fontColor('#FFFFFF')
.backgroundColor('#1976D2')
.borderRadius(8)
.enabled(!this.isLoading)
.onClick(() => {
this.handleSubmit();
})
Blank().width('10%')
Button('清空')
.width('45%')
.height(44)
.fontSize(16)
.fontColor('#666666')
.backgroundColor('#F5F5F5')
.borderRadius(8)
.onClick(() => {
this.clearAll();
})
}
.width('100%')
.margin({ left: 16, right: 16, top: 16 })
}
// 结果区(带滚动)
Scroll() {
Column() {
// 结果内容和历史记录
}
}
.scrollable(ScrollDirection.Vertical)
.width('100%')
.layoutWeight(1)
}
}
UI设计亮点:
- 极简风格:仅使用Column、Row、Text、TextInput、Button组件
- 响应式布局:使用layoutWeight实现自适应
- 滚动支持:结果区使用Scroll组件,支持内容溢出滚动
- 状态驱动:根据isLoading、answer等状态动态渲染
3.3 交互逻辑
应用实现了完整的交互流程:
提问流程:
- 用户输入问题
- 点击"提问"按钮
- 验证输入非空
- 设置加载状态
- 延迟500ms模拟网络请求
- 匹配Mock答案
- 保存到历史记录
- 清空输入框
清空流程:
- 点击"清空"按钮
- 清空输入框、答案、错误提示
- 清空历史记录
- 保存空历史到本地存储
四、鸿蒙PC适配策略
4.1 响应式设计
随着鸿蒙PC的发布,应用需要针对大屏设备进行优化:
// 使用百分比和vp单位实现响应式布局
.width('100%') // 宽度自适应
.height(56) // 固定高度
.layoutWeight(1) // 剩余空间填充
.padding({ left: 16, right: 16 }) // 固定内边距
4.2 大屏优化建议
针对鸿蒙PC大屏,可以进行以下优化:
// 建议的PC端优化方案
// 1. 增加输入框宽度
TextInput()
.width('80%') // PC端可使用更宽的输入框
.height(56)
// 2. 使用Flex布局优化按钮区域
Row() {
Button('提问')
.width(120) // PC端固定按钮宽度
Blank().width(20)
Button('清空')
.width(120)
}
.justifyContent(FlexAlign.Center) // 居中对齐
// 3. 结果区优化
Scroll() {
Column() {
// 历史记录可使用网格布局
}
}
.width('70%') // PC端限制内容宽度
.margin({ left: '15%', right: '15%' }) // 居中显示
4.3 多窗口支持
鸿蒙PC支持多窗口模式,应用需要处理窗口大小变化:
aboutToAppear(): void {
this.context = getContext(this) as common.Context;
this.loadHistory();
// 监听窗口尺寸变化(伪代码,需根据实际API调整)
/*
window.on('resize', () => {
// 根据窗口大小调整布局
this.adjustLayout();
});
*/
}
五、Flutter框架迁移路径
虽然当前应用使用ArkTS开发,但我们预留了完整的Flutter框架迁移路径。以下是迁移方案:
5.1 项目结构映射
| ArkTS模块 | Flutter模块 | 说明 |
|---|---|---|
| Index.ets | lib/main.dart | 主页面 |
| QARecord接口 | QARecord类 | 数据模型 |
| MockEntry接口 | MockEntry类 | Mock数据结构 |
| @State | StatefulWidget | 状态管理 |
| preferences | shared_preferences | 本地存储 |
5.2 状态管理迁移
ArkTS @State → Flutter StatefulWidget
// Flutter版本的状态管理
class _IndexState extends State<Index> {
String question = '';
String answer = '';
bool isLoading = false;
List<QARecord> history = [];
String errorText = '';
void handleSubmit() async {
setState(() {
isLoading = true;
});
// AI服务调用...
setState(() {
isLoading = false;
answer = mockAnswer;
});
}
Widget build(BuildContext context) {
// UI构建...
}
}
5.3 UI组件迁移
ArkTS组件 → Flutter Widget
// Flutter版本的输入区
Column(
children: [
const Text('输入问题'),
TextField(
decoration: const InputDecoration(
hintText: '请输入你想了解的知识点...',
border: OutlineInputBorder(),
),
onChanged: (value) {
setState(() {
question = value;
});
},
),
Row(
children: [
ElevatedButton(
onPressed: handleSubmit,
child: const Text('提问'),
),
const SizedBox(width: 16),
TextButton(
onPressed: clearAll,
child: const Text('清空'),
),
],
),
],
)
5.4 优势对比
| 维度 | ArkTS | Flutter |
|---|---|---|
| 性能 | 原生性能,接近系统应用 | JIT/AOT编译,性能优秀 |
| 生态 | 鸿蒙原生API丰富 | 跨平台,社区成熟 |
| 开发效率 | 声明式UI,学习曲线平缓 | 热重载,开发效率高 |
| 跨平台 | 鸿蒙全场景 | iOS/Android/Web/Desktop |
| 包体积 | 较小,只包含必要组件 | 较大,包含完整框架 |
六、网络API预留方案
应用预留了真实AI服务对接接口,可随时接入外部API:
// 预留的真实API对接代码
/*
import http from '@ohos.net.http';
private async fetchOnlineAnswer(question: string): Promise<string> {
let httpRequest = http.createHttp();
let response = await httpRequest.request(
'https://api.example.com/chat/completions',
{
method: http.RequestMethod.POST,
header: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
},
extraData: {
'question': question
}
}
);
let result = JSON.parse(response.result as string);
return result['answer'];
}
*/
API集成步骤:
- 取消注释上述代码
- 替换API地址和密钥
- 修改
handleSubmit方法调用fetchOnlineAnswer - 添加网络权限配置(已在module.json5中配置)
七、权限配置
应用已在module.json5中添加网络权限配置:
"module": {
"name": "entry",
"type": "entry",
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
],
// ... 其他配置
}
权限说明:
ohos.permission.INTERNET:允许应用访问网络- 配置位置:module.json5的module层级
- 自动向系统申请,无需用户手动授权
八、性能优化与最佳实践
8.1 性能优化策略
列表渲染优化
ForEach(this.history, (record: QARecord) => {
// 渲染历史记录项
}, (record: QARecord) => record.timestamp.toString())
优化要点:
- 唯一ID:确保ForEach的第三个参数返回唯一标识
- 懒加载:ForEach默认支持懒加载,只渲染可见区域
状态更新优化
// 使用不可变更新策略
let newHistory: QARecord[] = [record];
for (let i = 0; i < this.history.length; i++) {
newHistory.push(this.history[i]);
}
this.history = newHistory;
优化要点:
- 不可变更新:创建新数组触发响应式更新
- 精准更新:只更新需要改变的数据
8.2 最佳实践总结
代码规范
- 接口定义:使用interface显式定义数据结构,禁止any类型
- 状态管理:使用@State装饰器管理组件状态
- 异常处理:所有异步操作都要有try-catch
- 命名规范:使用驼峰命名法,接口名首字母大写
用户体验
- 加载状态:所有耗时操作都要有加载提示
- 错误提示:友好的错误信息,引导用户纠正
- 输入验证:非空检查,避免无效请求
九、应用特点总结
9.1 技术特点
- ✅ 纯ArkTS实现:不依赖第三方框架,代码轻量化
- ✅ 严格类型检查:全部使用interface接口,禁止any类型
- ✅ 响应式状态管理:使用@State实现数据驱动UI
- ✅ 本地持久化:基于@ohos.data.preferences保存历史记录
- ✅ 离线可用:内置Mock知识库,无需网络即可运行
9.2 功能特点
- ✅ 智能问答:输入任意知识点问题,AI给出条理清晰的解答
- ✅ 清空功能:一键清空所有内容和历史记录
- ✅ 历史记录:自动保存提问历史,支持回顾
- ✅ 加载状态:友好的加载提示,提升交互体验
- ✅ 输入验证:非空检查,避免无效操作
9.3 适配特点
- ✅ 鸿蒙全场景:支持手机、平板、PC等多种设备
- ✅ 极简UI:仅使用系统原生组件,界面简洁
- ✅ 响应式布局:自适应不同屏幕尺寸
- ✅ 流畅滚动:支持内容溢出滚动
十、未来规划与扩展方向
10.1 功能扩展
- 语音输入:集成鸿蒙语音识别能力
- 图片识别:支持拍照识别知识点
- 多语言支持:支持中英文切换
- 知识分类:按主题分类展示知识
10.2 技术升级
- 真实AI对接:接入OpenAI、百度文心一言等真实AI服务
- 数据库升级:从preferences升级到关系型数据库
- 性能监控:集成性能监控和日志系统
- 单元测试:添加完整的单元测试用例
10.3 生态整合
- 鸿蒙原子化服务:开发原子化服务版本
- 多设备协同:支持手机、平板、PC多端协同
- HarmonyOS NEXT适配:适配HarmonyOS NEXT纯血鸿蒙
十一、总结
本文详细介绍了基于ArkTS开发的AI百科问答助手应用,涵盖了从接口定义、状态管理、UI开发到数据持久化的全过程。应用严格遵守鸿蒙开发规范,仅使用系统原生组件,实现了完整的问答功能和本地存储能力。
核心技术亮点:
- 轻量级架构:单文件实现,代码结构清晰
- 严格类型检查:全部使用interface接口,禁止any类型
- 响应式状态管理:使用@State实现数据驱动UI更新
- 离线Mock方案:内置22条百科知识,无需网络即可运行
- 本地持久化:基于@ohos.data.preferences保存历史记录
鸿蒙生态优势:
- 全场景适配:一次开发,多端运行
- 原生性能:接近系统级应用的性能表现
- 丰富API:访问鸿蒙系统的全部能力
无论是作为一款独立的AI问答应用,还是作为鸿蒙应用开发的学习案例,本项目都具有很高的参考价值。开发者可以基于此项目快速构建自己的AI应用,或学习鸿蒙应用开发的最佳实践。
附录:核心代码
完整Index.ets代码
import preferences from '@ohos.data.preferences';
import common from '@ohos.app.ability.common';
interface QARecord {
question: string;
answer: string;
timestamp: number;
}
interface MockEntry {
keyword: string;
answer: string;
}
@Entry
@Component
struct Index {
@State question: string = '';
@State answer: string = '';
@State isLoading: boolean = false;
@State history: QARecord[] = [];
@State errorText: string = '';
private preferencesInst: preferences.Preferences | null = null;
private context: common.Context | null = null;
private readonly STORAGE_KEY: string = 'qa_history';
private readonly MOCK_ANSWERS: MockEntry[] = [
{ keyword: '人工智能', answer: '人工智能(Artificial Intelligence,简称AI)是计算机科学的一个分支...' },
{ keyword: '计算机', answer: '计算机是一种能够按照程序运行...' },
// ... 更多知识条目
];
aboutToAppear(): void {
this.context = getContext(this) as common.Context;
this.loadHistory();
}
private async loadHistory(): Promise<void> {
if (!this.context) {
return;
}
try {
let prefs = await this.getPreferences();
let jsonStr: string = await prefs.get(this.STORAGE_KEY, '') as string;
if (jsonStr.length > 0) {
let stringList: Array<string> = JSON.parse(jsonStr) as Array<string>;
let records: QARecord[] = [];
for (let i = 0; i < stringList.length; i++) {
let str: string = stringList[i];
let data: QARecord = JSON.parse(str) as QARecord;
records.push(data);
}
this.history = records;
}
} catch (error) {
console.error('加载历史记录失败');
}
}
private async saveHistory(): Promise<void> {
if (!this.context) {
return;
}
try {
let prefs = await this.getPreferences();
let stringList: Array<string> = [];
for (let i = 0; i < this.history.length; i++) {
let record = this.history[i];
stringList.push(JSON.stringify(record));
}
await prefs.put(this.STORAGE_KEY, JSON.stringify(stringList));
await prefs.flush();
} catch (error) {
console.error('保存历史记录失败');
}
}
private async handleSubmit(): Promise<void> {
if (this.question.trim().length === 0) {
this.errorText = '请输入问题';
return;
}
this.errorText = '';
this.answer = '';
this.isLoading = true;
await this.delay(500);
let mockAnswer: string = this.getMockAnswer(this.question);
this.isLoading = false;
this.answer = mockAnswer;
let record: QARecord = {
question: this.question.trim(),
answer: mockAnswer,
timestamp: Date.now()
};
let newHistory: QARecord[] = [record];
for (let i = 0; i < this.history.length; i++) {
newHistory.push(this.history[i]);
}
this.history = newHistory;
await this.saveHistory();
this.question = '';
}
build() {
Column() {
// 标题栏
Row() {
Text('AI百科问答助手')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
}
.width('100%')
.height(56)
.padding({ left: 16, right: 16 })
.backgroundColor('#1976D2')
// 输入区和结果区...
}
.width('100%')
.height('100%')
}
}
权限配置
{
"module": {
"name": "entry",
"type": "entry",
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
}
参考资料:
更多推荐




所有评论(0)