DEV Community

linzhongxue
linzhongxue

Posted on

Developing Fitness and Health Applications Based on HarmonyOS Next: From Sensors to Cloud Sync

Developing Fitness and Health Applications Based on HarmonyOS Next: From Sensors to Cloud Sync

This hands-on guide will walk you through building core modules of a "HealthTracker" fitness app, covering motion data collection, local storage, cross-device sync, cloud backup, and dynamic card display using ArkTS and AppGallery Connect (AGC) services.


1. Project Initialization and Permission Configuration

1.1 Project Setup & Dependencies

Create a "HealthTracker" project in DevEco Studio (Type: Application, Model: Stage). Add health service and distributed data dependencies in oh-package.json5:

"dependencies": {  
  "@ohos.sensor": "2.0",         // Sensor service  
  "@ohos.distributedData": "1.0", // Distributed data  
  "@agconnect/database": "1.0"    // AGC cloud database  
}  
Enter fullscreen mode Exit fullscreen mode

1.2 Permission Declaration

Declare permissions in module.json5:

"requestPermissions": [  
  {  
    "name": "ohos.permission.HEALTH_DATA"  // Health data access  
  },  
  {  
    "name": "ohos.permission.DISTRIBUTED_DATASYNC" // Cross-device sync  
  },  
  {  
    "name": "ohos.permission.ACTIVITY_MOTION"      // Motion recognition  
  }  
]  
Enter fullscreen mode Exit fullscreen mode

2. Core Function Implementation

2.1 Real-time Step Counting

Create StepCounterService.ets for step monitoring:

import sensor from '@ohos.sensor';  

// Step counter service  
export class StepCounterService {  
  private sensorId: number | null = null;  
  private stepCount: number = 0;  

  // Start step monitoring  
  startStepCounter(callback: (step: number) => void): void {  
    try {  
      // Get pedometer sensor  
      const sensorType = sensor.SensorType.SENSOR_TYPE_PEDOMETER;  
      this.sensorId = sensor.on(sensorType, (data) => {  
        this.stepCount = data.steps;  
        callback(this.stepCount); // Real-time callback  
      });  
      console.info('Step counter activated');  
    } catch (error) {  
      console.error(`Activation failed: ${error.code}, ${error.message}`);  
    }  
  }  

  // Stop monitoring  
  stopStepCounter(): void {  
    this.sensorId && sensor.off(this.sensorId);  
  }  
}  
Enter fullscreen mode Exit fullscreen mode

2.2 Heart Rate Monitoring (Hardware-dependent)

In HeartRateMonitor.ets:

import sensor from '@ohos.sensor';  

export class HeartRateMonitor {  
  private sensorId: number | null = null;  

  // Start heart rate monitoring  
  startMonitoring(callback: (bpm: number) => void): void {  
    try {  
      const sensorType = sensor.SensorType.SENSOR_TYPE_HEART_RATE;  
      this.sensorId = sensor.on(sensorType, (data) => {  
        if (data.heartRate > 0) callback(data.heartRate);  
      });  
    } catch (error) {  
      console.error(`Monitoring error: ${error.message}`);  
    }  
  }  

  // Stop monitoring  
  stopMonitoring(): void {  
    this.sensorId && sensor.off(this.sensorId);  
  }  
}  
Enter fullscreen mode Exit fullscreen mode

3. Data Storage and Synchronization

3.1 Local Device Storage

Store daily fitness data using distributed objects:

import distributedData from '@ohos.distributedData';  

// Create distributed data object  
const context = getContext(this) as common.UIAbilityContext;  
let kvManager = distributedData.createKVManager({  
  context,  
  bundleName: 'com.example.healthtracker'  
});  

// Health data structure  
interface HealthData {  
  steps: number;  
  heartRate?: number;  
  lastUpdate: string;  
}  

// Save data locally  
async function saveLocalData(data: HealthData): Promise<void> {  
  const key = `health_${new Date().toLocaleDateString()}`;  
  const kvStore = await kvManager.getKVStore('healthStore');  
  await kvStore.put(key, JSON.stringify(data));  
}  
Enter fullscreen mode Exit fullscreen mode

3.2 Cross-Device Sync

Synchronize data across devices:

// Sync data to other devices  
async function syncDataAcrossDevices(): Promise<void> {  
  const allDevices = distributedData.getAvailableDevicesInfo();  
  const deviceIds = allDevices.map(device => device.deviceId);  

  if (deviceIds.length > 0) {  
    const kvStore = await kvManager.getKVStore('healthStore');  
    await kvStore.sync(deviceIds, distributedData.SyncMode.PUSH_PULL);  
  }  
}  
Enter fullscreen mode Exit fullscreen mode

