鸿蒙跨设备闹钟提醒系统设计与实现

一、系统架构设计

基于HarmonyOS的分布式能力和通知模块,我们设计了一套跨设备同步的闹钟提醒系统,确保用户在任何设备上设置的闹钟都能在所有设备上同步提醒。

https://example.com/alarm-system-arch.png

系统包含三个核心模块:

  1. ​本地通知模块​​ - 使用@ohos.notification实现基础闹钟功能
  2. ​分布式同步模块​​ - 通过@ohos.distributedData实现多设备状态同步
  3. ​UI组件模块​​ - 提供统一的闹钟设置界面和提醒弹窗

二、核心代码实现

1. 闹钟通知服务(ArkTS)

// AlarmService.ets
import notification from '@ohos.notification';
import distributedData from '@ohos.distributedData';

const ALARM_CHANNEL = 'alarm_sync_channel';

class AlarmService {
  private static instance: AlarmService = null;
  private dataManager: distributedData.DataManager;
  
  private constructor() {
    this.initDataManager();
  }
  
  public static getInstance(): AlarmService {
    if (!AlarmService.instance) {
      AlarmService.instance = new AlarmService();
    }
    return AlarmService.instance;
  }
  
  private initDataManager() {
    this.dataManager = distributedData.createDataManager({
      bundleName: 'com.example.alarmdemo',
      area: distributedData.Area.GLOBAL
    });
    
    this.dataManager.registerDataListener(ALARM_CHANNEL, (data) => {
      this.handleSyncAlarm(data);
    });
  }
  
  // 设置闹钟
  public setAlarm(alarm: Alarm): void {
    // 本地设置闹钟
    this.scheduleLocalNotification(alarm);
    
    // 同步到其他设备
    this.syncAlarm(alarm);
  }
  
  private scheduleLocalNotification(alarm: Alarm): void {
    const triggerTime = new Date(alarm.time);
    const currentTime = new Date();
    
    if (triggerTime <= currentTime) {
      console.warn('设置的闹钟时间必须晚于当前时间');
      return;
    }
    
    const delay = triggerTime.getTime() - currentTime.getTime();
    
    setTimeout(() => {
      this.showNotification(alarm);
    }, delay);
  }
  
  private showNotification(alarm: Alarm): void {
    try {
      notification.publish({
        id: alarm.id,
        contentType: notification.ContentType.NOTIFICATION_TEXT,
        content: {
          title: alarm.title || '闹钟提醒',
          text: alarm.content || '该起床了!',
          additionalText: alarm.additionalText || ''
        },
        tapAction: {
          want: {
            bundleName: 'com.example.alarmdemo',
            abilityName: 'AlarmNotificationAbility',
            parameters: {
              alarmId: alarm.id
            }
          }
        }
      });
    } catch (err) {
      console.error(`发布通知失败: ${JSON.stringify(err)}`);
    }
  }
  
  private syncAlarm(alarm: Alarm): void {
    this.dataManager.syncData(ALARM_CHANNEL, {
      type: 'set',
      alarm: alarm
    });
  }
  
  private handleSyncAlarm(data: any): void {
    if (data?.type === 'set' && data.alarm) {
      const alarm: Alarm = data.alarm;
      this.scheduleLocalNotification(alarm);
    }
  }
  
  // 取消闹钟
  public cancelAlarm(alarmId: string): void {
    // 本地取消
    notification.cancel(alarmId);
    
    // 同步取消
    this.dataManager.syncData(ALARM_CHANNEL, {
      type: 'cancel',
      alarmId: alarmId
    });
  }
}

interface Alarm {
  id: string;
  time: number; // 时间戳
  title?: string;
  content?: string;
  additionalText?: string;
}

2. 闹钟设置界面(ArkUI)

// AlarmSetting.ets
import { AlarmService } from './AlarmService';

@Entry
@Component
struct AlarmSetting {
  @State alarmTime: string = '08:00';
  @State alarmTitle: string = '起床闹钟';
  @State alarmContent: string = '该起床了!';
  
  private alarmService: AlarmService = AlarmService.getInstance();
  
  build() {
    Column() {
      // 时间选择器
      TimePicker({
        selected: this.alarmTime
      })
      .onChange((value: TimePickerResult) => {
        this.alarmTime = `${value.hour}:${value.minute}`;
      })
      
      // 闹钟标题输入
      TextInput({ placeholder: '闹钟标题' })
        .onChange((value: string) => {
          this.alarmTitle = value;
        })
      
      // 闹钟内容输入
      TextInput({ placeholder: '闹钟内容' })
        .onChange((value: string) => {
          this.alarmContent = value;
        })
      
      // 设置按钮
      Button('设置闹钟')
        .onClick(() => {
          this.setAlarm();
        })
    }
    .padding(20)
  }
  
