OpenHarmony 英语学习 App 实战:近场快传、跨端续学与全场景学习闭环
OpenHarmony 英语学习 App 实战:近场快传、跨端续学与全场景学习闭环
摘要
英语学习天然是一个跨场景任务:早上通勤路上用手机背几个单词,晚上回家用平板继续做语法练习,周末和同学互相分享学习成果。单设备 App 很容易形成“学习孤岛”,而 OpenHarmony/HarmonyOS 的全场景能力,正好可以把学习数据、学习状态和学习成果连接起来。📱➡️📲
本文以我的英语学习项目「英语视界 YingYu」为例,分享如何围绕学习场景设计 近场快传、跨端续学、分布式数据同步以及实况窗扩展思路。项目中已经包含 ShareKit 分享监听、DistributedKVStore 初始化、UIAbility onContinue() 续接能力等代码基础,非常适合写成一套全场景学习闭环。
一、全场景学习体验应该解决什么问题?
在教育类应用里,全场景并不是为了“炫技术”,而是为了解决真实学习问题:
- 用户换设备后,学习进度不要丢;
- 今日学习任务可以在系统级入口看到;
- 学习报告可以快速分享给同学或家长;
- 手机和平板之间能自然接续;
- 网络不稳定时,基础学习数据仍然可用。
因此「英语视界」的全场景设计围绕三个关键词展开:
- 分享:用碰一碰/隔空传送快速分享学习报告;
- 同步:用分布式数据能力同步轻量学习数据;
- 续接:用 UIAbility 生命周期完成跨设备学习状态恢复。
二、近场快传:碰一碰分享学习报告
项目中封装了 ShareService.ts,通过 @kit.ShareKit 的 harmonyShare 监听近场分享事件。
import { systemShare, harmonyShare } from '@kit.ShareKit'
import { uniformTypeDescriptor as utd } from '@kit.ArkData'
export function registerKnockShare(callback: (target: harmonyShare.SharableTarget) => void): void {
try {
harmonyShare.on('knockShare', (target: harmonyShare.SharableTarget) => {
console.info('Triggered Knock Share')
callback(target)
})
} catch (err) {
console.error(`Register knock share failed: ${JSON.stringify(err)}`)
}
}
除了碰一碰,还注册了隔空传送能力:
export function registerGesturesShare(callback: (target: harmonyShare.SharableTarget) => void): void {
try {
harmonyShare.on('gesturesShare', (target: harmonyShare.SharableTarget) => {
console.info('Triggered Gestures Share')
callback(target)
})
} catch (err) {
console.error(`Register gestures share failed: ${JSON.stringify(err)}`)
}
}
对于学习 App 来说,近场分享非常适合这些场景:
- 同学之间分享今日学习成果;
- 给家长展示连续打卡天数;
- 分享自定义生词本;
- 分享成就徽章;
- 分享待复习任务。
三、生命周期中注册和注销分享监听
在 MainTabsV2.ets 中,页面出现时注册监听,页面销毁时注销监听。
aboutToAppear(): void {
this.isTabletDevice = isTablet()
this.isPhone = !this.isTabletDevice && deviceInfo.deviceType === 'phone'
this.loadData()
this.registerShareListeners()
}
aboutToDisappear(): void {
this.unregisterShareListeners()
}
注册逻辑如下:
registerShareListeners(): void {
if (this.isPhone) {
registerKnockShare((target: harmonyShare.SharableTarget) => {
handleShareFromKnock(target, 'learning_record')
})
registerGesturesShare((target: harmonyShare.SharableTarget) => {
handleShareFromKnock(target, 'learning_record')
})
}
}
注销逻辑:
unregisterShareListeners(): void {
if (this.isPhone) {
unregisterKnockShare()
unregisterGesturesShare()
}
}
这里有两个设计点:
- 只在手机设备上启用近场分享;
- 页面离开时及时注销监听,避免资源长期占用。
这种写法更符合移动端能力使用习惯,也便于后续扩展到不同设备。
四、学习报告内容:不要直接分享冷冰冰的 JSON
分享内容不是技术数据,而是给用户看的学习成果。项目中定义了 LearningRecordShareData:
export interface LearningRecordShareData {
totalWords: number
totalDays: number
consecutiveDays: number
achievementsCount: number
achievementsTotal: number
customWordsCount: number
reviewDueCount: number
todayProgress: string
shareDate: string
}
然后把这些数据格式化成自然语言:
export function generateLearningRecordText(data: LearningRecordShareData): string {
return `🎓 我的英语学习报告\n\n📅 ${data.shareDate}\n\n` +
`✨ 已学单词: ${data.totalWords} 个\n` +
`📅 学习天数: ${data.totalDays} 天\n` +
`🔥 连续打卡: ${data.consecutiveDays} 天\n` +
`🏆 获得成就: ${data.achievementsCount}/${data.achievementsTotal}\n` +
`📚 自定义词: ${data.customWordsCount} 个\n` +
`🔄 待复习: ${data.reviewDueCount} 个\n\n` +
`💪 坚持学习,遇见更好的自己!\n` +
`📱 使用「趣味英语」App 一起学习吧~`
}
这样的分享文案比 JSON 更适合传播,因为它具备:
- 可读性;
- 成就感;
- 鼓励感;
- 社交表达价值。
五、真正发起分享:SharedData + target.share
当系统触发近场分享后,项目会生成文本内容,并包装成 SharedData。
export async function handleShareFromKnock(
target: harmonyShare.SharableTarget,
type: ShareContentType = 'learning_record'
): Promise<boolean> {
try {
const shareText = generateShareText(type)
const shareData: systemShare.SharedData = new systemShare.SharedData({
utd: utd.UniformDataType.TEXT,
content: shareText
})
target.share(shareData)
console.info('Knock/Gestures share initiated successfully')
return true
} catch (err) {
console.error(`Knock share failed: ${JSON.stringify(err)}`)
return false
}
}
这里使用 TEXT 类型,是因为学习报告天然适合复制、转发和保存。后续也可以继续扩展:
- 分享学习海报图片;
- 分享 AppLink;
- 分享某个单词本;
- 分享复习计划;
- 分享班级排行榜。
六、分布式 KV:让学习数据跟着设备走
除了分享,学习 App 更重要的是“连续性”。项目里通过 DistributedSync.ets 初始化分布式 KV。
import { distributedKVStore } from '@kit.ArkData'
const STORE_ID = 'yingyu_distributed_store'
let kvStore: distributedKVStore.SingleKVStore | null = null
export async function initDistributedStore(context: Context): Promise<void> {
const kvManagerConfig: distributedKVStore.KVManagerConfig = {
bundleName: 'ying.yu.app',
context: context
}
const kvManager = distributedKVStore.createKVManager(kvManagerConfig)
}
存储选项如下:
const options: distributedKVStore.Options = {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: true,
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
securityLevel: distributedKVStore.SecurityLevel.S1
}
kvStore = await kvManager.getKVStore<distributedKVStore.SingleKVStore>(
STORE_ID,
options
)
这里最重要的是:
autoSync: true:数据变更后支持自动同步;SINGLE_VERSION:适合轻量键值数据;SecurityLevel.S1:用于基础学习数据;backup: false:避免不必要的数据备份。
适合同步的数据包括:
- 已学单词 ID;
- 每日学习目标;
- 连续打卡天数;
- 成就解锁状态;
- 用户年级设置;
- 今日复习进度。
不建议同步的数据包括:
- 大段听力音频;
- 临时 UI 状态;
- 敏感身份信息;
- 可重新计算的缓存数据。
七、跨设备续接:手机学完,平板继续
项目还在 EntryAbility.ets 中实现了 onContinue(),用于跨设备迁移学习状态。
onContinue(wantParam: Record<string, Object>): AbilityConstant.OnContinueResult {
try {
const settings = AppStorage.get<string>('yingyu_settings') || ''
const progress = AppStorage.get<string>('yingyu_progress') || ''
const learnedWords = AppStorage.get<string>('yingyu_learned_words') || ''
wantParam['yingyu_settings'] = settings
wantParam['yingyu_progress'] = progress
wantParam['yingyu_learned_words'] = learnedWords
wantParam['yingyu_timestamp'] = Date.now().toString()
return AbilityConstant.OnContinueResult.AGREE
} catch (err) {
return AbilityConstant.OnContinueResult.MISMATCH
}
}
这个函数做的事情非常明确:把跨设备续接所需的最小数据写入 wantParam。
目标设备启动后,通过 onCreateWithWant() 恢复:
onCreateWithWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
this.onCreate(want, launchParam)
if (want.parameters) {
const settings = want.parameters['yingyu_settings'] as string
const progress = want.parameters['yingyu_progress'] as string
const learnedWords = want.parameters['yingyu_learned_words'] as string
if (settings) AppStorage.setOrCreate('yingyu_settings', settings)
if (progress) AppStorage.setOrCreate('yingyu_progress', progress)
if (learnedWords) AppStorage.setOrCreate('yingyu_learned_words', learnedWords)
}
}
这样就能实现:
- 手机上打开词汇学习;
- 切换到平板;
- 平板恢复学习设置和已学进度;
- 用户继续学习,不需要重新选择。
八、全场景体验的产品价值
如果只从技术上看,近场分享、分布式 KV、跨端续接是三个独立能力。但放到英语学习场景里,它们会形成闭环:
- 用户在手机上完成今日单词;
- 学习进度同步到分布式数据;
- 回家后用平板继续练听力;
- 学完后碰一碰分享学习报告;
- 家长或同学收到报告;
- 用户获得反馈和激励。
这就是“全场景”真正有价值的地方:它把学习行为从单点操作变成连续体验。
九、实况窗扩展思路
当前项目还没有完整接入实况窗,但学习场景非常适合做实况窗扩展。可以考虑展示:
- 今日任务完成度:8/10 个单词;
- 待复习数量:还有 5 个单词;
- 听力播放状态:正在播放第 3 条材料;
- 复习倒计时:下次复习还有 20 分钟;
- 打卡提醒:今日还未完成目标。
建议后续抽象一个统一状态模型:
interface LearningLiveState {
taskTitle: string
current: number
total: number
status: 'learning' | 'reviewing' | 'listening' | 'completed'
updatedAt: number
}
然后从 DailyTask、ReviewCenter、ListeningContent 等页面收集状态,再对接系统级展示。这样实况窗不会变成孤立功能,而是学习流程的自然延伸。
十、实现时需要注意的点
1. 分享监听要跟生命周期绑定
不要在全局长期注册监听。页面出现时注册,离开时注销,逻辑更清楚,也更节省资源。
2. 分享内容要可读
学习报告面向用户,不是面向程序。优先分享自然语言文本,再考虑 JSON 或结构化数据。
3. 同步数据要轻量
分布式 KV 适合轻量状态,不适合大文件。同步学习进度、设置、成就更合适。
4. 续接数据要最小化
跨设备续接只带必要字段,避免把无关数据一起迁移。
5. 实况窗要服务任务
实况窗不应该只是“展示存在感”,而应该帮助用户快速知道当前学习任务的状态。
十一、小结
本文结合「英语视界 YingYu」项目,梳理了 OpenHarmony/HarmonyOS 全场景能力在英语学习 App 中的落地方式:
- 使用
ShareKit实现碰一碰和隔空传送; - 将学习数据转成可读文本报告;
- 使用
SharedData发起近场分享; - 使用分布式 KV 存储轻量学习状态;
- 使用
onContinue()和onCreateWithWant()完成跨设备续接; - 基于学习任务扩展实况窗能力。
全场景不是简单地“多设备支持”,而是让用户在不同设备、不同时间、不同场景下都能接着学。对于教育类 App 来说,这种连续性就是体验优势。🌍

更多推荐



所有评论(0)