0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

HarmonyOS開發(fā)實例:【分布式手寫板】

jf_46214456 ? 來源:jf_46214456 ? 作者:jf_46214456 ? 2024-04-17 21:45 ? 次閱讀

介紹

本篇Codelab使用設備管理及分布式鍵值數(shù)據(jù)庫能力,實現(xiàn)多設備之間手寫板應用拉起及同步書寫內容的功能。操作流程:

  1. 設備連接同一無線網(wǎng)絡,安裝分布式手寫板應用。進入應用,點擊允許使用多設備協(xié)同,點擊主頁上查詢設備按鈕,顯示附近設備。
  2. 選擇設備確認,若已建立連接,啟動對方設備上的手寫板應用,否則提示建立連接。輸入PIN碼建立連接后再次點擊查詢設備按鈕,選擇設備提交,啟動對方設備應用。
  3. 建立連接前繪制的內容在啟動對方設備后同步,此時設備上繪制的內容會在另一端同步繪制。
  4. 點擊撤銷按鈕,兩側設備繪制內容同步撤銷。

相關概念

  • [設備管理]:模塊提供分布式設備管理能力。
  • [分布式鍵值數(shù)據(jù)庫]:分布式鍵值數(shù)據(jù)庫為應用程序提供不同設備間數(shù)據(jù)庫的分布式協(xié)同能力。

相關權限

本篇Codelab使用了設備管理及分布式鍵值數(shù)據(jù)庫能力,需要手動替換full-SDK,并在配置文件module.json5文件requestPermissions屬性中添加如下權限:

  • [分布式設備認證組網(wǎng)權限]:ohos.permission.ACCESS_SERVICE_DM。
  • [設備間的數(shù)據(jù)交換權限]:ohos.permission.DISTRIBUTED_DATASYNC。

約束與限制

  1. 本篇Codelab部分能力依賴于系統(tǒng)API,需下載full-SDK并替換DevEco Studio自動下載的public-SDK。
  2. 本篇Codelab使用的部分API僅系統(tǒng)應用可用,需要提升應用等級。

環(huán)境搭建

軟件要求

  • [DevEco Studio]版本:DevEco Studio 4.0 Beta2。
  • OpenHarmony SDK版本:API version 10。
  • 鴻蒙指導參考:[qr23.cn/AKFP8k]

搜狗高速瀏覽器截圖20240326151547.png

硬件要求

  • 開發(fā)板類型:[潤和RK3568開發(fā)板]。
  • OpenHarmony系統(tǒng):4.0 Release。

環(huán)境搭建

完成本篇Codelab我們首先要完成開發(fā)環(huán)境的搭建,本示例以RK3568開發(fā)板為例,參照以下步驟進行:

  1. [獲取OpenHarmony系統(tǒng)版本]:標準系統(tǒng)解決方案(二進制)。以4.0 Release版本為例:
  2. 搭建燒錄環(huán)境。
    1. [完成DevEco Device Tool的安裝]
    2. [完成RK3568開發(fā)板的燒錄]
  3. 搭建開發(fā)環(huán)境。
    1. 開始前請參考[工具準備],完成DevEco Studio的安裝和開發(fā)環(huán)境配置。
    2. 開發(fā)環(huán)境配置完成后,請參考[使用工程向導]創(chuàng)建工程(模板選擇“Empty Ability”)。
    3. 工程創(chuàng)建完成后,選擇使用[真機進行調測]。

代碼結構解讀

本篇Codelab只對核心代碼進行講解,對于完整代碼,我們會在gitee中提供。

├──entry/src/main/ets                 // 代碼區(qū)
│  ├──common
│  │  ├──constants
│  │  │  └──CommonConstants.ets       // 公共常量類
│  │  └──utils
│  │     ├──Logger.ets                // 日志打印類
│  │     └──RemoteDeviceUtil.ets      // 設備管理類
│  ├──entryability
│  │  └──EntryAbility.ets             // 程序入口類
│  ├──pages
│  │  └──Index.ets                    // 主界面
│  ├──view
│  │  └──CustomDialogComponent.ets    // 自定義彈窗組件類
│  └──viewmodel
│     ├──KvStoreModel.ets             // 分布式鍵值數(shù)據(jù)庫管理類
│     └──Position.ets                 // 繪制位置信息
└──entry/src/main/resources           // 資源文件目錄