  private setAlarm() {
    const now = new Date();
    const [hours, minutes] = this.alarmTime.split(':').map(Number);
    
    const alarmDate = new Date(
      now.getFullYear(),
      now.getMonth(),
      now.getDate(),
      hours,
      minutes
    );
    
    // 如果设置的时间已经过去,设置为第二天
    if (alarmDate <= now) {
      alarmDate.setDate(alarmDate.getDate() + 1);
    }
    
    const alarm: Alarm = {
      id: `alarm_${Date.now()}`,
      time: alarmDate.getTime(),
      title: this.alarmTitle,
      content: this.alarmContent
    };
    
    this.alarmService.setAlarm(alarm);
    
    // 显示设置成功提示
    promptAction.showToast({
      message: '闹钟设置成功',
      duration: 2000
    });
  }
}

3. 闹钟通知处理Ability(Java)

// AlarmNotificationAbility.java
public class AlarmNotificationAbility extends Ability {
    private static final String TAG = "AlarmNotificationAbility";
    
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        
        // 处理通知点击
        if (intent != null && intent.hasParameter("alarmId")) {
            String alarmId = intent.getStringParam("alarmId");
            HiLog.info(TAG, "处理闹钟通知点击, alarmId: " + alarmId);
            
            // 显示全屏提醒
            showFullScreenAlert(alarmId);
        }
    }
    
    private void showFullScreenAlert(String alarmId) {
        // 创建提醒对话框
        AlertDialog dialog = new AlertDialog(getContext());
        dialog.setTitle("闹钟提醒");
        dialog.setMessage("该起床了!");
        dialog.setButton(DialogInterface.BUTTON_POSITIVE, "关闭", (d, which) -> {
            terminateAbility();
        });
        
        // 全屏显示
        Window window = dialog.getWindow();
        if (window != null) {
            window.setLayout(WindowManager.LayoutConfig.MATCH_PARENT, 
                            WindowManager.LayoutConfig.MATCH_PARENT);
        }
        
        dialog.show();
        
        // 播放闹钟铃声
        playAlarmSound();
    }
    
    private void playAlarmSound() {
        // 使用媒体服务播放铃声
        // 实际实现需要添加权限和资源文件
        HiLog.info(TAG, "播放闹钟铃声");
    }
}

三、跨设备同步实现

1. 分布式数据管理配置

// module.json5
{
  "module": {
    "abilities": [
      {
        "name": "AlarmNotificationAbility",
        "type": "page",
        "visible": true
      }
    ],
    "distributedNotification": {
      "scenarios": [
        {
          "name": "alarm_scenario",
          "value": "alarm_reminder"
        }
      ]
    },
    "reqPermissions": [
      {
        "name": "ohos.permission.NOTIFICATION_CONTROLLER",
        "reason": "用于闹钟通知"
      },
      {
        "name": "ohos.permission.DISTRIBUTED_DATASYNC",
        "reason": "用于跨设备同步闹钟"
      }
    ]
  }
}

2. 增强版闹钟同步服务

// EnhancedAlarmService.ets
import notification from '@ohos.notification';
import distributedData from '@ohos.distributedData';
import deviceManager from '@ohos.distributedDeviceManager';

const ALARM_SYNC_CHANNEL = 'enhanced_alarm_sync';

class EnhancedAlarmService {
  private deviceManager: deviceManager.DeviceManager;
  private dataManager: distributedData.DataManager;
  private alarms: Map<string, Alarm> = new Map();
  
  constructor() {
    this.initDeviceManager();
    this.initDataManager();
  }
  
  private async initDeviceManager() {
    try {
      this.deviceManager = await deviceManager.createDeviceManager('com.example.alarmdemo');
      this.deviceManager.on('deviceOnline', (device) => {
        this.syncAllAlarmsToDevice(device.deviceId);
      });
    } catch (err) {
      console.error('初始化DeviceManager失败:', JSON.stringify(err));
    }
  }
  
  private initDataManager() {
    this.dataManager = distributedData.createDataManager({
      bundleName: 'com.example.alarmdemo',
      area: distributedData.Area.GLOBAL
    });
    
    this.dataManager.registerDataListener(ALARM_SYNC_CHANNEL, (data) => {
      this.handleSyncData(data);
    });
  }
  
  public setAlarm(alarm: Alarm): void {
    // 存储闹钟
    this.alarms.set(alarm.id, alarm);
    
    // 本地调度
    this.scheduleLocalAlarm(alarm);
    
    // 同步到所有设备
    this.syncAlarmToAllDevices(alarm);
  }
  
