鸿蒙HarmonyOS开发实战—流转(多端协同 二)_hormonyos 设备同屏开发(1)

53 阅读10分钟

}

为不同功能设置相应的控制按钮。

// 建议开发者按照自己的界面进行按钮设计,示例代码仅供参考 private static final int OFFSET_X = 100; private static final int OFFSET_Y = 100; private static final int ADD_OFFSET_Y = 150; private static final int BUTTON_WIDTH = 800; private static final int BUTTON_HEIGHT = 100; private static final int TEXT_SIZE = 50; private int offsetY = 0;

private Button btnShowDeviceList; private Button btnStartRemote; private Button btnStopRemote; private Button btnConnectRemotePA; private Button btnControlRemotePA; private Button btnDisconnectRemotePA;

private Button createButton(String text, ShapeElement buttonBg) { Button button = new Button(this); button.setContentPosition(OFFSET_X, OFFSET_Y + offsetY); offsetY += ADD_OFFSET_Y; button.setWidth(BUTTON_WIDTH); button.setHeight(BUTTON_HEIGHT); button.setTextSize(TEXT_SIZE); button.setTextColor(Color.YELLOW); button.setText(text); button.setBackground(buttonBg); return button; }

// 按照顺序在PositionLayout中依次添加按钮的示例 private void addComponents(PositionLayout linear, ShapeElement buttonBg) { // 构建显示注册流转任务管理服务的按钮 Button btnRegister = createButton("register", buttonBg); btnRegister.setClickedListener(mRegisterListener); linear.addComponent(btnRegister);

// 构建显示设备列表的按钮 btnShowDeviceList = createButton("ShowDeviceList", buttonBg); btnShowDeviceList.setClickedListener(mShowDeviceListListener); linear.addComponent(btnShowDeviceList);

// 构建远程启动FA/PA的按钮 btnStartRemote = createButton("StartRemote", buttonBg); btnStartRemote.setClickedListener(mStartRemoteListener); linear.addComponent(btnStartRemote);

// 构建远程关闭PA的按钮 btnStopRemote = createButton("StopRemote", buttonBg); btnStopRemote.setClickedListener(mStopRemoteListener); linear.addComponent(btnStopRemote);

// 构建连接远程PA的按钮 btnConnectRemotePA = createButton("ConnectRemotePA", buttonBg); btnConnectRemotePA.setClickedListener(mConnectRemotePAListener); linear.addComponent(btnConnectRemotePA);

// 构建控制连接PA的按钮 btnControlRemotePA = createButton("ControlRemotePA", buttonBg); btnControlRemotePA.setClickedListener(mControlPAListener); linear.addComponent(btnControlRemotePA);

// 构建与远程PA断开连接的按钮 btnDisconnectRemotePA = createButton("DisconnectRemotePA", buttonBg); btnDisconnectRemotePA.setClickedListener(mDisconnectRemotePAListener); linear.addComponent(btnDisconnectRemotePA); }

