Practical Development of Smart Health Monitoring Applications Based on HarmonyOS Next
1. Project Overview and Development Environment Setup
1.1 Project Background and Functional Planning
With the popularity of smart wearable devices, health management applications have become an important part of the HarmonyOS ecosystem. This project will develop a smart health monitoring application based on HarmonyOS Next, with the following main features:
- Real-time heart rate monitoring and historical data display
- Daily step counting and exercise goal setting
- Sleep quality analysis and recommendations
- Visualization of health data
- Alerts for abnormal health indicators
1.2 Configuring the DevEco Studio Development Environment
First, ensure that the latest version of DevEco Studio is installed and basic configurations are completed:
- Download DevEco Studio 4.0 or later from the official website.
- Install Node.js and the Ohpm package manager.
- Configure the HarmonyOS SDK, selecting API version 9 or higher.
- When creating a new project, select "Application" → "Empty Ability."
// Example of the basic project configuration file: entry/build-profile.json5
{
"app": {
"signingConfigs": [],
"compileSdkVersion": 9,
"compatibleSdkVersion": 9,
"products": [
{
"name": "default",
"signingConfig": "default"
}
]
},
"modules": [
{
"name": "entry",
"srcPath": "./src/main/ets",
"targets": [
{
"name": "default",
"applyToProducts": [
"default"
]
}
]
}
]
}
2. Building the Application Framework
2.1 Application Entry and Permission Configuration
Configure basic application information and required permissions in entry/src/main/module.json5
:
{
"module": {
"name": "healthmonitor",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "MainAbility",
"deviceTypes": [
"smartwatch",
"phone",
"tablet"
],
"abilities": [
{
"name": "MainAbility",
"srcEntry": "./ets/MainAbility/MainAbility.ts",
"label": "$string:MainAbility_label",
"icon": "$media:icon",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"backgroundModes": [
"healthData"
]
}
],
"requestPermissions": [
{
"name": "ohos.permission.READ_HEALTH_DATA"
},
{
"name": "ohos.permission.WRITE_HEALTH_DATA"
},
{
"name": "ohos.permission.ACTIVITY_MOTION"
}
]
}
}
2.2 Designing the Main Page Layout
Use ArkTS declarative UI to build the main interface of the health application:
// src/main/ets/pages/Index.ets
@Entry
@Component
struct HealthMainPage {
@State currentTab: string = 'dashboard'
build() {
Column() {
// Top health data overview
HealthOverview()
// Main content area
TabContent(this.currentTab)
// Bottom navigation bar
Tabs({ barPosition: BarPosition.End }) {
TabContent() {
DashboardPage()
}.tabBar('Home', 'home.svg', 'home_filled.svg')
TabContent() {
ActivityPage()
}.tabBar('Activity', 'activity.svg', 'activity_filled.svg')
TabContent() {
SleepPage()
}.tabBar('Sleep', 'sleep.svg', 'sleep_filled.svg')
TabContent() {
ProfilePage()
}.tabBar('Profile', 'profile.svg', 'profile_filled.svg')
}
.barMode(BarMode.Fixed)
.onChange((index: number) => {
this.currentTab = ['dashboard', 'activity', 'sleep', 'profile'][index]
})
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
}
@Component
struct HealthOverview {
@State heartRate: number = 72
@State steps: number = 4582
@State sleepHours: number = 6.5
build() {
Row() {
// Heart rate card
HealthMetricCard('Heart Rate', `${this.heartRate} bpm`, 'heart.svg', '#FF6B81')
// Steps card
HealthMetricCard('Steps', this.steps.toString(), 'footsteps.svg', '#4CAF50')
// Sleep card
HealthMetricCard('Sleep', `${this.sleepHours} h`, 'moon.svg', '#5D6DBE')
}
.padding(16)
.justifyContent(FlexAlign.SpaceBetween)
}
}
@Component
struct HealthMetricCard {
private title: string
private value: string
private icon: string
private color: string
build() {
Column() {
Image(this.icon)
.width(24)
.height(24)
.margin({ bottom: 8 })
Text(this.title)
.fontSize(12)
.fontColor('#666')
Text(this.value)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ top: 4 })
}
.padding(16)
.backgroundColor(Color.White)
.borderRadius(12)
.shadow({ radius: 8, color: '#10000000', offsetX: 0, offsetY: 2 })
.width('30%')
}
}
3. Health Data Collection and Processing
3.1 Integrating HarmonyOS Health Data Services
HarmonyOS provides rich health data APIs. First, we need to initialize the health data manager:
// src/main/ets/utils/HealthDataManager.ts
import { health } from '@kit.HealthKit';
export class HealthDataManager {
private static instance: HealthDataManager;
private healthDataHelper: health.HealthDataHelper;
private constructor() {
this.healthDataHelper = health.createHealthDataHelper();
}
public static getInstance(): HealthDataManager {
if (!HealthDataManager.instance) {
HealthDataManager.instance = new HealthDataManager();
}
return HealthDataManager.instance;
}
// Get today's step count
async getTodaySteps(): Promise<number> {
try {
const endTime = new Date();
const startTime = new Date();
startTime.setHours(0, 0, 0, 0);
const options: health.HealthDataOptions = {
startTime: startTime.getTime(),
endTime: endTime.getTime(),
dataType: health.DataType.DATA_TYPE_STEP_COUNT
};
const result = await this.healthDataHelper.query(options);
return result?.length > 0 ? result[0].value : 0;
} catch (err) {
console.error(`Failed to get steps: ${err.code}, ${err.message}`);
return 0;
}
}
// Get real-time heart rate
async getLatestHeartRate(): Promise<number> {
try {
const options: health.HealthDataOptions = {
dataType: health.DataType.DATA_TYPE_HEART_RATE,
sort: { field: 'startTime', order: 'desc' },
limit: 1
};
const result = await this.healthDataHelper.query(options);
return result?.length > 0 ? result[0].value : 0;
} catch (err) {
console.error(`Failed to get heart rate: ${err.code}, ${err.message}`);
return 0;
}
}
// Subscribe to heart rate changes
subscribeHeartRate(callback: (value: number) => void): void {
const options: health.HealthDataOptions = {
dataType: health.DataType.DATA_TYPE_HEART_RATE
};
this.healthDataHelper.on('healthDataChange', options, (data) => {
if (data?.length > 0) {
callback(data[0].value);
}
});
}
}
3.2 Using Health Data in Pages
Integrate health data into pages for real-time updates:
// src/main/ets/pages/DashboardPage.ets
@Component
struct DashboardPage {
@State heartRate: number = 0
@State steps: number = 0
@State sleepHours: number = 0
private healthManager = HealthDataManager.getInstance()
aboutToAppear() {
this.loadHealthData()
this.setupSubscriptions()
}
private async loadHealthData() {
this.heartRate = await this.healthManager.getLatestHeartRate()
this.steps = await this.healthManager.getTodaySteps()
// Sleep data requires more complex processing; simplified here
this.sleepHours = 7.2
}
private setupSubscriptions() {
this.healthManager.subscribeHeartRate((rate) => {
this.heartRate = rate
})
// Refresh step data every 5 minutes
setInterval(async () => {
this.steps = await this.healthManager.getTodaySteps()
}, 300000)
}
build() {
Column() {
// Heart rate chart
HealthLineChart('Heart Rate Trend', this.heartRate, 60, 100, 'bpm')
// Steps progress ring
ProgressRing('Today’s Steps', this.steps, 10000, '#4CAF50')
// Sleep quality card
SleepQualityCard(this.sleepHours)
}
.padding(16)
}
}
@Component
struct HealthLineChart {
private title: string
@State private currentValue: number
private min: number
private max: number
private unit: string
@State private historyData: number[] = []
build() {
Column() {
Text(`${this.title}: ${this.currentValue} ${this.unit}`)
.fontSize(16)
.fontWeight(FontWeight.Bold)
Line()
.width('90%')
.height(2)
.backgroundColor('#EEE')
.margin({ top: 8, bottom: 16 })
// Simplified chart implementation
Row() {
ForEach(this.historyData, (value, index) => {
Column() {
Line()
.height(value / this.max * 100)
.width(4)
.backgroundColor('#FF6B81')
.margin({ right: 4 })
}
.height(100)
.alignItems(VerticalAlign.Bottom)
})
}
.height(100)
.margin({ bottom: 16 })
}
.padding(16)
.backgroundColor(Color.White)
.borderRadius(12)
.width('100%')
.onAppear(() => {
// Simulate historical data
this.historyData = Array.from({ length: 24 }, (_, i) =>
Math.min(this.max, Math.max(this.min, this.currentValue + (Math.random() * 20 - 10)))
)
})
}
}
4. Advanced Feature Implementation
4.1 Abnormal Heart Rate Alert System
// src/main/ets/utils/HealthAlarmSystem.ts
import { HealthDataManager } from './HealthDataManager';
import { common, notification } from '@kit.AbilityKit';
export class HealthAlarmSystem {
private static instance: HealthAlarmSystem;
private healthManager = HealthDataManager.getInstance();
private normalRange: [number, number] = [60, 100];
private context: common.Context | null = null;
private constructor() {}
public static getInstance(): HealthAlarmSystem {
if (!HealthAlarmSystem.instance) {
HealthAlarmSystem.instance = new HealthAlarmSystem();
}
return HealthAlarmSystem.instance;
}
public init(context: common.Context): void {
this.context = context;
this.setupHeartRateMonitoring();
}
private setupHeartRateMonitoring(): void {
this.healthManager.subscribeHeartRate((rate) => {
this.checkHeartRate(rate);
});
}
private checkHeartRate(rate: number): void {
if (rate < this.normalRange[0]) {
this.showNotification('Low Heart Rate', `Your heart rate is low (${rate} bpm). Consider resting.`);
} else if (rate > this.normalRange[1]) {
this.showNotification('High Heart Rate', `Your heart rate is high (${rate} bpm). Stay calm.`);
}
}
private showNotification(title: string, content: string): void {
if (!this.context) return;
const wantAgent: notification.WantAgent = {
wants: [
{
bundleName: this.context.applicationInfo.name,
abilityName: 'MainAbility'
}
],
operationType: notification.OperationType.START_ABILITY,
requestCode: 0
};
notification.publish({
content: {
contentType: notification.ContentType.NOTIFICATION_TEXT,
normal: {
title: title,
text: content,
additionalText: 'Health Alert'
}
},
id: 1,
wantAgent: wantAgent
}).catch(err => {
console.error(`Failed to show notification: ${err.code}, ${err.message}`);
});
}
public setNormalRange(min: number, max: number): void {
this.normalRange = [min, max];
}
}
4.2 Sleep Quality Analysis Algorithm
// src/main/ets/utils/SleepAnalyzer.ts
import { health } from '@kit.HealthKit';
export class SleepAnalyzer {
private healthDataHelper = health.createHealthDataHelper();
async analyzeSleepQuality(date: Date): Promise<SleepAnalysisResult> {
const startTime = new Date(date);
startTime.setHours(0, 0, 0, 0);
const endTime = new Date(date);
endTime.setHours(23, 59, 59, 999);
try {
const options: health.HealthDataOptions = {
startTime: startTime.getTime(),
endTime: endTime.getTime(),
dataType: health.DataType.DATA_TYPE_SLEEP_STATE
};
const sleepData = await this.healthDataHelper.query(options);
return this.processSleepData(sleepData);
} catch (err) {
console.error(`Failed to analyze sleep: ${err.code}, ${err.message}`);
return {
totalHours: 0,
deepSleepHours: 0,
lightSleepHours: 0,
remHours: 0,
wakeTimes: 0,
score: 0
};
}
}
private processSleepData(data: health.HealthData[]): SleepAnalysisResult {
let totalSleep = 0;
let deepSleep = 0;
let lightSleep = 0;
let remSleep = 0;
let wakeCount = 0;
let lastState: string | null = null;
data.forEach(record => {
const duration = (record.endTime - record.startTime) / (1000 * 60 * 60); // Convert to hours
switch (record.value) {
case health.SleepState.SLEEP_STATE_DEEP:
deepSleep += duration;
totalSleep += duration;
break;
case health.SleepState.SLEEP_STATE_LIGHT:
lightSleep += duration;
totalSleep += duration;
break;
case health.SleepState.SLEEP_STATE_REM:
remSleep += duration;
totalSleep += duration;
break;
case health.SleepState.SLEEP_STATE_AWAKE:
if (lastState !== health.SleepState.SLEEP_STATE_AWAKE) {
wakeCount++;
}
break;
}
lastState = record.value;
});
// Simplified sleep scoring algorithm
const score = Math.min(100,
Math.round(
(deepSleep * 40 + lightSleep * 20 + remSleep * 30) / totalSleep * 10 -
wakeCount * 2 +
(totalSleep >= 7 ? 20 : totalSleep >= 5 ? 10 : 0)
)
);
return {
totalHours: parseFloat(totalSleep.toFixed(1)),
deepSleepHours: parseFloat(deepSleep.toFixed(1)),
lightSleepHours: parseFloat(lightSleep.toFixed(1)),
remHours: parseFloat(remSleep.toFixed(1)),
wakeTimes: wakeCount,
score: score
};
}
}
interface SleepAnalysisResult {
totalHours: number;
deepSleepHours: number;
lightSleepHours: number;
remHours: number;
wakeTimes: number;
score: number;
}
5. Application Optimization and Release Preparation
5.1 Performance Optimization Recommendations
- Data Caching Strategy: Implement local caching for frequently accessed health data.
// Example caching implementation
import { dataStorage } from '@kit.ArkData';
class HealthDataCache {
private static PREFIX = 'health_cache_';
static async set(key: string, value: any, ttl: number = 3600): Promise<void> {
try {
const cacheItem = {
data: value,
expires: Date.now() + ttl * 1000
};
await dataStorage.put(this.PREFIX + key, JSON.stringify(cacheItem));
} catch (err) {
console.error(`Cache set error: ${err.code}, ${err.message}`);
}
}
static async get(key: string): Promise<any | null> {
try {
const value = await dataStorage.get(this.PREFIX + key);
if (!value) return null;
const cacheItem = JSON.parse(value);
if (cacheItem.expires < Date.now()) {
await this.delete(key);
return null;
}
return cacheItem.data;
} catch (err) {
console.error(`Cache get error: ${err.code}, ${err.message}`);
return null;
}
}
}
- UI Rendering Optimization: Use LazyForEach for long lists.
@Component
struct SleepHistoryList {
@State sleepData: SleepAnalysisResult[] = []
build() {
List() {
LazyForEach(this.sleepData, (item: SleepAnalysisResult) => {
ListItem() {
SleepHistoryItem({ data: item })
}
}, (item) => item.date.toString())
}
.height('100%')
.width('100%')
}
}
5.2 Application Testing and Debugging
- Use DevEco Studio's previewer for quick UI validation.
- Test health data APIs on real devices.
- Use HiLog for debug logging.
import { hilog } from '@kit.PerformanceAnalysisKit';
const DOMAIN = 0xFF00;
const TAG = 'HealthApp';
function logDebug(msg: string): void {
hilog.debug(DOMAIN, TAG, msg);
}
function logError(msg: string): void {
hilog.error(DOMAIN, TAG, msg);
}
5.3 Application Packaging and Release
- Configure application signing.
- Generate HAP package.
- Submit to AppGallery Connect for review.
- Configure application privacy policy.
// Example privacy dialog
@Component
struct PrivacyDialog {
@State showDialog: boolean = true
build() {
if (this.showDialog) {
AlertDialog({
title: 'Privacy Policy',
message: 'We take your privacy very seriously...',
primaryButton: {
value: 'Agree',
action: () => {
this.showDialog = false;
// Store user consent
Preferences.put('privacy_accepted', true);
}
},
secondaryButton: {
value: 'Disagree',
action: () => {
// Exit the application
AppStorage.setOrCreate('exitApp', true);
}
}
})
}
}
}
6. Summary and Expansion Suggestions
This tutorial provides a comprehensive guide to developing a health management application based on HarmonyOS Next, covering all aspects from basic framework setup to advanced feature implementation. Using ArkTS declarative UI development and HarmonyOS health data services, we have built a fully functional health monitoring application.
Expansion Suggestions:
- Integrate more health metrics such as blood oxygen and blood pressure monitoring.
- Add health recommendations and exercise planning features.
- Implement multi-device data synchronization.
- Develop a watch version of the application with optimized UI adaptation.
- Incorporate AI models for health prediction analysis.
Through continuous iteration and improvement, you can create a more professional and intelligent health management application, providing users with comprehensive health monitoring services.
Top comments (0)