【共创季稿事节】HarmonyOS 7.0 跨设备场景实战:手机+平板+手表三端协同笔记应用

文章目录


每日一句正能量

人生的每一步都算数,你只需埋头播种,坐等秋后丰收。

付出不会消失,只是延迟显现。

导读

分布式能力是 HarmonyOS 最核心的差异化特性,也是学生开发者在毕业设计中最想展示的技术亮点。但"跨设备协同"不能只停留在概念层面------用户需要的是在手机上速记、在平板上深度编辑、在手表上查看提醒的无缝体验。本文将完整解析一个"多端协同笔记应用"的项目实现,涵盖手机端的富文本编辑、平板端的大屏深度处理、手表端的轻量提醒,以及三者之间的实时数据同步、任务接续和跨设备拖拽。所有代码基于 HarmonyOS 7.0 API 26 编写,可直接在 DevEco Studio 4.2 中运行。


一、项目架构设计:三端协同的总体思路

1.1 功能分工

设备 核心功能 交互形态 数据角色
手机 速记、拍照插入、语音转文字 单手握持、快速操作 主控端 + 数据发起
平板 长文编辑、手写批注、多窗口对照 大屏沉浸、精细操作 协同端 + 深度处理
手表 查看标题列表、语音速记、到期提醒 抬腕即用、极简交互 轻量端 + 提醒触达

1.2 技术架构

图1:三端协同笔记应用系统架构图
图片内容说明(中文):中心为"分布式数据层(UDDL)",内部包含"NoteSyncObject(笔记同步对象)"和"ConflictResolver(冲突解决器)"。中心向外连接三个设备节点:手机(包含NoteEditorAbility、CameraCapture、VoiceInput)、平板(包含NoteEditorAbility、HandwritingCanvas、SplitWindow)、手表(包含NoteListAbility、VoiceMemo、ReminderService)。三个设备之间用双向箭头标注"实时同步",箭头旁标注"sessionId: note_sync_group"。底部为"云端备份(可选)",用虚线连接中心数据层。
#mermaid-svg-2YD5I6xLqRHJQuGj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2YD5I6xLqRHJQuGj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2YD5I6xLqRHJQuGj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2YD5I6xLqRHJQuGj .error-icon{fill:#552222;}#mermaid-svg-2YD5I6xLqRHJQuGj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2YD5I6xLqRHJQuGj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2YD5I6xLqRHJQuGj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2YD5I6xLqRHJQuGj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2YD5I6xLqRHJQuGj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2YD5I6xLqRHJQuGj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2YD5I6xLqRHJQuGj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2YD5I6xLqRHJQuGj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2YD5I6xLqRHJQuGj .marker.cross{stroke:#333333;}#mermaid-svg-2YD5I6xLqRHJQuGj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2YD5I6xLqRHJQuGj p{margin:0;}#mermaid-svg-2YD5I6xLqRHJQuGj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2YD5I6xLqRHJQuGj .cluster-label text{fill:#333;}#mermaid-svg-2YD5I6xLqRHJQuGj .cluster-label span{color:#333;}#mermaid-svg-2YD5I6xLqRHJQuGj .cluster-label span p{background-color:transparent;}#mermaid-svg-2YD5I6xLqRHJQuGj .label text,#mermaid-svg-2YD5I6xLqRHJQuGj span{fill:#333;color:#333;}#mermaid-svg-2YD5I6xLqRHJQuGj .node rect,#mermaid-svg-2YD5I6xLqRHJQuGj .node circle,#mermaid-svg-2YD5I6xLqRHJQuGj .node ellipse,#mermaid-svg-2YD5I6xLqRHJQuGj .node polygon,#mermaid-svg-2YD5I6xLqRHJQuGj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2YD5I6xLqRHJQuGj .rough-node .label text,#mermaid-svg-2YD5I6xLqRHJQuGj .node .label text,#mermaid-svg-2YD5I6xLqRHJQuGj .image-shape .label,#mermaid-svg-2YD5I6xLqRHJQuGj .icon-shape .label{text-anchor:middle;}#mermaid-svg-2YD5I6xLqRHJQuGj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2YD5I6xLqRHJQuGj .rough-node .label,#mermaid-svg-2YD5I6xLqRHJQuGj .node .label,#mermaid-svg-2YD5I6xLqRHJQuGj .image-shape .label,#mermaid-svg-2YD5I6xLqRHJQuGj .icon-shape .label{text-align:center;}#mermaid-svg-2YD5I6xLqRHJQuGj .node.clickable{cursor:pointer;}#mermaid-svg-2YD5I6xLqRHJQuGj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2YD5I6xLqRHJQuGj .arrowheadPath{fill:#333333;}#mermaid-svg-2YD5I6xLqRHJQuGj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2YD5I6xLqRHJQuGj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2YD5I6xLqRHJQuGj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2YD5I6xLqRHJQuGj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2YD5I6xLqRHJQuGj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2YD5I6xLqRHJQuGj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2YD5I6xLqRHJQuGj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2YD5I6xLqRHJQuGj .cluster text{fill:#333;}#mermaid-svg-2YD5I6xLqRHJQuGj .cluster span{color:#333;}#mermaid-svg-2YD5I6xLqRHJQuGj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2YD5I6xLqRHJQuGj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2YD5I6xLqRHJQuGj rect.text{fill:none;stroke-width:0;}#mermaid-svg-2YD5I6xLqRHJQuGj .icon-shape,#mermaid-svg-2YD5I6xLqRHJQuGj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2YD5I6xLqRHJQuGj .icon-shape p,#mermaid-svg-2YD5I6xLqRHJQuGj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2YD5I6xLqRHJQuGj .icon-shape .label rect,#mermaid-svg-2YD5I6xLqRHJQuGj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2YD5I6xLqRHJQuGj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2YD5I6xLqRHJQuGj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2YD5I6xLqRHJQuGj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 手表端
平板端
手机端
分布式数据层
实时同步
实时同步
实时同步
UDDL 统一数据层
NoteSyncObject