界面設計

主界面由導航欄及繪制區(qū)域組成,導航欄包含撤回按鈕及查詢設備按鈕。繪制區(qū)域使用Canvas畫布組件展示繪制效果。Index.ets文件完成界面實現(xiàn),使用Column及Row容器組件進行布局。

// Index.ets
let storage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {
  ...
  build() {
    Column() {
      Row() {
        // 撤回按鈕
        Image($r('app.media.ic_back'))
          .width($r('app.float.ic_back_width'))
          .height($r('app.float.ic_back_height'))
          ...
        Blank()
        // 查找設備按鈕
        Image($r('app.media.ic_hop'))
          .width($r('app.float.ic_hop_width'))
          .height($r('app.float.ic_hop_height'))
          ...
      }
      .width(CommonConstants.FULL_PERCENT)
      .height(CommonConstants.TITLE_HEIGHT)

      Row() {
        // 繪制區(qū)域
        Canvas(this.canvasContext)
          .width(CommonConstants.FULL_PERCENT)
          .height(CommonConstants.FULL_PERCENT)
          ...
      }
      ...
      .width(CommonConstants.FULL_PERCENT)
      .layoutWeight(CommonConstants.NUMBER_ONE)
    }
    .height(CommonConstants.FULL_PERCENT)
    .width(CommonConstants.FULL_PERCENT)
  }
  ...
}

分布式組網(wǎng)

準備分布式環(huán)境

創(chuàng)建設備管理器。設備管理器創(chuàng)建完成后注冊設備上線離線監(jiān)聽,信任設備上線離線時觸發(fā)。執(zhí)行獲取本地設備信息,獲取信任設備列表,初始化展示設備列表等方法。其中deviceManager類需使用full-SDK。

// RemoteDeviceUtil.ets
import deviceManager from '@ohos.distributedHardware.deviceManager';

class RemoteDeviceUtil {
  ...
  async createDeviceManager() {
    ...
    await new Promise((resolve: (value: Object | PromiseLike< Object >) = > void, reject: ((reason?: RejectError) = > void)) = > {
      try {
        // 創(chuàng)建設備管理器
        deviceManager.createDeviceManager(CommonConstants.BUNDLE_NAME,
          (error, value: deviceManager.DeviceManager) = > {
            ...
            this.myDeviceManager = value;
            // 注冊信任設備上線離線監(jiān)聽
            this.registerDeviceStateListener();
            // 獲取本地設備信息
            this.getLocalDeviceInfo();
            // 獲取信任設備列表
            this.getTrustedDeviceList();
            // 初始化展示設備列表
            this.initDeviceList();
            resolve(value);
        });
      } catch (error) {
        Logger.error('RemoteDeviceModel',
          `createDeviceManager failed, error=${JSON.stringify(error)}`);
      }
    });
  }
  ...
}

注冊設備狀態(tài)監(jiān)聽。已驗證設備上線或有新設備驗證通過時狀態(tài)類型為ONLINE,將設備添加至信任設備列表。設備離線時狀態(tài)類型為OFFLINE,將設備從信任列表中移除。

// RemoteDeviceUtil.ets
class RemoteDeviceUtil {
  ...
  // 注冊設備狀態(tài)改變監(jiān)聽
  registerDeviceStateListener(): void {
    ...
    try {
      // 注冊監(jiān)聽
      this.myDeviceManager.on('deviceStateChange', (data) = > {
        ...
        switch (data.action) {
          // 設備上線
          case deviceManager.DeviceStateChangeAction.ONLINE: {
            this.deviceStateChangeActionOnline(data.device);
            break;
          }
          // 設備離線
          case deviceManager.DeviceStateChangeAction.OFFLINE: {
            this.deviceStateChangeActionOffline(data.device);
            break;
          }
          ...
        }
      });
    } catch (error) {
      Logger.error('RemoteDeviceModel',
        `registerDeviceStateListener on('deviceStateChange') failed, error=${JSON.stringify(error)}`);
    }
  }

  // 設備上線,加入信任列表及展示列表
  deviceStateChangeActionOnline(device: deviceManager.DeviceInfo): void {
    this.trustedDeviceList[this.trustedDeviceList.length] = device;
    this.addToDeviceList(device);
  }