  private scheduleLocalAlarm(alarm: Alarm): void {
    const now = new Date().getTime();
    const delay = alarm.time - now;
    
    if (delay <= 0) {
      console.warn('闹钟时间已过,不进行调度');
      return;
    }
    
    setTimeout(() => {
      this.triggerAlarm(alarm);
    }, delay);
  }
  
  private triggerAlarm(alarm: Alarm): void {
    // 发布通知
    notification.publish({
      id: alarm.id,
      contentType: notification.ContentType.NOTIFICATION_TEXT,
      content: {
        title: alarm.title || '闹钟提醒',
        text: alarm.content || '该起床了!'
      },
      tapAction: {
        want: {
          bundleName: 'com.example.alarmdemo',
          abilityName: 'EnhancedAlarmNotificationAbility'
        }
      }
    });
    
    // 分布式通知
    this.notifyAllDevices(alarm);
  }
  
  private notifyAllDevices(alarm: Alarm): void {
    const devices = this.deviceManager.getTrustedDeviceListSync();
    devices.forEach(device => {
      distributedData.distributedNotification.publish({
        scenario: 'alarm_reminder',
        content: {
          alarmId: alarm.id,
          message: alarm.content
        },
        targetDevice: device.deviceId
      });
    });
  }
  
  private syncAlarmToAllDevices(alarm: Alarm): void {
    const devices = this.deviceManager.getTrustedDeviceListSync();
    devices.forEach(device => {
      this.dataManager.syncData(ALARM_SYNC_CHANNEL, {
        type: 'set',
        alarm: alarm,
        targetDevice: device.deviceId
      });
    });
  }
  
  private syncAllAlarmsToDevice(deviceId: string): void {
    this.alarms.forEach(alarm => {
      this.dataManager.syncData(ALARM_SYNC_CHANNEL, {
        type: 'set',
        alarm: alarm,
        targetDevice: deviceId
      });
    });
  }
  
  private handleSyncData(data: any): void {
    if (!data) return;
    
    switch (data.type) {
      case 'set':
        if (data.alarm) {
          this.handleSetAlarm(data.alarm);
        }
        break;
      case 'cancel':
        if (data.alarmId) {
          this.handleCancelAlarm(data.alarmId);
        }
        break;
    }
  }
  
  private handleSetAlarm(alarmData: any): void {
    const alarm: Alarm = {
      id: alarmData.id,
      time: alarmData.time,
      title: alarmData.title,
      content: alarmData.content
    };
    
    // 避免重复设置
    if (!this.alarms.has(alarm.id)) {
      this.alarms.set(alarm.id, alarm);
      this.scheduleLocalAlarm(alarm);
    }
  }
  
  private handleCancelAlarm(alarmId: string): void {
    if (this.alarms.has(alarmId)) {
      this.alarms.delete(alarmId);
      notification.cancel(alarmId);
    }
  }
}

export const enhancedAlarmService = new EnhancedAlarmService();

四、使用示例

1. 在应用中使用闹钟服务

// MainPage.ets
import { enhancedAlarmService } from './EnhancedAlarmService';

@Entry
@Component
struct MainPage {
  @State alarms: Alarm[] = [];
  
  build() {
    Column() {
      // 闹钟列表
      List({ space: 10 }) {
        ForEach(this.alarms, (alarm) => {
          ListItem() {
            this.buildAlarmItem(alarm);
          }
        })
      }
      .layoutWeight(1)
      
      // 添加闹钟按钮
      Button('添加闹钟')
        .onClick(() => {
          this.addSampleAlarm();
        })
    }
  }
  
  @Builder
  buildAlarmItem(alarm: Alarm) {
    Row() {
      Column() {
        Text(alarm.title)
          .fontSize(18)
        Text(new Date(alarm.time).toLocaleTimeString())
          .fontSize(16)
      }
      .layoutWeight(1)
      
      Button('取消')
        .onClick(() => {
          enhancedAlarmService.cancelAlarm(alarm.id);
          this.alarms = this.alarms.filter(a => a.id !== alarm.id);
        })
    }
    .padding(10)
  }
  
  private addSampleAlarm() {
    const now = new Date();
    const alarmTime = new Date(
      now.getFullYear(),
      now.getMonth(),
      now.getDate(),
      now.getHours(),
      now.getMinutes() + 2 // 2分钟后
    );
    
    const alarm: Alarm = {
      id: `alarm_${Date.now()}`,
      time: alarmTime.getTime(),
      title: '测试闹钟',
      content: '这是一个测试闹钟'
    };
    
    enhancedAlarmService.setAlarm(alarm);
    this.alarms = [...this.alarms, alarm];
    
    promptAction.showToast({
      message: '已添加测试闹钟,2分钟后响铃',
      duration: 2000
    });
  }
}