@Override public void onStart(Intent intent) { ... //添加功能按钮布局 addComponents(layout, buttonBg); super.setUIContent(layout); }

注册流转任务管理服务。

// 注册流转任务管理服务 private Component.ClickedListener mRegisterListener = new Component.ClickedListener() { @Override public void onClick(Component arg0) { HiLog.info(LABEL_LOG, "register call."); //增加过滤条件 ExtraParams params = new ExtraParams(); String[] devTypes = new String[]{ExtraParams.DEVICETYPE_SMART_PAD, ExtraParams.DEVICETYPE_SMART_PHONE}; params.setDevType(devTypes); String jsonParams = "{'filter':{'commonFilter':{'system':{'harmonyVersion':'2.0.0'},'groupType':'1|256','curComType': 0x00030004,'faFilter':'{"localVersionCode":1,"localMinCompatibleVersionCode":2,"targetBundleName": "com.xxx.yyy"}'}},'transferScene':0,'remoteAuthenticationDescription': '拉起HiVision扫描弹框描述','remoteAuthenticationPicture':''}"; params.setJsonParams(jsonParams); continuationRegisterManager.register(BUNDLE_NAME, params, callback, requestCallback); } };

通过流转任务管理服务提供的showDeviceList()接口获取选择设备列表,用户选择设备后在IContinuationDeviceCallback回调中获取设备ID。

// 显示设备列表,获取设备信息 private ClickedListener mShowDeviceListListener = new ClickedListener() { @Override public void onClick(Component arg0) { // 设置过滤设备类型 ExtraParams params = new ExtraParams(); String[] devTypes = new String[]{ExtraParams.DEVICETYPE_SMART_PAD, ExtraParams.DEVICETYPE_SMART_PHONE}; params.setDevType(devTypes); String jsonParams = "{'filter':{'commonFilter':{'system':{'harmonyVersion':'2.0.0'},'groupType':'1|256','curComType': 0x00030004,'faFilter':'{"localVersionCode":1,"localMinCompatibleVersionCode":2,"targetBundleName": "com.xxx.yyy"}'}},'transferScene':0,'remoteAuthenticationDescription': '拉起HiVision扫描弹框描述','remoteAuthenticationPicture':''}"; params.setJsonParams(jsonParams); // 显示选择设备列表 continuationRegisterManager.showDeviceList(abilityToken, params, null); } };

为启动远程FA/PA的按钮设置点击回调,实现启动FA/PA和关闭远程PA的能力。

// 启动远程FA/PA private ClickedListener mStartRemoteListener = new ClickedListener() { @Override public void onClick(Component arg0) { if (selectDeviceId != null) { // 通过showDeviceList获取指定目标设备deviceId // 指定待启动FA/PA的bundleName和abilityName // 设置分布式标记,表明当前涉及分布式能力 Operation operation = new Intent.OperationBuilder() .withDeviceId(selectDeviceId) .withBundleName(REMOTE_BUNDLE_NAME) .withAbilityName(REMOTE_FA_NAME) .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) .build(); Intent startIntent = new Intent(); startIntent.setOperation(operation); // 通过AbilitySlice包含的startAbility接口实现跨设备启动FA/PA startAbility(startIntent); } else { btnStartRemote.setText("StartRemote selectDeviceId is null"); } } };

// 关闭远程PA private ClickedListener mStopRemoteListener = new ClickedListener() { @Override public void onClick(Component arg0) { if (selectDeviceId != null) { // 通过showDeviceList获取指定目标设备deviceId // 指定待关闭PA的bundleName和abilityName // 设置分布式标记,表明当前涉及分布式能力 Operation operation = new Intent.OperationBuilder() .withDeviceId(selectDeviceId) .withBundleName(REMOTE_BUNDLE_NAME) .withAbilityName(REMOTE_PA_NAME) .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) .build(); Intent stopIntent = new Intent(); stopIntent.setOperation(operation); // 通过AbilitySlice包含的stopAbility接口实现跨设备关闭PA stopAbility(stopIntent); } else { btnStopRemote.setText("StopRemote selectDeviceId is null"); } } };

需要注意,目标FA/PA需要在config.json中设置**“visible”**为true。visible标签表示Ability是否可以被其他应用调用,默认为false,即只允许同应用(同appid)访问;如需被其他应用访问,需要将其设置为true,同时建议在目标FA/PA中添加自定义权限,控制访问范围,防止被其他应用随意访问。

在config.json中的配置如下:

{ "module": { "abilities": [ { ... "visible": true ... } ] ... } ... }

设备A连接设备B侧的PA,利用连接关系调用该PA执行特定任务,以及断开连接。

// 当连接完成时,用来提供管理已连接PA的能力 private MyRemoteProxy mProxy = null; // 用于管理连接关系 private IAbilityConnection mConn = new IAbilityConnection() { @Override public void onAbilityConnectDone(ElementName element, IRemoteObject remote, int resultCode) { // 跨设备PA连接完成后,会返回一个序列化的IRemoteObject对象 // 通过该对象得到控制远端服务的代理 mProxy = new MyRemoteProxy(remote); btnConnectRemotePA.setText("connectRemoteAbility done"); }

@Override public void onAbilityDisconnectDone(ElementName element, int resultCode) { // 当已连接的远端PA异常关闭时,会触发该回调 // 支持开发者按照返回的错误信息进行PA生命周期管理 disconnectAbility(mConn); } };

仅通过启动/关闭两种方式对PA进行调度无法应对需长期交互的场景,因此,系统向开发者提供了跨设备PA连接及断开连接的能力。为了对已连接PA进行管理,开发者需要实现一个满足IAbilityConnection接口的连接状态检测实例,通过该实例可以对连接及断开连接完成时设置具体的处理逻辑,例如:获取控制对端PA的代理等。进一步为了使用该代理跨设备调度PA,开发者需要在本地及对端分别实现对外接口一致的代理。一个具备加法能力的代理示例如下:

// 以连接提供加法计算能力的PA为例。为了提供跨设备连接能力,需要在本地发起连接侧和对端被连接侧分别实现代理 // 发起连接侧的代理示例如下: public class MyRemoteProxy implements IRemoteBroker { private static final int ERR_OK = 0; private static final int COMMAND_PLUS = IRemoteObject.MIN_TRANSACTION_ID; private static final String DESCRIPTOR = "com.XXX.DESCRIPTOR"; private final IRemoteObject remote;

public MyRemoteProxy(IRemoteObject remote) { this.remote = remote; }

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

public int plus(int a, int b) throws RemoteException { MessageParcel data = MessageParcel.obtain(); MessageParcel reply = MessageParcel.obtain(); // option不同的取值,决定采用同步或异步方式跨设备控制PA // 本例需要同步获取对端PA执行加法的结果,因此采用同步的方式,即MessageOption.TF_SYNC // 具体MessageOption的设置,可参考相关API文档 MessageOption option = new MessageOption(MessageOption.TF_SYNC); data.writeInterfaceToken(DESCRIPTOR); data.writeInt(a); data.writeInt(b);

try { remote.sendRequest(COMMAND_PLUS, data, reply, option); int errCode = reply.readInt(); if (errCode != ERR_OK) { throw new RemoteException(); } int result = reply.readInt(); return result; } finally { data.reclaim(); reply.reclaim(); } } }

此外,对端待连接的PA需要实现对应的客户端,代码示例如下所示:

// 以计算加法为例,对端实现的客户端如下 public class MyRemote extends RemoteObject implements IRemoteBroker{ private static final int ERR_OK = 0; private static final int ERROR = -1; private static final int COMMAND_PLUS = IRemoteObject.MIN_TRANSACTION_ID; private static final String DESCRIPTOR = "com.XXX.DESCRIPTOR";

public MyRemote() { super("MyService_Remote"); }

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

@Override public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) { String token = data.readInterfaceToken(); if (!DESCRIPTOR.equals(token)) { reply.writeInt(ERROR); return false; }

if (code != COMMAND_PLUS) { reply.writeInt(ERROR); return false; }

int value1 = data.readInt(); int value2 = data.readInt(); int sum = value1 + value2; reply.writeInt(ERR_OK); reply.writeInt(sum); return true; } }