  // 設備下線,將設備移出信任列表和展示列表
  deviceStateChangeActionOffline(device: deviceManager.DeviceInfo): void {
    let list: deviceManager.DeviceInfo[] = [];
    for (let i: number = 0; i < this.trustedDeviceList.length; i++) {
      if (this.trustedDeviceList[i].networkId !== device.networkId) {
        list.push(this.trustedDeviceList[i]);
        continue;
      }
    }
    this.deleteFromDeviceList(device);
    this.trustedDeviceList = list;
  }
  ...
}

建立分布式連接

點擊主界面的查詢設備按鈕,執(zhí)行發(fā)現(xiàn)設備方法,注冊設備發(fā)現(xiàn)監(jiān)聽任務,同時拉起彈窗展示設備列表。當彈窗關閉時,執(zhí)行停止發(fā)現(xiàn)設備方法,注銷監(jiān)聽任務。

// RemoteDeviceUtil.ets
class RemoteDeviceUtil {
  ...
  // 處理新發(fā)現(xiàn)的設備
  deviceFound(data: DeviceInfoInterface): void {
    for (let i: number = 0; i < this.discoverList.length; i++) {
      if (this.discoverList[i].deviceId === data.device.deviceId) {
        Logger.info('RemoteDeviceModel', `deviceFound device exist=${JSON.stringify(data)}`);
        return;
      }
    }
    this.discoverList[this.discoverList.length] = data.device;
    this.addToDeviceList(data.device);
  }

  startDeviceDiscovery(): void {
    ...
    try {
      // 注冊發(fā)現(xiàn)設備監(jiān)聽
      this.myDeviceManager.on('deviceFound', (data) = > {
        ...
        // 處理發(fā)現(xiàn)的設備
        this.deviceFound(data);
      });
      ...
      let info: deviceManager.SubscribeInfo = {
        subscribeId: this.subscribeId,
        mode: CommonConstants.SUBSCRIBE_MODE,
        medium: CommonConstants.SUBSCRIBE_MEDIUM,
        freq: CommonConstants.SUBSCRIBE_FREQ,
        isSameAccount: false,
        isWakeRemote: true,
        capability: CommonConstants.SUBSCRIBE_CAPABILITY
      };
      // 發(fā)現(xiàn)周邊設備
      this.myDeviceManager.startDeviceDiscovery(info);
    } catch (error) {
      Logger.error('RemoteDeviceModel',
        `startDeviceDiscovery failed error=${JSON.stringify(error)}`);
    }
  }

  // 停止發(fā)現(xiàn)設備
  stopDeviceDiscovery(): void {
    ...
    try {
      // 停止發(fā)現(xiàn)設備
      this.myDeviceManager.stopDeviceDiscovery(this.subscribeId);
      // 注銷監(jiān)聽任務
      this.myDeviceManager.off('deviceFound');
      this.myDeviceManager.off('discoverFail');
    } catch (error) {
      Logger.error('RemoteDeviceModel',
        `stopDeviceDiscovery failed error=${JSON.stringify(error)}`);
    }
  }
  ...
}

選擇彈窗內的設備項提交后,執(zhí)行設備驗證。

  1. 若設備在信任設備列表,執(zhí)行startAbility()方法啟動連接設備上的應用,將當前的繪制信息作為參數(shù)發(fā)送至連接設備。
  2. 若設備不是信任設備,執(zhí)行authenticateDevice()方法啟動驗證。此時連接設備提示是否接受,接收連接后連接設備展示PIN碼,本地設備輸入PIN碼確認后連接成功。再次點擊查詢設備按鈕,選擇已連接設備,點擊確認啟動連接設備上的應用。
// RemoteDeviceUtil.ets
class RemoteDeviceUtil {
  ...
  // 設備驗證
  authenticateDevice(
    context: common.UIAbilityContext,
    device: deviceManager.DeviceInfo,
    positionList: Position[]
  ): void {
    // 設備為信任設備,啟動連接設備上的應用
    let tmpList = this.trustedDeviceList.filter((item: deviceManager.DeviceInfo) = > device.deviceId === item.deviceId);
    if (tmpList.length > 0) {
      this.startAbility(context, device, positionList);
      return;
    }
    ...
    try {
      // 執(zhí)行設備認證,啟動驗證相關彈窗,接受信任,顯示PIN碼,輸入PIN碼等
      this.myDeviceManager.authenticateDevice(device, authParam, (err) = > {
        ...
      })
    } catch (error) {
      Logger.error('RemoteDeviceModel',
        `authenticateDevice failed error=${JSON.stringify(error)}`);
    }
  }

