小编在前两篇中给大家讲解了鸿蒙应用开发入门,总体感觉与android比较类似,很容易上手,那么鸿蒙HarmonyOS到底跟android有什么不同呢?鸿蒙最大的不同点在于它是为万物互联而出生的,而不仅仅是为了手机而生,最核心优势就是它的分布式开发。那么到底什么是鸿蒙分布式?它有什么不可替代的价值?它又如何开发呢?小编将在本篇为大家一一解密。

首先来看鸿蒙官网一个案例图吧,这个案例讲的是一个用户用手机打车软件叫了网约车,然后把手机装兜里,抬手在手表上可以看车辆应答进展,手机与手表之间的信息是无缝切换的,如同一个设备两个屏幕一样,这就是分布式的最好体现。所以通俗来讲,所谓分布式,就是在不同设备之间实时协同工作,如同一个大设备不同模块一样能紧密配合。这种跨设备协同能力,与传统的android系统不一样,传统android设备之间需要多个app约定好协议才能搜索、发现、配对后联机工作。而harmonyos是直接在系统层支持了多设备间的发现、连接,对应用来说只用调用简单的接口,就可以实现分布式运行在多个设备上进行高效协同。未来科幻电影里的智能家居场景,就需要鸿蒙这样的操作系统才能支撑,这也是鸿蒙能够弯道超车安卓的一大潜在机遇。

解密鸿蒙HarmonyOS核心优势——分布式开发

手机手表协同叫车

好,说了鸿蒙分布式的含义和价值后,我们直接上代码,带领大家来体验一下如何开发分布式应用吧。在New Project时,选择News Feature Ability这个模板,这个模板是一个新闻信息流页面,包含一个新闻列表页和一个新闻详情页。这个demo的Ability如何布局、列表List如何用MVC实现等等,由于在上篇电商demo里已经解析过,这里就不详细说了。我们直接找到最核心的分布式代码。

解密鸿蒙HarmonyOS核心优势——分布式开发

创建demo工程

第一步,先看看config.json文件,这个文件跟android里的mainfest.xml一样,是定义一些ability属性及所需权限的,这里面有三个非常重要的权限,它表示是否允许本app访问和监听其它分布式设备。

{
  "name": "ohos.permission.DISTRIBUTED_DATASYNC"
},
{
  "name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE"
},
{
  "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO"
},

第二步,分布式嘛,肯定是要一个设备连接访问另一个设备,在鸿蒙里,我们可以把本机叫做客户端,把要连接的目标设备叫服务端,那么客户端与服务端是两个独立的进程,如何通信呢?鸿蒙采用了流行的proxy/stub模式,即代理存根模式。通过IDL(接口定义语言)定义设备间通信的接口,然后用工具自动生成对应的proxy和stub文件,其中proxy运行在客户端,stub运行在服务端,他们像是中介一样,连接着客户端和服务端。

解密鸿蒙HarmonyOS核心优势——分布式开发

代理和存根

解密鸿蒙HarmonyOS核心优势——分布式开发

代理框架图

我们从上面截图里可以看到,IDL里只有一个接口,就是transShare(),它有5个参数,从变量名称我们大概可以猜到其含义是设备ID、新闻网址、新闻标题、新闻摘要、新闻封面图。这个IDL接口的目的是可以让客户端设备把一条新闻信息同步给服务端设备。

void tranShare(
        String deviceId,
        String shareUrl,
        String shareTitle,
        String shareAbstract,
        String shareImg)

我们再看一下根据这个IDL文件自动生成的proxy/stub文件代码,以proxy为例,我们可以看到它主要是把新闻各个信息字段通过data.write()函数序列化,然后把序列化的结果通过remote.sendRequest()方法发给服务端,服务端那边会通过stub文件接收这个对象,然后再把它反序列化,从而得到新闻各个信息字段。stub代码这里就不贴出来了。

package com.example.myapplication.manager;

import ohos.rpc.IRemoteObject;
import ohos.rpc.MessageOption;
import ohos.rpc.MessageParcel;
import ohos.rpc.RemoteException;

/**
 * News demo
 */
public class NewsDemoIDLProxy implements INewsDemoIDL {
    private static final String DESCRIPTOR = "com.example.myapplication.INewsDemoIDL";

    private static final int COMMAND_TRAN_SHARE = IRemoteObject.MIN_TRANSACTION_ID;

    private final IRemoteObject remote;

    NewsDemoIDLProxy(IRemoteObject remote) {
        this.remote = remote;
    }

    @Override
    public IRemoteObject asObject() {
        return remote;
    }

    @Override
    public void tranShare(
            String deviceId,
            String shareUrl,
            String shareTitle,
            String shareAbstract,
            String shareImg) throws RemoteException {
        MessageParcel data = MessageParcel.obtain();
        MessageParcel reply = MessageParcel.obtain();
        MessageOption option = new MessageOption(MessageOption.TF_SYNC);

        data.writeInterfaceToken(DESCRIPTOR);
        data.writeString(deviceId);
        data.writeString(shareUrl);
        data.writeString(shareTitle);
        data.writeString(shareAbstract);
        data.writeString(shareImg);

        try {
            remote.sendRequest(COMMAND_TRAN_SHARE, data, reply, option);
            reply.readException();
        } finally {
            data.reclaim();
            reply.reclaim();
        }
    }
}