笔记同步对象
ConflictResolver

LWW + 字段级合并
NoteEditorAbility

富文本编辑
CameraCapture

拍照插入
VoiceInput

语音转文字
NoteEditorAbility

长文编辑
HandwritingCanvas

手写批注
SplitWindow

多窗口对照
NoteListAbility

标题列表
VoiceMemo

语音速记
ReminderService

到期提醒
云端备份

可选

1.3 数据模型

typescript 复制代码
// model/NoteModel.ets
export class NoteModel {
  id: string = '';
  title: string = '';
  content: string = '';        // 富文本 HTML / MarkDown
  handwritingData: ArrayBuffer | null = null;  // 手写笔迹数据
  voiceMemoUri: string = '';   // 语音备忘路径
  tags: string[] = [];
  createTime: number = 0;
  updateTime: number = 0;
  reminderTime: number = 0;    // 提醒时间
  syncVersion: number = 0;     // 用于冲突解决
}

export class NoteChangeLog {
  noteId: string = '';
  field: string = '';          // 变更字段:title/content/handwriting/...
  oldValue: string = '';
  newValue: string = '';
  timestamp: number = 0;
  deviceId: string = '';
}

二、分布式数据同步:三端一致的基石

2.1 同步服务封装

typescript 复制代码
// service/NoteSyncService.ets
import { distributed } from '@ohos.data.distributed';
import { NoteModel } from '../model/NoteModel';

export class NoteSyncService {
  private syncObject: distributed.DistributedObject | null = null;
  private sessionId: string = 'note_sync_group';
  private listeners: Array<(notes: NoteModel[]) => void> = [];

  async init(): Promise<void> {
    // 7.0:创建分布式数据对象,启用实时同步
    this.syncObject = distributed.createObject(this.sessionId, {
      notes: [] as NoteModel[],
      lastSyncTime: 0
    }, {
      qos: distributed.QoS.HIGH_RELIABILITY,
      syncMode: distributed.SyncMode.REALTIME,
      conflictResolution: distributed.ConflictResolution.LAST_WRITE_WINS
    });

    // 监听数据变更
    this.syncObject.on('change', (sessionId: string, fields: Array<string>) => {
      if (fields.includes('notes')) {
        const notes = this.syncObject!.notes as NoteModel[];
        this.notifyListeners(notes);
      }
    });
  }