对端除了要实现如上所述的客户端外,待连接的PA还需要作如下修改:

// 为了返回给连接方可调用的代理,需要在该PA中实例化客户端,例如作为该PA的成员变量 private MyRemote remote = new MyRemote(); // 当该PA接收到连接请求时,即将该客户端转化为代理返回给连接发起侧 @Override protected IRemoteObject onConnect(Intent intent) { super.onConnect(intent); return remote.asObject(); }

创建远程连接目标PA的步骤可参考创建Service。需要注意,目标PA需要在config.json中设置**“visible”**为true。visible标签表示Ability是否可以被其他应用调用,默认为false,即只允许同应用(同appid)访问;如需被其他应用访问,需要将其设置为true,同时建议在目标PA中添加自定义权限,控制访问范围,防止被其他应用随意访问。

在config.json中的配置如下:

{ "module": { "abilities": [ { ... "visible": true ... } ] ... } ... }

完成上述步骤后,可以通过点击事件实现连接、利用连接关系控制PA以及断开连接等行为,代码示例如下:

// 连接远程PA private ClickedListener mConnectRemotePAListener = new ClickedListener() { @Override public void onClick(Component arg0) { if (selectDeviceId != null) { // 指定待连接PA的bundleName和abilityName // 设置分布式标记,表明当前涉及分布式能力 Operation operation = new Intent.OperationBuilder() .withDeviceId(selectDeviceId) .withBundleName(REMOTE_BUNDLE_NAME) .withAbilityName(REMOTE_PA_NAME) .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) .build(); Intent connectPAIntent = new Intent(); connectPAIntent.setOperation(operation); // 通过AbilitySlice包含的connectAbility接口实现跨设备连接PA connectAbility(connectPAIntent, mConn); } } };