定义好了代理和存根之后,客户端设备就可以通过代理连接服务端了。那么客户端又该如何发现和选中服务端设备呢?我们先运行一下demo看看界面,首页是一个新闻列表页,点击某条新闻后,是一个新闻详情页面,底部右侧角上有个分享按钮,点了后会弹出一个对话框(Harmony devices),这个对话框本意是要显示发现的其他鸿蒙设备,以便将新闻信息分布式的同步到那些设备上。由于小编这里调试使用的是远程云手机,因此没有办法真正发现其他鸿蒙设备,如果是在家里用真正的手机调试,旁边又有其他鸿蒙设备比如电视的话,那这个对话框里就会显示鸿蒙设备列表的。

解密鸿蒙HarmonyOS核心优势——分布式开发

新闻列表界面

解密鸿蒙HarmonyOS核心优势——分布式开发

新闻详情页

解密鸿蒙HarmonyOS核心优势——分布式开发

点分享按钮弹出的对话框

那到底要怎么发现和连接分布式设备呢?在新闻详情页代码里(MainAbilityDetailSlice.java)里,我们可以看到分享按钮的响应事件处理代码,它调用了两个函数,一个用来获取分布式设备信息,一个用来展示设备列表。

iconShared.setClickedListener(listener -> {
    initDevices();
    showDeviceList();
});

再看看initDevices()是如何做的?通过鸿蒙系统提供的设备管理API DeviceManager直接可以获取分布式设备信息,这里返回的是一个List,因为可连接的分布式设备可能有多个。

private void initDevices() {
    if (devices.size() > 0) {
        devices.clear();
    }
    List<ohos.distributedschedule.interwork.DeviceInfo> deviceInfos =
            DeviceManager.getDeviceList(ohos.distributedschedule.interwork.DeviceInfo.FLAG_GET_ONLINE_DEVICE);
    LogUtil.info("MainAbilityDetailSlice", "deviceInfos size is :" + deviceInfos.size());
    devices.addAll(deviceInfos);
}

然后再把这些设备显示出来,让用户选择需要同步到哪个设备上去分布式运行?这里就比较简单了,把设备List绑定到一个对话框的列表UI里即可。

private void showDeviceList() {
    dialog = new CommonDialog(MainAbilityDetailSlice.this);
    dialog.setAutoClosable(true);
    dialog.setTitleText("Harmony devices");
    dialog.setSize(DIALOG_SIZE_WIDTH, DIALOG_SIZE_HEIGHT);
    ListContainer devicesListContainer = new ListContainer(getContext());
    DevicesListAdapter devicesListAdapter = new DevicesListAdapter(devices, this);
    devicesListContainer.setItemProvider(devicesListAdapter);
    devicesListContainer.setItemClickedListener((listContainer, component, position, listener) -> {
        dialog.destroy();
        startAbilityFA(devices.get(position).getDeviceId());
    });
    devicesListAdapter.notifyDataChanged();
    dialog.setContentCustomComponent(devicesListContainer);
    dialog.show();
}

仔细看看上面代码中,设备列表的点击事件listener,它调用了startAbilityFA()函数,意图在于当用户选择某个设备时,就开始分布式迁移到对应设备上去运行。打开它的代码,可以看到第一步是定义一个intent,里面填入目标设备deviceID,目标设备里要运行的ability(这个案例里是一个服务SharedService)。然后开始调用 connectAbility()函数连接远程设备,在连接成功的回调函数里,就可以拿到远端设备的代理,通过IDL接口就可以像操控本地设备一样,操控远端设备。至此,我们就完成了分布式开发啦。

private void startAbilityFA(String devicesId) {
    Intent intent = new Intent();
    Operation operation = new Intent.OperationBuilder()
            .withDeviceId(devicesId)
            .withBundleName(getBundleName())
            .withAbilityName("com.example.myapplication.SharedService")
            .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
            .build();
    intent.setOperation(operation);
    boolean connectFlag = connectAbility(intent, new IAbilityConnection() {
        @Override
        public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int i) {
            INewsDemoIDL sharedManager = NewsDemoIDLStub.asInterface(iRemoteObject);
            try {
                sharedManager.tranShare(devicesId, "url", "title", "abstract", "image");
            } catch (RemoteException e) {
                LogUtil.info("MainAbilityDetailSlice", "connect successful,but have remote exception");
            }
        }

        @Override
        public void onAbilityDisconnectDone(ElementName elementName, int i) {
            disconnectAbility(this);
        }
    });
    if (connectFlag) {
        DialogUtil.toast(this, connectFlag ? "Sharing succeeded!"
                : "Sharing failed. Please try again later.", WAIT_TIME);
    }
}

作为Android开发者,看完鸿蒙核心优势分布式代码后,我们反过来从安卓里去找类似设计,可以看到与安卓Activity去Bind它的Services逻辑有点像,都是通过connect回调进行操作。但安卓的bind机制只是为了完成跨进程通信,而鸿蒙的分布式机制是为了完成跨设备通信。这一点是鸿蒙胜于安卓之处,鸿蒙这样设计,会让所有的设备都能虚拟为一个大终端,每个设备都可以成为其他设备的一个组件,从而实现软件定义硬件。鸿蒙的核心优势现在你明白了吗?