  // 啟動連接設備上的應用
  startAbility(context: common.UIAbilityContext, device: deviceManager.DeviceInfo, positionList: Position[]): void {
    ...
    // 啟動連接設備上的應用
    context.startAbility(wantValue).then(() = > {
      Logger.info('RemoteDeviceModel', `startAbility finished wantValue=${JSON.stringify(wantValue)}`);
    }).catch((error: Error) = > {
      Logger.error('RemoteDeviceModel', `startAbility failed, error=${JSON.stringify(error)}`);
    })
  }
  ...
}

資源釋放

程序關閉時,注銷設備狀態(tài)監(jiān)聽任務,并釋放DeviceManager實例。

// RemoteDeviceUtil.ets
class RemoteDeviceUtil {
  ...
  // 注銷監(jiān)聽任務
  unregisterDeviceListCallback(): void {
    ...
    try {
      // 注銷設備狀態(tài)監(jiān)聽
      this.myDeviceManager.off('deviceStateChange');
      // 釋放DeviceManager實例
      this.myDeviceManager.release();
    } catch (err) {
      Logger.error('RemoteDeviceModel',
        `unregisterDeviceListCallback stopDeviceDiscovery failed, error=${JSON.stringify(err)}`);
    }
  }
  ...
}

繪制功能

Canvas組件區(qū)域監(jiān)聽觸摸事件,按照按下、移動、抬起等觸摸事件,記錄繪制的起點、中間點以及終點。觸摸事件觸發(fā)時,使用CanvasRenderingContext2D對象的繪制方法根據(jù)位置信息進行繪制。繪制結束后,將當前位置信息列表存入分布式鍵值數(shù)據(jù)庫。

// Index.ets
let storage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {
  ...  
  build() {
    Column() {
      ...
      Row() {
        Canvas(this.canvasContext)
          ...
      }
      .onTouch((event: TouchEvent) = > {
        this.onTouchEvent(event);
      })
      ...
    }
    ...
  }

  // 繪制事件
  onTouchEvent(event: TouchEvent): void {
    let positionX: number = event.touches[0].x;
    let positionY: number = event.touches[0].y;
    switch (event.type) {
      // 手指按下
      case TouchType.Down: {
        this.canvasContext.beginPath();
        this.canvasContext.lineWidth = CommonConstants.CANVAS_LINE_WIDTH;
        this.canvasContext.lineJoin = CommonConstants.CANVAS_LINE_JOIN;
        this.canvasContext.moveTo(positionX, positionY);
        this.pushData(true, false, positionX, positionY);
        break;
      }
      // 手指移動
      case TouchType.Move: {
        this.canvasContext.lineTo(positionX, positionY);
        this.pushData(false, false, positionX, positionY);
        break;
      }
      // 手指抬起
      case TouchType.Up: {
        this.canvasContext.lineTo(positionX, positionY);
        this.canvasContext.stroke();
        this.pushData(false, true, positionX, positionY);
        break;
      }
      default: {
        break;
      }
    }
  }

  pushData(isFirstPosition: boolean, isEndPosition: boolean, positionX: number, positionY: number): void {
    let position = new Position(isFirstPosition, isEndPosition, positionX, positionY);
    // 存入位置信息列表
    this.positionList.push(position);
    if (position.isEndPosition) {
      // 當前位置為終點時,將位置信息列表存入分布式鍵值數(shù)據(jù)庫
      this.kvStoreModel.put(CommonConstants.CHANGE_POSITION, JSON.stringify(this.positionList));
    }
  }
  ...
}

點擊撤銷按鈕時,從位置列表中后序遍歷移除位置信息,直到找到軌跡的初始位置,完成移除上一次繪制的軌跡。移除完成后將位置信息列表存入分布式鍵值數(shù)據(jù)庫中。執(zhí)行redraw()方法,清空畫板上的內容,遍歷位置信息列表,重新繪制。

