如何实现手机向智能手表发送消息
===============

#### 问题现象

手机与智能穿戴设备的互联通信是智能穿戴设备的核心开发场景，如何实现手机向智能手表发送消息？

#### 背景知识

*   Wear Engine聚焦华为穿戴设备能力开放，目前提供了应用间消息通信（设备基础能力）等能力，参考：[开放能力](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/we-business_introduction#zh-cn_topic_0000001073786836_section0241143121313)。
*   接入前需要[申请接入Wear Engine服务](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/wearengine_apply)和[配置Client ID](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/configuration_client_id)。

#### 解决方案

1.  手机端应用使用WearEngine的[sendMessage](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/wearengine_api#section10826949193413)接口向智能手表端应用发送消息通知，需配置智能手表端应用的包名与指纹，代码示例如下：
    
    import { wearEngine } from '@kit.WearEngine';
    import { BusinessError } from '@kit.BasicServicesKit';
    import { util } from '@kit.ArkTS';
    import { promptAction } from '@kit.ArkUI';
    
    @Entry
    @Component
    struct SendMessageForPhone {
      build() {
        Column() {
          Button('发送消息')
            .onClick(() => {
             _ // 获取DeviceClient对象_
              try {
                let deviceClient: wearEngine.DeviceClient =
                  wearEngine.getDeviceClient(this.getUIContext().getHostContext());
                _// 创建一个设备列表用于存储返回的设备_
                let deviceList: wearEngine.Device\[\] = \[\];
    
               _ // 调用getConnectedDevices方法，查询用户是否有已连接的穿戴设备_
                deviceClient.getConnectedDevices().then(devices => {
                  _// 处理返回的设备列表_
                  deviceList = devices;
                  console.info(\`Succeeded in getting deviceList, deviceList number is ${deviceList.length}\`);
    
                  if (deviceList.length > 0) {
                   _ // 从得到的设备列表中选取目标设备，并定义为device(假设数组中存在已连接设备且第一位即为目标设备)_
                    let targetDevice: wearEngine.Device = deviceList\[0\];
                    console.info(\`Succeeded in getting targetDevice, result is ${targetDevice}\`);
    
                  _  // 构造穿戴设备侧应用参数_
                    let appInfo: wearEngine.AppInfo = {
                     _ // 替换为穿戴设备应用的包名与指纹_
                      bundleName: 'XXXX.XXXX.XXXX',
                      fingerprint: 'XXXXXXXXXXXXXXXXXXX'
                    };
                    let appParam: wearEngine.P2pAppParam = {
                      remoteApp: appInfo
                    };
    
                   _ // 设置需要发送的消息内容，长度限制为4096字节_
                    let messageContent: string = 'this is message from phone';
    
                  _  // 构造消息结构体_
                    let textEncoder: util.TextEncoder = new util.TextEncoder;
                    let message: wearEngine.P2pMessage = {
                      content: textEncoder.encodeInto(messageContent)
                    };
    
                    _// 获取P2pClient对象_
                    let p2pClient: wearEngine.P2pClient = wearEngine.getP2pClient(this.getUIContext().getHostContext());
    
                  _  // 发送消息_
                    p2pClient.sendMessage(targetDevice.randomId, appParam, message).then((p2pResult) => {
                      console.info(\`Succeeded in sending message, result is ${p2pResult.code}.\`);
                      this.getUIContext().getPromptAction().showToast({
                        message: 'Succeeded in sending message',
                        duration: 2000,
                        showMode: promptAction.ToastShowMode.DEFAULT,
                        bottom: 80
                      });
                    }).catch((error: BusinessError) => {
                      console.error(\`Failed to send message. Code is ${error.code}, message is ${error.message}.\`);
                    });
    
                  }
                }).catch((error: BusinessError) => {
                _  // 处理调用失败时捕获到的异常_
                  console.error(\`Failed to get deviceList. Code is ${error.code}, message is ${error.message}\`);
                });
              } catch (error) {
                console.error(\`Failed to get deviceClient. Code is ${error.code}, message is ${error.message}\`);
              }
    
            });
        }.padding(20);
      }
    }
    
2.  智能手表端应用通过[registerMessageReceiver](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/wearengine_api#section893873771918)接口订阅接收手机端应用发来的消息，需配置手机端应用的包名与指纹，并在aboutToDisappear中调用[unregisterMessageReceiver](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/wearengine_api#section13676139154415)方法，取消接收手机端应用发过来的消息，需要传入订阅监听时的同一个回调函数对象。代码示例如下：
    
    _// 在使用Wear Engine服务前，请导入wearEngine与相关模块_
    import { wearEngine } from '@kit.WearEngine';
    import { BusinessError } from '@kit.BasicServicesKit';
    import { util } from '@kit.ArkTS';
    
    @Entry
    @Component
    struct ReceiveMessageforWearable {
      message: string = 'Hello';
      p2pClient?: wearEngine.P2pClient;
      targetDevice?: wearEngine.Device;
      appParam?: wearEngine.P2pAppParam;
      msgCallback = (p2pMessage: wearEngine.P2pMessage) => {
        console.info(\`Succeeded in receiving message, the message is ${p2pMessage.content}.\`);
        let decoder: util.TextDecoder = util.TextDecoder.create();
        if (canIUse('SystemCapability.Health.WearEngine')) {
          let str: string = decoder.decodeToString(p2pMessage.content);
          console.info(\`Succeeded in receiving message, the message is ${str}.\`);
          this.getUIContext().getPromptAction().openToast({
            message: 'message: ' + str,
            duration: 2000
          }).catch(() => {
            console.error('Failed to open Toast');
          });
        }
      };
    
      aboutToAppear(): void {
        try {
         _ // 获取DeviceClient对象_
          let deviceClient: wearEngine.DeviceClient = wearEngine.getDeviceClient(this.getUIContext().getHostContext());
    
        _  // 调用getConnectedDevices方法，查询用户是否有已连接的对端设备_
          deviceClient.getConnectedDevices().then(devices => {
            console.info(\`Succeeded in getting deviceList, deviceList number is ${devices.length}\`);
           _ // 从已连接设备列表中选定需要通信的设备_
            if (devices.length > 0) {
              this.targetDevice = devices\[0\];
              console.info(\`Succeeded to get target device.\`);
    
             _ // 获取P2pClient对象_
              this.p2pClient = wearEngine.getP2pClient(this.getUIContext().getHostContext());
    
             _ // 构造手机侧应用参数_
              let appInfo: wearEngine.AppInfo = {
             _   // 替换为手机侧的包名与指纹_
                bundleName: 'XXXX.XXXX.XXXX',
                fingerprint: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
              };
           _   // 将对端应用参数类定义为appParam_
              this.appParam = {
                remoteApp: appInfo
              };
    
            _  // 订阅监听消息接收事件_
              this.p2pClient.registerMessageReceiver(this.targetDevice.randomId, this.appParam, this.msgCallback)
                .then(() => {
                  console.info(\`Succeeded in registering message receiver.\`);
                })
                .catch((error: BusinessError) => {
                  console.error(\`Failed to register message receiver. Code is ${error.code}, message is ${error.message}.\`);
                });
    
            } else {
              console.warn(\`Failed to get target device. deviceList is empty.\`);
            }
          }).catch((error: BusinessError) => {
           _ // 处理调用失败时捕获到的异常_
            console.error(\`Failed to get deviceList. Code is ${error.code}, message is ${error.message}\`);
          });
        } catch (error) {
          console.error(\`Failed to get deviceClient. Code is ${error.code}, message is ${error.message}\`);
        }
      }
    
      aboutToDisappear(): void {
        _// 取消订阅监听消息接收事件_
        this.p2pClient?.unregisterMessageReceiver(this.targetDevice?.randomId, this.appParam,
          this.msgCallback).catch(() => {
          console.error('Failed to unregister message receiver.');
        });
      }
    
      build() {
        RelativeContainer() {
          Text(this.message)
            .id('HelloWorld')
            .fontSize($r('app.float.page\_text\_font\_size'))
            .fontWeight(FontWeight.Bold)
            .alignRules({
              center: { anchor: '\_\_container\_\_', align: VerticalAlign.Center },
              middle: { anchor: '\_\_container\_\_', align: HorizontalAlign.Center }
            });
        }
        .height('100%')
        .width('100%');
      }
    }
    

#### 常见FAQ

Q：智能手表端应用的fingerprint如何获取？

A：通过命令hdc shell bm dump –n ${BundleName} | findstr appIdentifier获取。

Q：运动表端应用的fingerprint如何获取？

A：参考文档：[获取穿戴设备侧签名证书的指纹信息](https://developer.huawei.com/consumer/cn/doc/connectivity-guides/signature-0000001053969657)。