HarmonyOS 通讯录管家 —— 基于 ArkTS + SQLite 的联系人管理应用
HarmonyOS 通讯录管家 —— 基于 ArkTS + SQLite 的联系人管理应用
一个完整的 HarmonyOS(鸿蒙)原生通讯录管理应用,采用 ArkTS 语言开发,使用
@ohos.data.relationalStore(SQLite)进行数据持久化,实现了联系人的增删改查、分组管理、模糊搜索、生日智能提醒、快速拨号等核心功能。
运行效果




目录
项目亮点
- 纯 ArkTS 原生开发:完全使用 HarmonyOS ArkTS 语言和 ArkUI 声明式框架,无第三方依赖
- SQLite 本地持久化:基于
@ohos.data.relationalStore实现完整的数据库 CRUD,数据存储在设备本地 - 清晰的三层架构:数据类型定义层(
ContactTypes.ets)→ 数据库操作层(ContactDB.ets)→ UI 展示层(Index.ets/ContactForm.ets),职责分明,易于维护 - 智能生日提醒:自动计算 7 天内即将过生日的联系人,在主页顶部以醒目横幅提示
- ArkTS 严格模式适配:解决了
arkts-no-any-unknown、arkts-no-untyped-obj-literals、arkts-no-utility-types等编译限制问题,可作为 ArkTS 类型系统实践参考
技术栈
| 类别 | 技术 | 说明 |
|---|---|---|
| 开发语言 | ArkTS | HarmonyOS 原生应用开发语言 |
| UI 框架 | ArkUI | 声明式 UI 开发框架 |
| 数据库 | @ohos.data.relationalStore | SQLite 关系型数据库 |
| 路由导航 | router | 页面间跳转与参数传递 |
| UI 组件 | promptAction | Toast 提示与确认对话框 |
| 系统能力 | Want / UIAbilityContext | 拉起系统拨号界面 |
| 构建工具 | Hvigor | HarmonyOS 应用构建工具 |
| 目标平台 | HarmonyOS(Stage 模型) | API 12+,SDK 6.1.1 |
项目结构
ArkTS-SQLite/
├── entry/src/main/ets/
│ ├── entryability/
│ │ └── EntryAbility.ets # 应用入口 Ability
│ ├── model/
│ │ ├── ContactTypes.ets # 数据类型定义 + 工具函数
│ │ └── ContactDB.ets # SQLite 数据库操作层
│ └── pages/
│ ├── Index.ets # 主页面(联系人列表 + 搜索 + 生日提醒)
│ └── ContactForm.ets # 表单页面(新增 / 编辑联系人)
├── entry/src/main/resources/ # 资源文件(颜色、字符串、图片等)
└── build-profile.json5 # 项目构建配置
核心文件说明:
| 文件 | 职责 | 核心内容 |
|---|---|---|
ContactTypes.ets | 数据层 | 定义 Contact、ContactGroup、ContactInsertParam 等接口,提供分组常量和工具函数 |
ContactDB.ets | 数据库层 | 封装 SQLite 建表、增删改查、模糊搜索、生日查询等全部数据库操作 |
Index.ets | 主页面 | 联系人分组列表、搜索栏、生日提醒横幅、快速拨号、空状态展示 |
ContactForm.ets | 表单页面 | 新增/编辑联系人的完整表单,含输入验证、分组选择、生日日期选择器 |
功能特性
1. 联系人管理(CRUD)
- 新增:填写姓名、电话、邮箱、分组、生日、备注,支持收藏标记
- 编辑:通过路由传递联系人 JSON 数据,表单自动回填,修改后更新数据库
- 删除:弹出确认对话框,确认后从数据库删除并实时刷新列表
- 查询:按收藏降序 → 分组升序 → 姓名升序排列展示
2. 分组管理
预设 5 个分组(带 emoji 图标),主页按分组聚合展示:
| 分组 | 图标 | ID |
|---|---|---|
| 家人 | 👪 | 1 |
| 朋友 | 🤝 | 2 |
| 同事 | 💼 | 3 |
| 同学 | 🎓 | 4 |
| 其他 | 📍 | 5 |
3. 搜索功能
- 支持按姓名或电话号码模糊匹配(SQL
LIKE语义) - 使用
RdbPredicates的beginWrap/contains/or/endWrap组合复杂查询条件 - 搜索结果仍按分组聚合展示,带清除按钮
4. 生日智能提醒
- 自动计算 7 天内即将过生日的联系人
- 主页顶部显示红色提醒横幅,带 Badge 数量标记
- 点击"查看"弹出对话框,列出具体联系人生日信息
- 联系人卡片中对生日临近者显示蛋糕 🎂 标记
5. 快速拨号
- 联系人卡片右侧电话按钮
- 通过
ohos.want.action.dialIntent 拉起系统拨号界面
6. 表单验证
- 姓名:必填,不超过 20 字符
- 电话:必填,正则验证
[\d\-+\s()]{5,20} - 邮箱:非必填,如有则验证格式
- 未保存修改离开时弹出确认提示
数据库设计
数据库文件:contact_manager.db,安全级别 S1
contact 表(联系人)
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | INTEGER | PRIMARY KEY AUTOINCREMENT | 主键,自增 |
| name | TEXT | NOT NULL | 姓名 |
| phone | TEXT | NOT NULL | 电话号码 |
| TEXT | DEFAULT ‘’ | 邮箱地址 | |
| group_id | INTEGER | DEFAULT 5 | 分组 ID,默认"其他" |
| birthday | TEXT | DEFAULT ‘’ | 生日,格式 yyyy-MM-dd |
| is_favorite | INTEGER | DEFAULT 0 | 是否收藏(0 否 / 1 是) |
| remark | TEXT | DEFAULT ‘’ | 备注 |
| created_at | TEXT | DEFAULT ‘’ | 创建时间 |
contact_group 表(分组)
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | INTEGER | PRIMARY KEY AUTOINCREMENT | 主键,自增 |
| name | TEXT | NOT NULL | 分组名称 |
| icon | TEXT | DEFAULT ‘’ | 分组图标 |
核心代码解析
1. 数据库初始化与建表
使用 relationalStore.getRdbStore() 获取数据库实例,通过 executeSql() 执行建表语句:
async init(): Promise<void> {
if (this.store) return;
const config: relationalStore.StoreConfig = {
name: DB_NAME,
securityLevel: relationalStore.SecurityLevel.S1,
};
this.store = await relationalStore.getRdbStore(this.context, config);
await this.store.executeSql(CREATE_TABLE_GROUP);
await this.store.executeSql(CREATE_TABLE_CONTACT);
await this.initGroups(); // 首次启动时插入预设分组
}
2. 联系人 CRUD 操作
插入联系人 —— 将表单数据映射为数据库字段,自动填充创建时间:
async insertContact(contact: ContactInsertParam): Promise<number> {
if (!this.store) throw new Error('数据库未初始化');
const now = new Date();
const dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
const row: relationalStore.ValuesBucket = {
name: contact.name,
phone: contact.phone,
email: contact.email || '',
group_id: contact.groupId || 5,
birthday: contact.birthday || '',
is_favorite: contact.isFavorite || 0,
remark: contact.remark || '',
created_at: dateStr,
};
return await this.store.insert(TABLE_CONTACT, row);
}
查询并转换结果集 —— 将 ResultSet 逐行读取并映射为 Contact 对象:
private resultSetToContacts(resultSet: relationalStore.ResultSet): Contact[] {
const contacts: Contact[] = [];
try {
while (resultSet.goToNextRow()) {
contacts.push({
id: resultSet.getLong(resultSet.getColumnIndex('id')),
name: resultSet.getString(resultSet.getColumnIndex('name')),
phone: resultSet.getString(resultSet.getColumnIndex('phone')),
email: resultSet.getString(resultSet.getColumnIndex('email')) || '',
groupId: resultSet.getLong(resultSet.getColumnIndex('group_id')),
birthday: resultSet.getString(resultSet.getColumnIndex('birthday')) || '',
isFavorite: resultSet.getLong(resultSet.getColumnIndex('is_favorite')),
remark: resultSet.getString(resultSet.getColumnIndex('remark')) || '',
createdAt: resultSet.getString(resultSet.getColumnIndex('created_at')) || '',
});
}
} catch (e) {
console.error('resultSetToContacts error:', JSON.stringify(e));
} finally {
resultSet.close();
}
return contacts;
}
3. 模糊搜索实现
使用 RdbPredicates 的组合查询方法,实现姓名或电话的模糊匹配:
async searchContacts(keyword: string): Promise<Contact[]> {
if (!this.store) throw new Error('数据库未初始化');
const predicates = new relationalStore.RdbPredicates(TABLE_CONTACT);
predicates.beginWrap();
predicates.contains('name', keyword); // 姓名包含关键词
predicates.or(); // OR 条件
predicates.contains('phone', keyword); // 电话包含关键词
predicates.endWrap();
predicates.orderByDesc('is_favorite');
predicates.orderByAsc('name');
const resultSet: relationalStore.ResultSet = await this.store.query(predicates, [
'id', 'name', 'phone', 'email', 'group_id',
'birthday', 'is_favorite', 'remark', 'created_at'
]);
return this.resultSetToContacts(resultSet);
}
4. 生日智能提醒
查询所有联系人后,在应用层过滤 7 天内即将过生日的联系人:
async queryUpcomingBirthdays(): Promise<Contact[]> {
if (!this.store) throw new Error('数据库未初始化');
const all = await this.queryAllContacts();
return all.filter(c => {
if (!c.birthday) return false;
const parts = c.birthday.split('-');
if (parts.length < 3) return false;
const today = new Date();
const bd = new Date(today.getFullYear(), parseInt(parts[1]) - 1, parseInt(parts[2]));
const diffDays = Math.ceil((bd.getTime() - today.getTime()) / (1000 * 60 * 60 * 24));
return diffDays >= 0 && diffDays <= 7;
});
}
5. 声明式 UI 列表渲染
使用 ForEach 按分组聚合展示联系人,每个联系人卡片包含头像、信息和操作按钮:
ForEach(this.groupedContacts, (item: GroupWithContacts) => {
// 分组标题
ListItem() {
Row() {
Text(`${item.group.icon} ${item.group.name}`)
.fontSize(16)
.fontWeight(FontWeight.Medium)
}
}
// 该分组下的联系人列表
ForEach(item.contacts, (contact: Contact) => {
ListItem() {
this.buildContactItem(contact)
}
})
})
6. 路由传参与表单回填
通过 router.pushUrl 传递 JSON 序列化的联系人数据,表单页面解析后自动回填:
// 主页面 —— 传递参数
onEditContact(contact: Contact): void {
const param: RouterParams = { mode: 'edit', contact: JSON.stringify(contact) };
router.pushUrl({ url: 'pages/ContactForm', params: param });
}
// 表单页面 —— 解析参数并回填
aboutToAppear(): void {
const params = router.getParams() as Record<string, string>;
if (params) {
this.mode = params['mode'] || 'add';
if (this.mode === 'edit' && params['contact']) {
const contactData = JSON.parse(params['contact']) as Contact;
this.contactName = contactData.name;
this.contactPhone = contactData.phone;
// ... 其他字段回填
}
}
}
ArkTS 编译避坑指南
在开发过程中遇到并解决了多个 ArkTS 严格模式下的编译问题,总结如下:
| 错误码 | 错误描述 | 解决方案 |
|---|---|---|
| arkts-no-any-unknown | 禁止使用 any / unknown 类型 | 为所有变量添加显式类型标注,如 const count: number = ... |
| arkts-no-untyped-obj-literals | 对象字面量必须对应显式声明的接口 | 先声明接口(如 RouterParams),再用接口类型约束变量 |
| arkts-no-utility-types | 不支持 Omit、Pick 等 TypeScript 工具类型 | 手动定义等效接口(如 ContactInsertParam) |
| arkts-no-aliases-by-index | 不支持索引访问类型 | 使用 ForEach 回调参数直接声明类型,如 (group: ContactGroup) |
| arkts-no-type-query | typeof 仅允许在表达式上下文中使用 | 避免在类型位置使用 typeof,改用显式接口声明 |
| Button 多子组件 | Button 组件只能有一个子组件 | 用 Row / Column 包裹多个子组件 |
经验总结:ArkTS 的类型系统比 TypeScript 更严格,开发时应始终使用显式类型标注,避免依赖类型推断;不使用
any、不使用工具类型、不使用typeof在类型位置。
运行环境
| 项目 | 要求 |
|---|---|
| 开发工具 | DevEco Studio 5.0+ |
| HarmonyOS SDK | API 12+,SDK 6.1.1 |
| 目标设备 | HarmonyOS 手机(phone) |
| 编译模式 | Stage 模型 |
| 语言 | ArkTS(TypeScript 超集) |
快速开始
- 使用 DevEco Studio 打开项目
- 连接 HarmonyOS 设备或启动模拟器
- 点击 Run 按钮运行应用
- 应用首次启动会自动创建数据库和预设分组数据
总结与展望
本项目是一个典型的 HarmonyOS 原生应用实践,通过通讯录管理场景覆盖了以下核心知识点:
- ArkTS 语言特性:接口定义、异步编程(async/await)、类型系统
- ArkUI 声明式 UI:组件嵌套、状态管理(@State)、ForEach 列表渲染、自定义 Builder
- SQLite 数据持久化:relationalStore 建表、增删改查、复合条件查询
- 页面路由:pushUrl 传参、getParams 接收参数、back 返回
- 系统能力调用:Want Intent 拉起系统拨号
- ArkTS 编译限制适配:严格模式下的类型系统实践
后续可扩展方向:
- 接入
@ohos.contacts系统通讯录能力,实现与系统通讯录的数据同步 - 添加联系人头像拍照/相册选择功能
- 实现数据备份与恢复(利用已声明的
EntryBackupAbility) - 接入分布式数据管理,实现多设备间通讯录同步
- 添加更多分组管理功能(自定义分组、分组排序)
项目技术栈:HarmonyOS / ArkTS / ArkUI / SQLite / relationalStore / Stage 模型
更多推荐

所有评论(0)