// Index.ets
let storage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {
  ...
  @LocalStorageProp('positionList') positionList: Position[] = [];
  ...
  build() {
    Column() {
      Row() {
        // 撤銷按鈕
        Image($r('app.media.ic_back'))
          .width($r('app.float.ic_back_width'))
          .height($r('app.float.ic_back_height'))
          .margin({ left: CommonConstants.ICON_MARGIN_LEFT })
          .onClick(() = > {
            this.goBack();
          })
        ...
      }
      .width(CommonConstants.FULL_PERCENT)
      .height(CommonConstants.TITLE_HEIGHT)
      ...
  }

  ...
  redraw(): void {
    // 刪除畫布內的繪制內容
    this.canvasContext.clearRect(0, 0, this.canvasContext.width, this.canvasContext.height);
    // 使用當前記錄的位置信息,重新繪制
    this.positionList.forEach((position) = > {
      ...
      if (position.isFirstPosition) {
        this.canvasContext.beginPath();
        this.canvasContext.lineWidth = CommonConstants.CANVAS_LINE_WIDTH;
        this.canvasContext.lineJoin = CommonConstants.CANVAS_LINE_JOIN;
        this.canvasContext.moveTo(position.positionX, position.positionY);
      } else {
        this.canvasContext.lineTo(position.positionX, position.positionY);
        if (position.isEndPosition) {
          this.canvasContext.stroke();
        }
      }
    });
  }


  // 撤回上一筆繪制
  goBack(): void {
    if (this.positionList.length === 0) {
      return;
    }
    // 移除位置信息直到位置起始位置
    for (let i: number = this.positionList.length - 1; i >= 0; i--) {
      let position: Position | undefined = this.positionList.pop();
      if (position !== undefined && position.isFirstPosition) {
        break;
      }
    }
    this.redraw();
    this.kvStoreModel.put(CommonConstants.CHANGE_POSITION, JSON.stringify(this.positionList));
  }
  ...
}

分布式鍵值數(shù)據(jù)庫

使用分布式鍵值數(shù)據(jù)庫需申請數(shù)據(jù)交換權限:ohos.permission.DISTRIBUTED_DATASYNC。

應用啟動時創(chuàng)建分布式鍵值數(shù)據(jù)庫,設置數(shù)據(jù)庫數(shù)據(jù)改變監(jiān)聽。數(shù)據(jù)改變時執(zhí)行回調,獲取插入或更新數(shù)據(jù)列表,遍歷列表,匹配位置信息列表的設置key,更新位置列表后重新繪制。

// Index.ets
...
import KvStoreModel from '../viewmodel/KvStoreModel';
...
let storage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {
  ...
  private kvStoreModel: KvStoreModel = new KvStoreModel();
  ...
  aboutToAppear() {
    ...
    this.createKVStore();
  }

  ...
  createKVStore(): void {
    // 創(chuàng)建分布式鍵值數(shù)據(jù)庫
    this.kvStoreModel.createKvStore(this.context, (data: distributedKVStore.ChangeNotification) = > {
      // 使用分布式鍵值數(shù)據(jù)庫內的內容重置位置信息列表
      this.positionList = [];
      let entries: distributedKVStore.Entry[] = data.insertEntries.length > 0 ? data.insertEntries : data.updateEntries;
      entries.forEach((entry: distributedKVStore.Entry) = > {
        if (CommonConstants.CHANGE_POSITION === entry.key) {
          this.positionList = JSON.parse((entry.value.value) as string);
          // 位置信息列表更新后,重新繪制
          this.redraw();
        }
      });
    });
  }
  ...
}

創(chuàng)建分布式鍵值數(shù)據(jù)庫。設置數(shù)據(jù)庫類型為KVStoreType.SINGLE_VERSION單版本數(shù)據(jù)庫,其他配置參考[創(chuàng)建數(shù)據(jù)庫配置信息]。創(chuàng)建數(shù)據(jù)庫成功后,調用enableSync()方法開啟同步,調用setDataChangeListener()方法訂閱數(shù)據(jù)變更通知。

// KvStoreModel.ets
export default class KvStoreModel {
  ...
  kvStore?: distributedKVStore.SingleKVStore;
  ...
  createKvStore(
    context: common.UIAbilityContext,
    callback: (data: distributedKVStore.ChangeNotification) = > void
  ): void {
    ...
    try {
      // 創(chuàng)建一個KVManager對象實例,用于管理數(shù)據(jù)庫對象
      this.kvManager = distributedKVStore.createKVManager(config);
    } catch (error) {
      Logger.error('KvStoreModel',
        `createKvStore createKVManager failed, err=${JSON.stringify(error)}`);
      return;
    }

    // 創(chuàng)建數(shù)據(jù)庫的配置信息
    let options: distributedKVStore.Options = {
      ...
      kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION
      ...
    };

    // 獲取分布式鍵值數(shù)據(jù)庫
    this.kvManager.getKVStore(CommonConstants.KVSTORE_ID, options).then((store: distributedKVStore.SingleKVStore) = > {
      ...
      this.kvStore = store;
      // 開啟同步
      this.kvStore.enableSync(true).then(() = > {
        Logger.info('KvStoreModel', 'createKvStore enableSync success');
      }).catch((error: Error) = > {
        Logger.error('KvStoreModel',
          `createKvStore enableSync fail, error=${JSON.stringify(error)}`);
      });
      this.setDataChangeListener(callback);
    }).catch((error: Error) = > {
      Logger.error('getKVStore',
        `createKvStore getKVStore failed, error=${JSON.stringify(error)}`);
    })
  }
  ...
}

訂閱數(shù)據(jù)變更通知。創(chuàng)建分布式鍵值數(shù)據(jù)庫,設置數(shù)據(jù)變更訂閱,訂閱類型為全部,當更新數(shù)據(jù)集或插入數(shù)據(jù)集大于0時,執(zhí)行傳入的callback()方法。

// KvStoreModel.ets
export default class KvStoreModel {
  ...
  kvStore?: distributedKVStore.SingleKVStore;
  ...
  setDataChangeListener(callback: (data: distributedKVStore.ChangeNotification) = > void): void {
    ...
    try {
      // 訂閱數(shù)據(jù)變更通知
      this.kvStore.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL,
        (data: distributedKVStore.ChangeNotification) = > {
          if ((data.updateEntries.length > 0) || (data.insertEntries.length > 0)) {
            callback(data);
          }
        });
    } catch (error) {
      Logger.error('KvStoreModel',
        `setDataChangeListener on('dataChange') failed, err=${JSON.stringify(error)}`);
    }
  }
  ...
}