  addNote(note: NoteModel): void {
    if (!this.syncObject) return;
    const notes = this.syncObject.notes as NoteModel[];
    note.syncVersion = Date.now();
    notes.push(note);
    this.syncObject.batchUpdate({ notes });
  }

  updateNote(id: string, updates: Partial<NoteModel>): void {
    if (!this.syncObject) return;
    const notes = this.syncObject.notes as NoteModel[];
    const idx = notes.findIndex(n => n.id === id);
    if (idx >= 0) {
      notes[idx] = { ...notes[idx], ...updates, syncVersion: Date.now() };
      this.syncObject.batchUpdate({ notes });
    }
  }

  deleteNote(id: string): void {
    if (!this.syncObject) return;
    const notes = (this.syncObject.notes as NoteModel[]).filter(n => n.id !== id);
    this.syncObject.batchUpdate({ notes });
  }

  onNotesChange(callback: (notes: NoteModel[]) => void): void {
    this.listeners.push(callback);
  }

  private notifyListeners(notes: NoteModel[]): void {
    this.listeners.forEach(cb => cb(notes));
  }
}

2.2 冲突解决策略

三端同时编辑同一笔记时,采用**字段级 LWW(Last Write Wins)**策略:

typescript 复制代码
// service/ConflictResolver.ets
export class NoteConflictResolver {
  static resolve(local: NoteModel, remote: NoteModel): NoteModel {
    const merged = { ...local };

    // 逐字段比较时间戳,取较新的值
    if (remote.updateTime > local.updateTime) {
      merged.title = remote.title;
      merged.content = remote.content;
      merged.tags = remote.tags;
    }

    // 手写数据和语音备忘独立判断(不同字段可能由不同设备更新)
    if (remote.handwritingData && (!local.handwritingData || remote.syncVersion > local.syncVersion)) {
      merged.handwritingData = remote.handwritingData;
    }

    if (remote.voiceMemoUri && (!local.voiceMemoUri || remote.syncVersion > local.syncVersion)) {
      merged.voiceMemoUri = remote.voiceMemoUri;
    }

    merged.updateTime = Math.max(local.updateTime, remote.updateTime);
    merged.syncVersion = Date.now();
    return merged;
  }
}

三、任务接续:从手机速记到平板编辑

3.1 手机端发起接续

typescript 复制代码
// phone/PhoneNoteAbility.ets
import { UIAbility, Want } from '@ohos.app.ability.UIAbility';
import { distributedMissionManager } from '@ohos.distributedMissionManager';

export default class PhoneNoteAbility extends UIAbility {
  private currentNoteId: string = '';

  // 用户点击"在平板上继续编辑"
  async continueToTablet(): Promise<void> {
    const continuationDevices = await distributedMissionManager.getContinuationDevices();
    if (continuationDevices.length === 0) {
      // 提示用户先组网
      return;
    }

    // 保存当前编辑状态到 want 参数
    const want: Want = {
      bundleName: 'com.example.crossnote',
      abilityName: 'PadNoteAbility',
      parameters: {
        noteId: this.currentNoteId,
        scrollPosition: AppStorage.get('scrollPosition') || 0,
        cursorPosition: AppStorage.get('cursorPosition') || 0
      }
    };

    await distributedMissionManager.continueMission({
      srcDeviceId: '',
      dstDeviceId: continuationDevices[0].deviceId,
      missionId: this.context.missionId,
      want: want
    });
  }

  onContinue(wantParam: Record<string, Object>): AbilityConstant.OnContinueResult {
    // 手机端保存状态
    wantParam['noteId'] = this.currentNoteId;
    wantParam['lastEditTime'] = Date.now();
    return AbilityConstant.OnContinueResult.AGREE;
  }
}

3.2 平板端恢复接续

typescript 复制代码
// pad/PadNoteAbility.ets
import { UIAbility, Want } from '@ohos.app.ability.UIAbility';