2. 跨设备同步测试

// AlarmSyncTest.ets
@Entry
@Component
struct AlarmSyncTest {
  @State localAlarms: Alarm[] = [];
  @State remoteDevices: Device[] = [];
  @State remoteAlarms: Map<string, Alarm[]> = new Map();
  
  private alarmService: EnhancedAlarmService = enhancedAlarmService;
  
  aboutToAppear() {
    this.loadDevices();
  }
  
  build() {
    Column() {
      // 本地闹钟
      Text('本地闹钟').fontSize(20)
      this.buildAlarmList(this.localAlarms)
      
      // 远程设备闹钟
      ForEach(this.remoteDevices, (device) => {
        Text(`${device.deviceName}的闹钟`).fontSize(18)
        this.buildAlarmList(this.remoteAlarms.get(device.deviceId) || [])
      })
      
      // 测试按钮
      Button('添加测试闹钟到所有设备')
        .onClick(() => {
          this.addTestAlarmToAllDevices();
        })
    }
  }
  
  @Builder
  buildAlarmList(alarms: Alarm[]) {
    Column() {
      ForEach(alarms, (alarm) => {
        Row() {
          Text(alarm.title)
          Text(new Date(alarm.time).toLocaleTimeString())
        }
      })
    }
  }
  
  private loadDevices() {
    const devices = this.alarmService.getDeviceManager().getTrustedDeviceListSync();
    this.remoteDevices = devices.map(device => ({
      deviceId: device.deviceId,
      deviceName: device.deviceName
    }));
  }
  
  private addTestAlarmToAllDevices() {
    const now = new Date();
    const alarmTime = new Date(now.getTime() + 5 * 60 * 1000); // 5分钟后
    
    const alarm: Alarm = {
      id: `test_${now.getTime()}`,
      time: alarmTime.getTime(),
      title: '跨设备测试闹钟',
      content: '这是一个跨设备同步的测试闹钟'
    };
    
    this.alarmService.setAlarm(alarm);
    this.localAlarms = [...this.localAlarms, alarm];
    
    // 模拟远程设备接收
    this.remoteDevices.forEach(device => {
      const existing = this.remoteAlarms.get(device.deviceId) || [];
      this.remoteAlarms.set(device.deviceId, [...existing, alarm]);
    });
  }
}

interface Device {
  deviceId: string;
  deviceName: string;
}

五、性能优化与最佳实践

  1. ​批量同步策略​​:当有多个闹钟需要同步时,采用批量同步减少网络请求
// 批量同步实现
public syncAllAlarms(): void {
  const alarms = Array.from(this.alarms.values());
  
  // 分批同步,每批10个
  const batchSize = 10;
  for (let i = 0; i < alarms.length; i += batchSize) {
    const batch = alarms.slice(i, i + batchSize);
    this.dataManager.syncData(ALARM_SYNC_CHANNEL, {
      type: 'batchSet',
      alarms: batch
    });
  }
}
  1. ​智能唤醒​​:在设备休眠时优化闹钟触发
// AlarmScheduler.java
public class AlarmScheduler {
    public static void scheduleExactAlarm(Context context, long triggerAtMillis) {
        // 使用精确闹钟API
        AlarmManager alarmManager = context.getSystemService(AlarmManager.class);
        if (alarmManager == null) return;
        
        Intent intent = new Intent(context, AlarmReceiver.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(
            context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            AlarmManager.AlarmClockInfo info = new AlarmManager.AlarmClockInfo(
                triggerAtMillis, pendingIntent);
            alarmManager.setAlarmClock(info, pendingIntent);
        } else {
            alarmManager.setExactAndAllowWhileIdle(
                AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
        }
    }
}
  1. ​省电模式处理​​:检测设备是否处于省电模式并调整提醒策略
// 检查省电模式
import power from '@ohos.power';

function checkPowerSaveMode(): boolean {
  try {
    const mode = power.getPowerMode();
    return mode === power.PowerMode.MODE_POWER_SAVE;
  } catch (err) {
    console.error('检查省电模式失败:', err);
    return false;
  }
}

// 调整提醒策略
function scheduleWithPowerSave(alarm: Alarm): void {
  const isPowerSave = checkPowerSaveMode();
  
  if (isPowerSave) {
    // 省电模式下使用延迟提醒
    setTimeout(() => {
      showDelayedNotification(alarm);
    }, 10000); // 延迟10秒
  } else {
    // 正常提醒
    showNotification(alarm);
  }
}

通过以上实现,我们创建了一个功能完善、跨设备同步的闹钟提醒系统,能够在鸿蒙生态中提供一致的用户体验。系统具备良好的扩展性,可以方便地添加更多高级功能如重复闹钟、智能跳过节假日等。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