應用退出時,分布式鍵值數(shù)據(jù)庫取消數(shù)據(jù)改變監(jiān)聽。

// Index.ets
...
import KvStoreModel from '../viewmodel/KvStoreModel';
...
let storage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {
  ...
  private kvStoreModel: KvStoreModel = new KvStoreModel();
  ...
  aboutToDisappear() {
    this.kvStoreModel.removeDataChangeListener();
  }
  ...
}

// KvStoreModel.ets
export default class KvStoreModel {
  ...
  kvStore?: distributedKVStore.SingleKVStore;
  ...
  removeDataChangeListener(): void {
    ...
    try {
      // 取消數(shù)據(jù)改變監(jiān)聽
      this.kvStore.off('dataChange');
    } catch (error) {
      Logger.error('KvStoreModel',
        `removeDataChangeListener off('dataChange') failed, err=${JSON.stringify(error)}`);
    }
  }
  ...
}

審核編輯 黃宇

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 分布式
    +關注

    關注

    1

    文章

    858

    瀏覽量

    74439
  • HarmonyOS
    +關注

    關注

    79

    文章

    1966

    瀏覽量

    29962
  • OpenHarmony
    +關注

    關注

    25

    文章

    3635

    瀏覽量

    16061
  • 鴻蒙OS
    +關注

    關注

    0

    文章

    188

    瀏覽量

    4359
