如何实现跨设备的双向连接? Labo 涂鸦鸿蒙亲子版分布式开发技术分享
近期,首届 HarmonyOS 开发者创新大赛正式落下帷幕。大赛共历时 5 个月,超过 3000 支队伍的 10000 多名选手参赛,25000 多位开发者参与了大赛学习,最终 23 支参赛队伍斩获奖项,产出了多款有创新、有创意、有价值的优秀作品。其中由“Labo Lado 儿童艺术创想”团队打造的《Labo 涂鸦鸿蒙亲子版》就是其中之一,其创造性地通过 HarmonyOS 分布式技术,实现了多设备下的亲子互动涂鸦功能,最终摘得大赛一等奖。
在很早以前,“Labo Lado 儿童艺术创想”团队就做过一款涂鸦游戏的应用,该应用可以让孩子和父母在一个平板或者手机上进行绘画比赛,比赛的方式就是屏幕一分为二,两人各在设备的一边进行涂鸦。这种方式虽然有趣,但是对于绘画而言,屏幕尺寸限制了用户的发挥和操作。因此团队希望这类玩法能通过多个设备完成,于是他们研究了 ZeroConf、iOS 的 Multipeer Connectivity、Google Nearby 等近距离互联的技术, 结果发现这些技术在设备发现和应用拉起方面实现的都不理想,尤其是当目标用户是儿童的情况下,操作起来不够简便也不易上手。
HarmonyOS 的出现给团队带来了希望。他们发现 HarmonyOS 的分布式技术有着很大的应用潜力,这项技术让设备的发现和应用拉起变的非常的简单自然,互联的过程也很流畅,很好地解决了单机操作的限制,让跨设备联机功能能够非常容易地实现。此外,HarmonyOS 的开发也给团队留下了很深刻的印象,以往繁琐的开发步骤,在 HarmonyOS 中仅需几个配置、几行代码即可完成,无需花费太多精力。在《Labo 涂鸦鸿蒙亲子版》里面的 5 个分布式玩法的开发只用了团队一名开发者不到两个月的时间,其中还包括了学习上手、解决文档不全和各种疑难问题的过程。
以下是“Labo Lado 儿童艺术创想”团队基于 HarmonyOS 的分布式开发关键技术的简单分享:
一、分布式技术实践
HarmonyOS 的分布式能力是在系统层面实现的,在设备双方同属一个局域网的情况下,设备都可以快速的发现和进行流畅的通讯。下面将从 HarmonyOS 设备的发现、应用的拉起、应用通讯和双向通讯几个部分来进行分享。
1、设备的发现
假设设备 A 想要邀请另外一个设备 B 加入,AB 任何一方都无需启动特别的广播服务,只要发起方设备 A 在应用内调用设备发现代码,就可以列出附近符合条件可用的的设备。
以下是获取设备列表的示例代码:
public static List<DeviceInfo> getRemoteDevice() {
List<DeviceInfo> deviceInfoList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);
return deviceInfoList;
}
列出设备之后,用户就可以通过设备名选择想要邀请的设备了。
(左侧设备 A 发现右侧名为“ye”的设备 B 的界面展示)
2、应用的拉起
设备 A 邀请了设备 B 之后,如果设备 B 上应用没启动,设备 A 可直接通过调用 startAbility 方法来拉起设备 B 上的应用。双方应用都启动了之后,就可以进行 RPC 通讯了。如果需要事先检查设备 B 上的应用是否已经启动或者是否在后台,可通过在应用中增加一个 PA 来实现。在拉起之前,设备 A 先连接设备 B 的应用中的 PA 可以实现更复杂精准的远程应用启动控制。
3、应用通讯
在应用中启动一个 PA,专门用作通讯的服务器端。当设备 B 的应用被拉起之后,设备 A 就会通过 connectAbility 与设备 B 的 PA 进行连接,通讯采用 RPC 方式实现,并使用 IDL 定义通讯接口。
4、双向通讯
RPC 的通讯方式使用简单,但是只能支持单向通讯。为了实现双向通讯,可在设备 A 与设备 B 发起建立连接成功之后,再让设备 B 与设备 A 发起建立一个连接,用两个连接实现了双向通讯。下面是这两个连接建立过程的示意时序图:
在设备 A 与设备 B 建立连接的时候,设备 A 必须将自己的 DeviceId 发送给设备 B,然后设备 B 才可以主动发起一个与设备 A 的连接,获取当前设备的 DeviceId 方法如下:
KvManagerFactory.getInstance().createKvManager(new KvManagerConfig(this)).getLocalDeviceInfo().getId()
应用中,FA 主要实现了界面层逻辑,PA 部分用做数据通讯的服务端。为了防止拉起应用导致用户当前面的操作被中断,可通过 PA 来查询当前 FA 的状态,如果 FA 已经启动了,就跳过拉起,直接进行下一步操作即可。
二、数据接口与数据结构定义
使用了 IDL 定义了两个通用的接口,分别用来进行异步和同步调用:
int sendSyncCommand([in] int command, [in] String params);
void sendAsyncCommand([in] int command, [in] String params, [in] byte[] content);
大部分情况下,远程调用大部分都通过同步的方式进行,用户之间的绘画数据通过异步接口传输,数据在用户绘制的时候采集,每 50ms 左右发送一次,这个频率可以大概保证用户视觉上没有卡顿,而又不至于因为接口过度调用导致卡顿或者耗电量过大。采集的绘画数据的数据结构大致如下:
enum action //动作,表示落笔、移动、提笔等动作
int tagId //多点触摸识别标记
int x //x 坐标
int y //y 坐标
enum brushType //笔刷类型
int brushSize //笔刷大小
enum brushColor //笔刷颜色
int layer //图层
这款应用是支持多点触摸的,所以每个触摸点在落笔的的时候,都使用了 tagId 进行标记。这些数据除了通讯外,还会完整地保存在文件中,这样用户就可以通过应用内的播放功能播放该数据,回看绘画的整个过程。
三、教程录制与曲线平滑
1、教程制作
这款产品的特色之一是教程是动态的,用户可以自己拼装或者通过游戏生成教程角色。目前应用内置六种教程。这些教程预先由设计师在 photoshop 中画好并标记各个部位,然后再通过专门的 photoshop 脚本导出到教程录制应用中,再由设计师按部位逐个进行临摹绘制,绘制完成,应用会将设计师的绘制过程数据保存为 json 文件,通过将这些 json 的文件里的部位互换,我们就实现了用户自己拼装教程的功能了。
2、曲线平滑
绘制过程,为了让用户绘制的曲线更加平滑,采用二次贝塞尔曲线算法进行差值(Quadratic Bezier Curve),该算法简单效率也非常不错:
public Point quadraticBezier(Point p0, Point p1, Point p2, float t) {
Point pFinal = new Point();
pFinal.x = (float) (Math.pow(1 - t, 2) * p0.x + (1 - t) * 2 * t * p1.x + t * t * p2.x);
pFinal.y = (float) (Math.pow(1 - t, 2) * p0.y + (1 - t) * 2 * t * p1.y + t * t * p2.y);
return pFinal;
}
基于 HarmonyOS 的分布式特性,《Labo 涂鸦鸿蒙亲子版》完成了一次已有应用的自我尝试和突破,大大的增加了用户在使用过程中的乐趣,为用户带来了全新的跨设备亲子交互体验,“Labo Lado 儿童艺术创想”团队在未来将与更多的 HarmonyOS 开发者一起,为用户创作出更多更有趣的儿童创造类应用。
近一段时间以来,HarmonyOS 2 的发布吸引了广大开发者的关注。作为一款面向万物互联时代的智能终端操作系统,HarmonyOS 2 带来了诸多新特性、新功能和新玩法,等待开发者去探索、去学习、去实践。也欢迎广大开发者继续发挥创造力和想象力,基于 HarmonyOS 开发出更多有创新、有创意、有价值的作品,打造出专属于万物互联时代的创新产品。
评论