
鸿蒙智能饮水提醒应用开发指南 原创
鸿蒙智能饮水提醒应用开发指南
一、系统架构设计
基于HarmonyOS的AI能力和分布式技术,我们设计了一套智能饮水提醒系统,主要功能包括:
人脸检测:通过摄像头检测用户面部特征
脱水分析:AI分析面部特征判断脱水状态
饮水记录:记录用户饮水时间和量
多设备同步:跨设备同步健康数据和提醒
健康建议:根据脱水程度提供饮水建议
!https://example.com/harmony-hydration-arch.png
二、核心代码实现
人脸检测服务
// FaceDetectionService.ets
import camera from ‘@ohos.multimedia.camera’;
import image from ‘@ohos.multimedia.image’;
import face from ‘@ohos.ai.face’;
class FaceDetectionService {
private static instance: FaceDetectionService;
private cameraManager: camera.CameraManager;
private faceDetector: face.FaceDetector;
private cameraInput: camera.CameraInput | null = null;
private previewOutput: camera.PreviewOutput | null = null;
private constructor() {
this.cameraManager = camera.getCameraManager();
this.faceDetector = face.createFaceDetector();
public static getInstance(): FaceDetectionService {
if (!FaceDetectionService.instance) {
FaceDetectionService.instance = new FaceDetectionService();
return FaceDetectionService.instance;
public async initCamera(previewSurfaceId: string): Promise<void> {
const cameras = this.cameraManager.getSupportedCameras();
if (cameras.length === 0) {
throw new Error('No camera available');
this.cameraInput = this.cameraManager.createCameraInput(cameras[0]);
await this.cameraInput.open();
const previewProfile = this.cameraManager.getSupportedOutputCapability(
cameras[0],
camera.ProfileMode.PROFILE_MODE_DEFAULT
).previewProfiles[0];
this.previewOutput = this.cameraManager.createPreviewOutput(
previewProfile,
previewSurfaceId
);
public async detectFace(image: image.Image): Promise<FaceDetectionResult> {
const detectionOptions = {
featureType: face.FeatureType.FEATURE_TYPE_ALL,
processMode: face.ProcessMode.PROCESS_MODE_FAST
};
return new Promise((resolve, reject) => {
this.faceDetector.detect(image, detectionOptions, (err, result) => {
if (err) {
reject(err);
else {
resolve(this.processFaceResult(result));
});
});
private processFaceResult(rawData: any): FaceDetectionResult {
if (!rawData || rawData.faces.length === 0) {
return { faceDetected: false };
const face = rawData.faces[0];
return {
faceDetected: true,
boundingBox: face.boundingBox,
landmarks: face.landmarks,
skinTone: face.skinTone,
skinMoisture: face.skinMoisture,
darkCircles: face.darkCircles,
confidence: face.confidence
};
}
export const faceDetectionService = FaceDetectionService.getInstance();
脱水分析服务
// HydrationAnalysisService.ets
import http from ‘@ohos.net.http’;
class HydrationAnalysisService {
private static instance: HydrationAnalysisService;
private httpClient: http.HttpRequest;
private apiKey = ‘YOUR_HYDRATION_API_KEY’;
private constructor() {
this.httpClient = http.createHttp();
public static getInstance(): HydrationAnalysisService {
if (!HydrationAnalysisService.instance) {
HydrationAnalysisService.instance = new HydrationAnalysisService();
return HydrationAnalysisService.instance;
public async analyzeHydration(faceData: FaceDetectionResult): Promise<HydrationStatus> {
return new Promise((resolve, reject) => {
this.httpClient.request(
'https://hydration-api.example.com/analyze',
method: ‘POST’,
header: { 'Content-Type': 'application/json' },
extraData: JSON.stringify({
skin_moisture: faceData.skinMoisture,
skin_tone: faceData.skinTone,
dark_circles: faceData.darkCircles,
api_key: this.apiKey
})
},
(err, data) => {
if (err) {
reject(err);
else {
const result = JSON.parse(data.result);
resolve(this.processHydrationResult(result));
}
);
});
private processHydrationResult(rawData: any): HydrationStatus {
return {
hydrationLevel: rawData.hydration_level || 'normal',
dehydrationScore: rawData.dehydration_score || 0,
suggestedWaterIntake: rawData.suggested_water_intake || 200,
symptoms: rawData.symptoms || [],
timestamp: Date.now()
};
public calculateDailyRequirement(userProfile: UserProfile): number {
// 基础水需求量计算(毫升)
let baseRequirement = userProfile.weight * 30; // 30ml/kg
// 根据活动量调整
if (userProfile.activityLevel === 'low') {
baseRequirement *= 0.9;
else if (userProfile.activityLevel === ‘high’) {
baseRequirement *= 1.2;
// 根据气候调整
if (userProfile.environment === 'hot') {
baseRequirement *= 1.3;
else if (userProfile.environment === ‘cold’) {
baseRequirement *= 0.9;
return Math.round(baseRequirement);
}
export const hydrationService = HydrationAnalysisService.getInstance();
多设备同步服务
// HydrationSyncService.ets
import distributedData from ‘@ohos.data.distributedData’;
import deviceManager from ‘@ohos.distributedHardware.deviceManager’;
class HydrationSyncService {
private static instance: HydrationSyncService;
private kvManager: distributedData.KVManager;
private kvStore: distributedData.KVStore;
private constructor() {
this.initKVStore();
private async initKVStore(): Promise<void> {
const config = {
bundleName: 'com.example.hydrationReminder',
userInfo: { userId: 'currentUser' }
};
this.kvManager = distributedData.createKVManager(config);
this.kvStore = await this.kvManager.getKVStore('hydration_store', {
createIfMissing: true
});
this.kvStore.on('dataChange', (data) => {
this.handleRemoteUpdate(data);
});
public static getInstance(): HydrationSyncService {
if (!HydrationSyncService.instance) {
HydrationSyncService.instance = new HydrationSyncService();
return HydrationSyncService.instance;
public async syncHydrationRecord(record: HydrationRecord): Promise<void> {
await this.kvStore.put(hydration_${record.id}, JSON.stringify(record));
public async getHydrationRecords(date: string): Promise<HydrationRecord[]> {
const entries = await this.kvStore.getEntries('hydration_');
return Array.from(entries)
.map(([_, value]) => JSON.parse(value))
.filter(record => record.date === date);
public async syncUserProfile(profile: UserProfile): Promise<void> {
await this.kvStore.put('user_profile', JSON.stringify(profile));
public async getUserProfile(): Promise<UserProfile | null> {
const value = await this.kvStore.get('user_profile');
return value ? JSON.parse(value) : null;
public async syncHydrationStatus(status: HydrationStatus): Promise<void> {
await this.kvStore.put('hydration_status', JSON.stringify(status));
public async getHydrationStatus(): Promise<HydrationStatus | null> {
const value = await this.kvStore.get('hydration_status');
return value ? JSON.parse(value) : null;
private handleRemoteUpdate(data: distributedData.ChangeInfo): void {
if (data.deviceId === deviceInfo.deviceId) return;
const key = data.key as string;
if (key.startsWith('hydration_')) {
const record = JSON.parse(data.value);
EventBus.emit('hydrationRecordUpdated', record);
else if (key === ‘user_profile’) {
const profile = JSON.parse(data.value);
EventBus.emit('userProfileUpdated', profile);
else if (key === ‘hydration_status’) {
const status = JSON.parse(data.value);
EventBus.emit('hydrationStatusUpdated', status);
}
export const hydrationSyncService = HydrationSyncService.getInstance();
三、主界面实现
人脸检测界面
// FaceDetectionView.ets
@Component
struct FaceDetectionView {
@State previewSurfaceId: string = ‘’;
@State faceResult: FaceDetectionResult | null = null;
@State hydrationStatus: HydrationStatus | null = null;
@State isAnalyzing: boolean = false;
aboutToAppear() {
this.initCamera();
build() {
Stack() {
// 相机预览
XComponent({
id: 'faceCameraPreview',
type: 'surface',
libraryname: 'libcamera.so',
controller: this.previewController
})
.onLoad(() => {
this.previewSurfaceId = this.previewController.getXComponentSurfaceId();
faceDetectionService.initCamera(this.previewSurfaceId);
})
.width('100%')
.height('100%')
// 人脸检测框
if (this.faceResult?.faceDetected) {
Rect()
.width(this.faceResult.boundingBox.width)
.height(this.faceResult.boundingBox.height)
.position({
x: this.faceResult.boundingBox.x,
y: this.faceResult.boundingBox.y
})
.borderWidth(2)
.borderColor('#4CAF50')
// 检测按钮
Button('检测水分状态')
.onClick(() => this.detectAndAnalyze())
.position({ x: '50%', y: '90%' })
// 分析状态
if (this.isAnalyzing) {
LoadingProgress()
.position({ x: '50%', y: '50%' })
// 结果显示
if (this.hydrationStatus) {
HydrationStatusView({ status: this.hydrationStatus })
.position({ x: '50%', y: '30%' })
}
private async detectAndAnalyze(): Promise<void> {
this.isAnalyzing = true;
const image = await faceDetectionService.captureFaceImage();
this.faceResult = await faceDetectionService.detectFace(image);
if (this.faceResult.faceDetected) {
this.hydrationStatus = await hydrationService.analyzeHydration(this.faceResult);
await hydrationSyncService.syncHydrationStatus(this.hydrationStatus);
// 根据状态决定是否提醒
if (this.hydrationStatus.hydrationLevel === 'dehydrated') {
EventBus.emit('hydrationAlert', this.hydrationStatus);
}
this.isAnalyzing = false;
}
@Component
struct HydrationStatusView {
private status: HydrationStatus;
build() {
Column() {
Text(this.getStatusTitle())
.fontSize(20)
.fontColor(this.getStatusColor())
Text(建议饮水量: ${this.status.suggestedWaterIntake}ml)
.fontSize(16)
.margin({ top: 8 })
if (this.status.symptoms.length > 0) {
Text(症状: ${this.status.symptoms.join(', ')})
.fontSize(14)
.margin({ top: 8 })
Button(‘记录饮水’)
.onClick(() => this.recordWaterIntake())
.margin({ top: 16 })
.padding(16)
.backgroundColor('#FFFFFF')
.borderRadius(8)
private getStatusTitle(): string {
switch (this.status.hydrationLevel) {
case 'dehydrated': return '脱水状态!需要立即补水';
case 'normal': return '水分状态正常';
case 'hydrated': return '水分充足';
default: return '未知状态';
}
private getStatusColor(): string {
switch (this.status.hydrationLevel) {
case ‘dehydrated’: return ‘#FF5722’;
case ‘normal’: return ‘#4CAF50’;
case ‘hydrated’: return ‘#2196F3’;
default: return ‘#000000’;
}
private async recordWaterIntake(): Promise<void> {
const record: HydrationRecord = {
id: generateId(),
amount: this.status.suggestedWaterIntake,
timestamp: Date.now(),
status: this.status.hydrationLevel
};
await hydrationSyncService.syncHydrationRecord(record);
EventBus.emit('waterIntakeRecorded', record);
}
function generateId(): string {
return Math.random().toString(36).substring(2) + Date.now().toString(36);
饮水记录界面
// HydrationHistoryView.ets
@Component
struct HydrationHistoryView {
@State records: HydrationRecord[] = [];
@State dailyGoal: number = 2000;
@State consumed: number = 0;
aboutToAppear() {
this.loadRecords();
this.loadUserProfile();
EventBus.on(‘hydrationRecordUpdated’, () => this.loadRecords());
build() {
Column() {
// 今日饮水进度
Text('今日饮水')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ top: 16 })
Progress({
value: this.consumed,
total: this.dailyGoal,
type: ProgressType.PROGRESS_TYPE_LINEAR
})
.width('90%')
.height(20)
.margin({ top: 16 })
Text({this.consumed} / {this.dailyGoal} ml)
.fontSize(16)
.margin({ top: 8 })
// 快速记录按钮
Row() {
Button('+200ml')
.onClick(() => this.recordIntake(200))
Button('+500ml')
.onClick(() => this.recordIntake(500))
.margin({ left: 16 })
.margin({ top: 24 })
// 记录列表
if (this.records.length === 0) {
Text('暂无饮水记录')
.fontSize(16)
.margin({ top: 32 })
else {
List({ space: 10 }) {
ForEach(this.records, (record) => {
ListItem() {
HydrationRecordItem({ record })
})
.layoutWeight(1)
}
.padding(16)
private async loadRecords(): Promise<void> {
const today = new Date().toISOString().split('T')[0];
this.records = await hydrationSyncService.getHydrationRecords(today);
this.consumed = this.records.reduce((sum, r) => sum + r.amount, 0);
private async loadUserProfile(): Promise<void> {
const profile = await hydrationSyncService.getUserProfile();
if (profile) {
this.dailyGoal = hydrationService.calculateDailyRequirement(profile);
}
private async recordIntake(amount: number): Promise<void> {
const record: HydrationRecord = {
id: generateId(),
amount,
timestamp: Date.now(),
status: ‘manual’
};
await hydrationSyncService.syncHydrationRecord(record);
}
@Component
struct HydrationRecordItem {
private record: HydrationRecord;
build() {
Row() {
Column() {
Text(${this.record.amount}ml)
.fontSize(18)
Text(formatTime(this.record.timestamp))
.fontSize(14)
.fontColor('#666666')
.margin({ top: 4 })
.layoutWeight(1)
if (this.record.status === 'dehydrated') {
Text('脱水状态')
.fontSize(14)
.fontColor('#FF5722')
}
.padding(12)
}
function formatTime(timestamp: number): string {
const date = new Date(timestamp);
return {date.getHours()}:{date.getMinutes().toString().padStart(2, ‘0’)};
健康报告界面
// HealthReportView.ets
@Component
struct HealthReportView {
@State hydrationData: HydrationData[] = [];
@State userProfile: UserProfile | null = null;
aboutToAppear() {
this.loadData();
this.loadUserProfile();
build() {
Column() {
if (this.userProfile) {
Text(${this.userProfile.name}的健康报告)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ top: 16 })
// 本周饮水趋势
Text('本周饮水趋势')
.fontSize(20)
.margin({ top: 24 })
if (this.hydrationData.length > 0) {
Row() {
ForEach(this.hydrationData, (day) => {
Column() {
Text('')
.width(20)
.height(day.amount / 50) // 比例缩放
.backgroundColor('#2196F3')
Text(day.day)
.fontSize(12)
.margin({ top: 4 })
.margin({ right: 8 })
})
.height(100)
.margin({ top: 8 })
// 脱水状态统计
Text('脱水状态统计')
.fontSize(20)
.margin({ top: 24 })
if (this.hydrationData.some(d => d.dehydratedCount > 0)) {
Row() {
Text(脱水次数: ${this.hydrationData.reduce((sum, d) => sum + d.dehydratedCount, 0)})
.fontSize(16)
Text(最严重: ${Math.max(...this.hydrationData.map(d => d.dehydrationScore))}分)
.fontSize(16)
.margin({ left: 16 })
.margin({ top: 8 })
else {
Text('本周未检测到脱水状态')
.fontSize(16)
.margin({ top: 8 })
// 健康建议
Text('健康建议')
.fontSize(20)
.margin({ top: 24 })
if (this.hydrationData.length > 0) {
const avgIntake = this.hydrationData.reduce((sum, d) => sum + d.amount, 0) / this.hydrationData.length;
const requirement = this.userProfile ?
hydrationService.calculateDailyRequirement(this.userProfile) : 2000;
if (avgIntake < requirement * 0.8) {
Text('您的平均饮水量低于建议值,建议增加每日饮水量')
.fontSize(16)
.margin({ top: 8 })
else if (avgIntake > requirement * 1.2) {
Text('您的饮水量充足,继续保持')
.fontSize(16)
.margin({ top: 8 })
else {
Text('您的饮水量在正常范围内,继续保持良好习惯')
.fontSize(16)
.margin({ top: 8 })
}
}
.padding(16)
private async loadData(): Promise<void> {
// 获取最近7天数据
const entries = await hydrationSyncService.getHydrationRecords();
const now = new Date();
this.hydrationData = Array.from({ length: 7 }, (_, i) => {
const date = new Date(now);
date.setDate(date.getDate() - i);
const dateStr = date.toISOString().split('T')[0];
const dayRecords = entries.filter(r => r.date === dateStr);
return {
day: ['日', '一', '二', '三', '四', '五', '六'][date.getDay()],
date: dateStr,
amount: dayRecords.reduce((sum, r) => sum + r.amount, 0),
dehydratedCount: dayRecords.filter(r => r.status === 'dehydrated').length,
dehydrationScore: dayRecords.reduce((max, r) =>
r.dehydrationScore ? Math.max(max, r.dehydrationScore) : max, 0
)
};
}).reverse();
private async loadUserProfile(): Promise<void> {
this.userProfile = await hydrationSyncService.getUserProfile();
}
四、高级功能实现
多设备协同提醒
// CollaborativeReminderService.ets
class CollaborativeReminderService {
private static instance: CollaborativeReminderService;
private constructor() {}
public static getInstance(): CollaborativeReminderService {
if (!CollaborativeReminderService.instance) {
CollaborativeReminderService.instance = new CollaborativeReminderService();
return CollaborativeReminderService.instance;
public async syncReminderToDevices(reminder: HydrationReminder): Promise<void> {
const devices = await deviceManager.getTrustedDevices();
await Promise.all(devices.map(device =>
this.sendReminderToDevice(device.id, reminder)
));
private async sendReminderToDevice(deviceId: string, reminder: HydrationReminder): Promise<void> {
const ability = await featureAbility.startAbility({
bundleName: 'com.example.hydrationReminder',
abilityName: 'HydrationReminderAbility',
deviceId
});
await ability.call({
method: 'receiveReminder',
parameters: [reminder]
});
public async syncEmergencyAlert(status: HydrationStatus): Promise<void> {
if (status.hydrationLevel !== 'dehydrated') return;
const devices = await deviceManager.getTrustedDevices();
await Promise.all(devices.map(device =>
this.sendAlertToDevice(device.id, status)
));
private async sendAlertToDevice(deviceId: string, status: HydrationStatus): Promise<void> {
const ability = await featureAbility.startAbility({
bundleName: 'com.example.hydrationReminder',
abilityName: 'HydrationAlertAbility',
deviceId
});
await ability.call({
method: 'receiveEmergencyAlert',
parameters: [status]
});
}
export const collaborativeReminderService = CollaborativeReminderService.getInstance();
智能饮水计划
// HydrationPlanService.ets
import reminderAgent from ‘@ohos.reminderAgent’;
class HydrationPlanService {
private static instance: HydrationPlanService;
private constructor() {}
public static getInstance(): HydrationPlanService {
if (!HydrationPlanService.instance) {
HydrationPlanService.instance = new HydrationPlanService();
return HydrationPlanService.instance;
public async createDailyPlan(userProfile: UserProfile): Promise<void> {
const dailyRequirement = hydrationService.calculateDailyRequirement(userProfile);
const intervals = this.calculateOptimalIntervals(dailyRequirement);
await this.cancelAllReminders();
for (const interval of intervals) {
await this.scheduleReminder(interval.time, interval.amount);
}
private calculateOptimalIntervals(total: number): { time: string; amount: number }[] {
// 将总水量分配到一天中的最佳时间点
const intervals = [
time: ‘08:00’, amount: Math.round(total * 0.2) },
time: ‘11:00’, amount: Math.round(total * 0.2) },
time: ‘14:00’, amount: Math.round(total * 0.2) },
time: ‘17:00’, amount: Math.round(total * 0.2) },
time: ‘20:00’, amount: Math.round(total * 0.2) }
];
return intervals;
private async scheduleReminder(time: string, amount: number): Promise<void> {
const [hours, minutes] = time.split(':').map(Number);
const reminderTime = new Date();
reminderTime.setHours(hours, minutes, 0, 0);
const reminderRequest: reminderAgent.ReminderRequest = {
reminderType: reminderAgent.ReminderType.REMINDER_TYPE_TIMER,
actionButton: [{ title: '已饮水' }, { title: '稍后提醒' }],
wantAgent: {
pkgName: 'com.example.hydrationReminder',
abilityName: 'HydrationReminderAbility'
},
ringDuration: 60,
snoozeTimes: 2,
triggerTime: reminderTime.getTime(),
repeatInterval: 24 60 60 * 1000, // 每天重复
title: '饮水提醒',
content: 该喝水了!建议饮水量: ${amount}ml,
expiredContent: "饮水提醒已过期"
};
await reminderAgent.publishReminder(reminderRequest);
public async cancelAllReminders(): Promise<void> {
const reminders = await reminderAgent.getValidReminders();
await Promise.all(reminders.map(rem =>
reminderAgent.cancelReminder(rem.id)
));
}
export const hydrationPlanService = HydrationPlanService.getInstance();
环境因素分析
// EnvironmentAnalysisService.ets
import geolocation from ‘@ohos.geolocation’;
import sensor from ‘@ohos.sensor’;
class EnvironmentAnalysisService {
private static instance: EnvironmentAnalysisService;
private geoLocationManager: geolocation.GeoLocationManager;
private temperatureSensor: sensor.Sensor | null = null;
private constructor() {
this.geoLocationManager = geolocation.createGeoLocationManager();
public static getInstance(): EnvironmentAnalysisService {
if (!EnvironmentAnalysisService.instance) {
EnvironmentAnalysisService.instance = new EnvironmentAnalysisService();
return EnvironmentAnalysisService.instance;
public async startMonitoring(): Promise<void> {
// 获取当前位置
const location = await this.geoLocationManager.getCurrentLocation();
// 获取天气数据
const weather = await this.getWeatherData(location.latitude, location.longitude);
// 启动温度传感器
this.temperatureSensor = await sensor.getSensor(sensor.SensorType.SENSOR_TYPE_AMBIENT_TEMPERATURE);
this.temperatureSensor.on('change', (data) => {
this.handleTemperatureChange(data);
});
EventBus.emit('environmentDataUpdated', {
location,
weather,
indoorTemperature: null
});
private async getWeatherData(lat: number, lon: number): Promise<WeatherData> {
const httpClient = http.createHttp();
return new Promise((resolve, reject) => {
httpClient.request(
https://weather-api.example.com/current?lat={lat}&lon={lon},
method: ‘GET’ },
(err, data) => {
if (err) {
reject(err);
else {
resolve(JSON.parse(data.result));
}
);
});
private handleTemperatureChange(data: sensor.SensorData): void {
const temperature = data.value;
let environmentType: 'hot' 'normal'
‘cold’ = ‘normal’;
if (temperature > 28) {
environmentType = 'hot';
else if (temperature < 15) {
environmentType = 'cold';
EventBus.emit(‘temperatureUpdated’, {
temperature,
environmentType
});
public adjustHydrationPlan(environment: EnvironmentData, currentPlan: HydrationPlan): HydrationPlan {
let adjustmentFactor = 1.0;
// 根据环境温度调整
if (environment.temperature > 30) {
adjustmentFactor *= 1.3;
else if (environment.temperature < 10) {
adjustmentFactor *= 0.9;
// 根据湿度调整
if (environment.humidity < 40) {
adjustmentFactor *= 1.2;
else if (environment.humidity > 70) {
adjustmentFactor *= 0.9;
// 根据活动量调整
if (environment.activityLevel === 'high') {
adjustmentFactor *= 1.2;
return {
...currentPlan,
dailyGoal: Math.round(currentPlan.dailyGoal * adjustmentFactor),
intervals: currentPlan.intervals.map(i => ({
...i,
amount: Math.round(i.amount * adjustmentFactor)
}))
};
}
export const environmentService = EnvironmentAnalysisService.getInstance();
五、总结
本智能饮水提醒应用实现了以下核心价值:
精准检测:通过面部特征分析准确判断脱水状态
个性化计划:根据用户特征和环境因素定制饮水计划
智能提醒:在最佳时间提醒用户补充水分
多设备协同:跨设备同步健康数据和提醒
健康追踪:长期记录和分析用户饮水习惯
扩展方向:
集成智能水杯数据
增加水分摄入来源分析(食物、饮料等)
开发家庭共享模式
增加健康知识推送
注意事项:
需要申请ohos.permission.CAMERA权限
面部检测准确率受光线和角度影响
脱水分析结果仅供参考,不能替代医学诊断
多设备协同需保持网络连接
环境数据需要位置权限和网络连接