收藏 人收藏

    評論

    相關推薦

    HarmonyOS開發(fā)實例:【分布式數(shù)據(jù)管理】

    eTS中分布式數(shù)據(jù)管理的使用,包括KVManager對象實例的創(chuàng)建和KVStore數(shù)據(jù)流轉的使用。
    的頭像 發(fā)表于 04-11 09:57 ?878次閱讀
    <b class='flag-5'>HarmonyOS</b><b class='flag-5'>開發(fā)</b><b class='flag-5'>實例</b>:【<b class='flag-5'>分布式</b>數(shù)據(jù)管理】

    手寫板

    手寫板不錯的資料,值得學習
    發(fā)表于 07-15 21:48

    HarmonyOS應用開發(fā)-分布式任務調度

    1. 介紹本篇CodeLab將實現(xiàn)的內容HarmonyOS是面向全場景多終端的分布式操作系統(tǒng),使得應用程序的開發(fā)打破了智能終端互通的性能和數(shù)據(jù)壁壘,業(yè)務邏輯原子化開發(fā),適配多端。通過一
    發(fā)表于 09-18 09:21

    HarmonyOS應用開發(fā)-分布式設計

    設計理念HarmonyOS 是面向未來全場景智慧生活方式的分布式操作系統(tǒng)。對消費者而言,HarmonyOS 將生活場景中的各類終端進行能力整合,形成“One Super Device”,以實現(xiàn)
    發(fā)表于 09-22 17:11

    HarmonyOS實戰(zhàn)—基于分布式能力,實現(xiàn)多設備同步書寫互動

    ,第三臺書寫筆跡是紅色(每臺設備的畫筆初始化時都會隨機一種顏色),每臺設備筆跡都會同步到連接的設備上顯示。圖3 分布式手寫板演示圖2. 搭建HarmonyOS環(huán)境我們首先需要完成Harmony
    發(fā)表于 09-03 17:14

    HarmonyOS分布式應用框架深入解讀

    設備、分布式的能力及應用,二者具有無限能力。從開發(fā)者角度看,HarmonyOS上基本的組件分為3+1,其中3代表三個Ability,分別是:PageAbility:負責用戶界面的顯示
    發(fā)表于 11-22 15:15

    如何高效完成HarmonyOS分布式應用測試?

    作者:liuxun,HarmonyOS測試架構師HarmonyOS是新一代的智能終端操作系統(tǒng),給開發(fā)者提供了設備發(fā)現(xiàn)、設備連接、跨設備調用等豐富的分布式API。隨著越來越多的
    發(fā)表于 12-13 18:07

    基于OpenHarmony3.1開發(fā)的一個分布式手寫板應用

    1.介紹基于TS擴展的聲明開發(fā)范式開發(fā)一個分布式手寫板應用。涉及的OS特性有分布式拉起和
    發(fā)表于 04-07 11:42

    HarmonyOS應用開發(fā)-EducationSystem分布式親子早教系統(tǒng)體驗

    HarmonyOS應用程序開發(fā),多屏協(xié)作交互和分布式跨設備傳輸?shù)慕涷灐?? 從項目創(chuàng)建、代碼編寫到編譯、構造、部署和操作。二、效果圖:完整代碼地址:https://gitee.com/jltfcloudcn/jump_to/tr
    發(fā)表于 07-25 10:23

    HarmonyOS應用開發(fā)-分布式語音攝像頭體驗

    一、組件說明使用HarmonyOS分布式文件系統(tǒng)和AI語音識別功能開發(fā)了一個分布式語音攝像頭。使用此相機應用程序,同一分布式網(wǎng)絡下的不同設備
    發(fā)表于 08-24 15:06

    手寫板購買指南

    手寫板購買指南 手寫板簡介 如何
    發(fā)表于 07-28 08:21 ?1543次閱讀

    如何選購手寫板

    如何選購手寫板 家用或繪畫愛好者使用的手寫板價格從不足700元至1750元不等,而專業(yè)手寫板的價格則在1400元左右至5250元之間乃至更高。帶有筆感應數(shù)位屏的產品價格區(qū)
    發(fā)表于 07-28 08:22 ?1529次閱讀

    手寫板感應方式

    手寫板感應方式            &
    發(fā)表于 12-28 13:42 ?3424次閱讀

    HarmonyOS測試技術與實戰(zhàn)-HarmonyOS分布式應用特征與挑戰(zhàn)

     HDC 2021華為開發(fā)者大會HarmonyOS測試技術與實戰(zhàn)-HarmonyOS分布式應用特征與挑戰(zhàn)
    的頭像 發(fā)表于 10-23 14:41 ?1644次閱讀
    <b class='flag-5'>HarmonyOS</b>測試技術與實戰(zhàn)-<b class='flag-5'>HarmonyOS</b><b class='flag-5'>分布式</b>應用特征與挑戰(zhàn)

    HarmonyOS分布式應用上架問題分析

    HarmonyOS是新一代的智能終端操作系統(tǒng),給開發(fā)者提供了設備發(fā)現(xiàn)、設備連接、跨設備調用等豐富的分布式API。隨著越來越多的開發(fā)者投入到Harmo
    的頭像 發(fā)表于 12-24 17:56 ?1869次閱讀
    <b class='flag-5'>HarmonyOS</b><b class='flag-5'>分布式</b>應用上架問題分析