export default class PadNoteAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
      // 从手机接续过来,恢复编辑状态
      const noteId = want.parameters?.['noteId'] as string;
      const scrollPosition = want.parameters?.['scrollPosition'] as number || 0;
      const cursorPosition = want.parameters?.['cursorPosition'] as number || 0;

      AppStorage.setOrCreate('continuedNoteId', noteId);
      AppStorage.setOrCreate('continuedScrollPosition', scrollPosition);
      AppStorage.setOrCreate('continuedCursorPosition', cursorPosition);
    }
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    windowStage.loadContent('pad/pages/PadEditor', (err) => {
      if (err.code) {
        console.error('加载平板编辑页失败:', err);
      }
    });
  }
}

3.3 接续状态恢复 UI

typescript 复制代码
// pad/pages/PadEditor.ets
@Entry
@Component
struct PadEditorPage {
  @State note: NoteModel = new NoteModel();
  @State scrollPosition: number = 0;
  private syncService: NoteSyncService = new NoteSyncService();

  aboutToAppear() {
    this.syncService.init();
    this.syncService.onNotesChange((notes) => {
      const continuedId = AppStorage.get('continuedNoteId') as string;
      const found = notes.find(n => n.id === continuedId);
      if (found) {
        this.note = found;
        this.scrollPosition = AppStorage.get('continuedScrollPosition') as number || 0;
      }
    });
  }

  build() {
    Column() {
      Text('从手机接续编辑')
        .fontSize(14)
        .fontColor('#666')
        .visibility(this.note.id ? Visibility.Visible : Visibility.None)

      TextInput({ text: $$this.note.title })
        .fontSize(24)
        .fontWeight(FontWeight.Bold)

      RichEditor({ text: $$this.note.content })
        .layoutWeight(1)

      // 手写批注区域(平板特有)
      HandwritingCanvas({
        data: this.note.handwritingData,
        onChange: (data) => {
          this.syncService.updateNote(this.note.id, { handwritingData: data });
        }
      })
    }
    .width('100%')
    .height('100%')
  }
}

四、跨设备拖拽:从手机拖图片到平板

4.1 拖拽发起(手机端)

typescript 复制代码
// components/DraggableImage.ets
@Component
struct DraggableImage {
  @Prop imageUri: string;
  @Prop noteId: string;

  build() {
    Image(this.imageUri)
      .width(100)
      .height(100)
      .borderRadius(8)
      .draggable(true)
      .onDragStart((event: DragEvent) => {
        // 7.0:设置跨设备拖拽数据
        event.setData({
          uniformDataItems: [
            {
              uniformDataType: 'image',
              uri: this.imageUri,
              noteId: this.noteId
            }
          ],
          dragProperties: {
            crossDevice: true,           // 允许跨设备拖拽
            supportedDevices: ['tablet'] // 目标设备类型
          }
        });
      })
  }
}

4.2 拖拽接收(平板端)

typescript 复制代码
// pad/components/DropZone.ets
@Component
struct DropZone {
  @State droppedImages: string[] = [];
  private syncService: NoteSyncService = new NoteSyncService();

  build() {
    Column() {
      Text('拖拽图片到此处')
        .fontSize(16)
        .fontColor('#999')

      ForEach(this.droppedImages, (uri: string) => {
        Image(uri).width(120).height(120)
      })
    }
    .width('100%')
    .height(200)
    .border({ width: 2, color: '#DDD', style: BorderStyle.Dashed })
    .backgroundColor('#F9F9F9')
    .dropArea({ ... })  // 声明可接收拖拽
    .onDrop((event: DragEvent, extraParams: string) => {
      const dragData = event.getData();
      const imageItem = dragData.uniformDataItems.find(
        item => item.uniformDataType === 'image'
      );

      if (imageItem) {
        // 接收跨设备图片
        this.droppedImages.push(imageItem.uri);

        // 同步到笔记中
        const noteId = imageItem.noteId;
        this.syncService.updateNote(noteId, {
          content: `![插入图片](${imageItem.uri})`
        });
      }
    })
  }
}

五、手表端:轻量提醒与语音速记

5.1 手表端 Ability

typescript 复制代码
// watch/WatchNoteAbility.ets
import { UIAbility } from '@ohos.app.ability.UIAbility';