// 控制已连接PA执行加法 private ClickedListener mControlPAListener = new ClickedListener() { @Override public void onClick(Component arg0) { if (mProxy != null) { int ret = -1; try { ret = mProxy.plus(10, 20); } catch (RemoteException e) { HiLog.error(LABEL_LOG, "ControlRemotePA error"); } btnControlRemotePA.setText("ControlRemotePA result = " + ret); } } };

// 与远程PA断开连接 private ClickedListener mDisconnectRemotePAListener = new ClickedListener() { @Override public void onClick(Component arg0) { // 按钮复位 btnConnectRemotePA.setText("ConnectRemotePA"); btnControlRemotePA.setText("ControlRemotePA"); disconnectAbility(mConn); } };

说明 通过连接/断开连接远程PA,与跨设备PA建立长期的管理关系。例如在本例中,通过连接关系得到远程PA的控制代理后,实现跨设备计算加法并将结果返回到本地显示。在实际开发中,开发者可以根据需要实现多种分布式场景,例如:跨设备位置/电量等信息的采集、跨设备计算资源互助等。


最后,为了能让大家更好的去学习提升鸿蒙 (Harmony OS) 开发技术,小编连夜整理了一份30个G纯血版学习资料(含视频电子书学习文档等)以及一份在Github上持续爆火霸榜的《纯血版华为鸿蒙 (Harmony OS)开发手册》(共计890页),希望对大家有所帮助。

纯血版鸿蒙 HarmonyOS 4.0 视频学习资料

需要以上视频学习资料小伙伴

请点击→纯血版全套鸿蒙HarmonyOS学习资料


《纯血版华为鸿蒙 (Harmony OS)开发手册》

这份手册涵盖了当前鸿蒙 (Harmony OS) 开发技术必掌握的核心知识点

纯血版鸿蒙 (Harmony OS)开发手册部分精彩内容

HarmonyOS 概念:

  • 系统定义
  • 技术架构
  • 技术特性
  • 系统安全

如何快速入门?

  • 基本概念
  • 构建第一个ArkTS应用
  • 构建第一个JS应用
  • ……


开发基础知识: 

  • 应用基础知识
  • 配置文件
  • 应用数据管理
  • 应用安全管理
  • 应用隐私保护
  • 三方应用调用管控机制
  • 资源分类与访问
  • 学习ArkTS语言
  • ……

基于ArkTS 开发:

  • Ability开发
  • UI开发
  • 公共事件与通知
  • 窗口管理
  • 媒体
  • 安全
  • 网络与链接
  • 电话服务
  • 数据管理
  • 后台任务(Background Task)管理
  • 设备管理
  • 设备使用信息统计
  • DFX
  • 国际化开发
  • 折叠屏系列
  • .……

获取以上文中提到的这份纯血版鸿蒙 (Harmony OS) 开发资料的小伙伴 

请点击→纯血版全套鸿蒙HarmonyOS学习资料


🚀写在最后

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

img img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以戳这里获取