3.3 Cloud Backup (AGC CloudDB)

Persist data using AppGallery Connect:

import cloudDB from '@agconnect/database';  

// Initialize cloud database  
const agcCloudDB = cloudDB.initialize({  
  zoneName: "HealthDataZone", // Data zone  
  persistenceEnabled: true    // Enable persistence  
});  

// Define cloud data object  
@cloudDB.object()  
class CloudHealthData {  
  userId: string = '';       // AGC user ID  
  date: string = '';         // Date  
  steps: number = 0;         // Steps  
  avgHeartRate: number = 0;  // Avg heart rate  
}  

// Upload to cloud  
async function uploadToCloud(data: HealthData): Promise<void> {  
  const cloudObject = new CloudHealthData();  
  cloudObject.userId = getUserId(); // Get logged-in user  
  cloudObject.date = new Date().toISOString().split('T')[0];  
  cloudObject.steps = data.steps;  

  const store = await agcCloudDB.getCloudDBZone();  
  await store.upsert(cloudObject); // Insert/update  
}  
Enter fullscreen mode Exit fullscreen mode

4. Dynamic Card Display

4.1 Card UI Layout (StepCard.ets)

// Step display card  
@Component  
export struct StepCard {  
  @LocalStorageProp('currentSteps') steps: number = 0;  

  build() {  
    Column() {  
      Text("Today's Steps")  
        .fontSize(16)  
        .fontColor(Color.Gray)  

      Text(`${this.steps}`)  
        .fontSize(36)  
        .margin({ top: 8 })  

      Progress({ value: this.steps, total: 10000 }) // 10k goal  
        .height(10)  
        .margin({ top: 15 })  
    }  
    .padding(20)  
  }  
}  
Enter fullscreen mode Exit fullscreen mode

4.2 Real-time Card Updates

In card provider FormAbility.ts:

import formBindingData from '@ohos.app.form.formBindingData';  

// Update card data  
onUpdateForm(formId: string): void {  
  let stepData = { 'currentSteps': 6850 }; // Get live data  
  let formData = formBindingData.createFormBindingData(stepData);  
  formProvider.updateForm(formId, formData)  
    .catch(err => console.error('Update failed: ' + err));  
}  
Enter fullscreen mode Exit fullscreen mode

5. Troubleshooting Guide

  1. Sensor Best Practices

    • Request ohos.permission.KEEP_BACKGROUND_RUNNING for background ops
    • Wrap sensor operations in try/catch blocks
  2. Sync Optimization

   // Low-power sync strategy:  
   const options = {  
     syncMode: distributedData.SYNC_MODE_LOW_POWER,  
     delay: 500 // Batch sync every 500ms  
   };  
   kvStore.sync(deviceIds, distributedData.SyncMode.PUSH, options);  
Enter fullscreen mode Exit fullscreen mode
  1. Cloud Security Configure data access rules in AGC console:
   {  
     "read": "auth != null",    // Authenticated users only  
     "write": "auth.uid == userId" // Data owner only  
   }  
Enter fullscreen mode Exit fullscreen mode

6. Complete Example: Fitness Dashboard

// HealthDashboard.ets  
import { StepCounterService } from './StepCounterService';  

@Entry  
@Component  
struct HealthDashboard {  
  @State steps: number = 0;  
  private stepService: StepCounterService = new StepCounterService();  

  aboutToAppear() {  
    this.stepService.startStepCounter((step) => {  
      this.steps = step;  
      // Sync every 10 minutes  
      if (step % 10 === 0) {  
        saveLocalData({ steps, lastUpdate: new Date().toISOString() });  
        uploadToCloud({ steps });  
      }  
    });  
  }  

  build() {  
    Column() {  
      // Step progress ring  
      Progress({   
        value: this.steps,   
        total: 10000,  
        type: ProgressType.Ring  
      }).width(200)  

      // Sync button  
      Button("Sync to Watch")  
        .onClick(() => syncDataAcrossDevices())  
    }  
  }  
}  
Enter fullscreen mode Exit fullscreen mode

Conclusion

You've now mastered core HarmonyOS fitness app development:

  1. Hardware sensor data acquisition
  2. Distributed data flow
  3. Cloud persistence solutions
  4. Real-time card visualization

Leveraging HarmonyOS Next's distributed architecture and AGC's backend capabilities, developers can seamlessly create consistent cross-device fitness experiences. Always test sensor features on real devices and configure detailed data security policies in the AGC console.

Top comments (0)