export default class WatchNoteAbility extends UIAbility {
  onCreate(): void {
    // 手表端仅显示标题列表和语音速记入口
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    windowStage.loadContent('watch/pages/WatchList');
  }
}

5.2 手表端列表与语音速记

typescript 复制代码
// watch/pages/WatchList.ets
@Entry
@Component
struct WatchListPage {
  @State notes: NoteModel[] = [];
  @State isRecording: boolean = false;
  private syncService: NoteSyncService = new NoteSyncService();

  aboutToAppear() {
    this.syncService.init();
    this.syncService.onNotesChange((allNotes) => {
      // 手表端仅显示有提醒的笔记和最近 5 条
      this.notes = allNotes
        .filter(n => n.reminderTime > Date.now() || n.updateTime > Date.now() - 86400000)
        .slice(0, 5);
    });
  }

  build() {
    Column() {
      List() {
        ForEach(this.notes, (note: NoteModel) => {
          ListItem() {
            Column() {
              Text(note.title || '无标题')
                .fontSize(16)
                .maxLines(1)
                .textOverflow({ overflow: TextOverflow.Ellipsis })

              if (note.reminderTime > Date.now()) {
                Text(this.formatTime(note.reminderTime))
                  .fontSize(12)
                  .fontColor('#FF6B6B')
              }
            }
            .width('100%')
            .padding(8)
          }
        })
      }

      // 语音速记按钮
      Button(this.isRecording ? '录音中...' : '语音速记')
        .width(120)
        .height(120)
        .type(ButtonType.Circle)
        .backgroundColor(this.isRecording ? '#FF6B6B' : '#4ECDC4')
        .onClick(() => this.toggleRecording())
    }
    .width('100%')
    .height('100%')
    .padding(8)
  }

  async toggleRecording(): Promise<void> {
    if (this.isRecording) {
      // 停止录音,保存
      const audioUri = await this.stopRecording();
      const newNote = new NoteModel();
      newNote.id = `voice_${Date.now()}`;
      newNote.voiceMemoUri = audioUri;
      newNote.title = '语音速记';
      newNote.createTime = Date.now();
      this.syncService.addNote(newNote);
      this.isRecording = false;
    } else {
      // 开始录音
      await this.startRecording();
      this.isRecording = true;
    }
  }

  private formatTime(timestamp: number): string {
    const date = new Date(timestamp);
    return `${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`;
  }
}

六、数据同步时序:一次完整的协同流程

图2:三端协同笔记应用数据同步时序图
图片内容说明(中文):时序图,四个泳道从上到下为"手机端"、"平板端"、"手表端"、"分布式数据层"。流程:①手机端创建笔记→写入UDDL→②UDDL推送变更→平板端/手表端同步收到。③平板端编辑标题→写入UDDL→④手机端/手表端收到更新。⑤手表端语音速记→写入UDDL→⑥手机端/平板端收到新笔记。⑦手机端跨设备拖拽图片到平板→平板端接收并更新UDDL→⑧所有端同步显示图片。各操作旁标注时间戳和syncVersion。
分布式数据层 手表端 平板端 手机端 分布式数据层 手表端 平板端 手机端 #mermaid-svg-BHclOAddLccoZerY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BHclOAddLccoZerY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BHclOAddLccoZerY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BHclOAddLccoZerY .error-icon{fill:#552222;}#mermaid-svg-BHclOAddLccoZerY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BHclOAddLccoZerY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BHclOAddLccoZerY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BHclOAddLccoZerY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BHclOAddLccoZerY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BHclOAddLccoZerY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BHclOAddLccoZerY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BHclOAddLccoZerY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BHclOAddLccoZerY .marker.cross{stroke:#333333;}#mermaid-svg-BHclOAddLccoZerY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BHclOAddLccoZerY p{margin:0;}#mermaid-svg-BHclOAddLccoZerY .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-BHclOAddLccoZerY text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-BHclOAddLccoZerY .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-BHclOAddLccoZerY .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-BHclOAddLccoZerY .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-BHclOAddLccoZerY .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-BHclOAddLccoZerY #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-BHclOAddLccoZerY .sequenceNumber{fill:white;}#mermaid-svg-BHclOAddLccoZerY #sequencenumber{fill:#333;}#mermaid-svg-BHclOAddLccoZerY #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-BHclOAddLccoZerY .messageText{fill:#333;stroke:none;}#mermaid-svg-BHclOAddLccoZerY .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-BHclOAddLccoZerY .labelText,#mermaid-svg-BHclOAddLccoZerY .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-BHclOAddLccoZerY .loopText,#mermaid-svg-BHclOAddLccoZerY .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-BHclOAddLccoZerY .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-BHclOAddLccoZerY .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-BHclOAddLccoZerY .noteText,#mermaid-svg-BHclOAddLccoZerY .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-BHclOAddLccoZerY .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-BHclOAddLccoZerY .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-BHclOAddLccoZerY .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-BHclOAddLccoZerY .actorPopupMenu{position:absolute;}#mermaid-svg-BHclOAddLccoZerY .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-BHclOAddLccoZerY .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-BHclOAddLccoZerY .actor-man circle,#mermaid-svg-BHclOAddLccoZerY line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-BHclOAddLccoZerY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 收到新笔记 Note-A 标题更新 收到语音笔记 内容同步更新 创建笔记 Note-A syncVersion=1001 推送变更: notes\[\] 推送变更: notes\[\] 编辑标题 syncVersion=1002 推送变更 推送变更 语音速记 创建语音笔记 Note-B syncVersion=1003 推送变更 推送变更 跨设备拖拽图片 更新笔记内容(插入图片) syncVersion=1004 推送变更 推送变更


七、测试验证要点

测试场景 操作步骤 预期结果
实时同步 手机创建笔记 平板和手表 1 秒内显示新笔记
字段级冲突 手机改标题,平板改内容(同时) 两端都保留:新标题 + 新内容
任务接续 手机编辑到一半,点击"平板继续" 平板打开同一笔记,光标位置一致
跨设备拖拽 手机相册图片拖入平板编辑区 平板显示图片,笔记内容同步更新
离线恢复 手表断网后语音速记,恢复网络后 语音笔记自动同步到手机和平板
手表提醒 设置提醒时间,到达时间点 手表震动提醒,显示笔记标题

八、结语

跨设备协同不是"把同一个应用装到三个设备上",而是"让三个设备共同完成一个任务的不同片段"。手机适合速记和采集,平板适合深度编辑,手表适合提醒和轻量查看------各自发挥所长,数据实时贯通。

HarmonyOS 7.0 的分布式数据对象、任务接续和跨设备拖拽,为这种"场景化协同"提供了底层能力支撑。开发者不再需要关心设备之间如何组网、如何传输、如何解决冲突,只需关注"用户在每个设备上需要做什么"。

对于高校学生开发者,三端协同项目是一个极具展示价值的毕业设计选题。它既体现了对鸿蒙分布式能力的深入理解,又展现了完整的系统架构设计能力。当你在答辩现场演示"手机拍照→拖拽到平板→平板手写批注→手表提醒复习"的完整链路时,评委看到的不仅是一个 App,更是一个生态。


转载自:https://blog.csdn.net/u014727709/article/details/162933246

欢迎 👍点赞✍评论⭐收藏,欢迎指正

相关推荐
想你依然心痛13 小时前
【共创季稿事节】HarmonyOS 7.0 应用框架(ArkUI-X)跨平台能力深度探索
flutter·react native·arkui-x·跨平台渲染·harmonyos 7.0·arkrender·组件兼容性
想你依然心痛1 天前
【共创季稿事节】HarmonyOS 7.0 车机/IoT/穿戴跨场景生态前瞻
matter协议·harmonyos 7.0·跨场景生态·分布式座舱·场景自治引擎·运动健康网络·意图框架
想你依然心痛1 天前
【共创季稿事节】HarmonyOS 7.0 安全架构升级前瞻:星盾安全体系下一代演进
可信执行环境·零信任架构·harmonyos 7.0·星盾安全体系·权限契约化·分布式tee·隐私决策引擎
想你依然心痛2 天前
【共创季稿事节】HarmonyOS 7.0 分布式软总线架构演进与多端协同前瞻
超级终端·零信任安全·分布式软总线·harmonyos 7.0·多端协同·意图驱动发现·星闪协议