重点撸代码:


1、跨设备启动FA、跨设备迁移、回迁

(1)权限

ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE:用于允许监听分布式组网内的设备状态变化。ohos.permission.GET_DISTRIBUTED_DEVICE_INFO:用于允许获取分布式组网内的设备列表和设备信息。ohos.permission.GET_BUNDLE_INFO:用于查询其他应用的信息。ohos.permission.DISTRIBUTED_DATASYNC:用于允许不同设备间的数据交换。"reqPermissions": [    {"name": "ohos.permission.DISTRIBUTED_DATASYNC"},    {"name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE"},     {"name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO" },    {"name": "ohos.permission.GET_BUNDLE_INFO"} ]//主动申明,要多设备协同,让用户选择允许还是禁止requestPermissionsFromUser(new String[]{"ohos.permission.DISTRIBUTED_DATASYNC"}, 0);

 

(2)界面:ability_main.xml

<?xml version="1.0" encoding="utf-8"?><DirectionalLayout    xmlns:ohos="http://schemas.huawei.com/res/ohos"    ohos:height="match_parent"    ohos:width="match_parent"    ohos:orientation="vertical">    <Button        ohos:id="$+id:main_start_fa_btn"        ohos:height="match_content"        ohos:width="300vp"        ohos:text="1.启动远程设备的FA"        ohos:text_size="20fp"        ohos:text_color="#ffffff"        ohos:background_element="$graphic:button_bg"        ohos:layout_alignment="horizontal_center"        ohos:top_padding="8vp"        ohos:bottom_padding="8vp"        ohos:left_padding="40vp"        ohos:right_padding="40vp"        ohos:top_margin="20vp"        />    <Button        ohos:id="$+id:main_migration_btn"        ohos:height="match_content"        ohos:width="300vp"        ohos:text="2.迁移到远程设备"        ohos:text_size="20fp"        ohos:text_color="#ffffff"        ohos:background_element="$graphic:button_bg"        ohos:layout_alignment="horizontal_center"        ohos:top_padding="8vp"        ohos:bottom_padding="8vp"        ohos:left_padding="40vp"        ohos:right_padding="40vp"        ohos:top_margin="20vp"        />    </DirectionalLayout>

 

button_bg.xml

<?xml version="1.0" encoding="utf-8"?><shape  xmlns:ohos="http://schemas.huawei.com/res/ohos"        ohos:shape="rectangle">    <solid ohos:color="#007DFF"/>    <corners ohos:radius="40"/></shape>

 

另外我们需要的Page Abiltiy:MigrationAbility、RemoveAbility
 
MainAbilitySlice:

public class MainAbilitySlice extends AbilitySlice {   private Button mainStartFABtn,mainMigrationBtn;    @Override    public void onStart(Intent intent) {        super.onStart(intent);        super.setUIContent(ResourceTable.Layout_ability_main);        mainStartFABtn = (Button)findComponentById(ResourceTable.Id_main_start_fa_btn);        mainMigrationBtn = (Button)findComponentById(ResourceTable.Id_main_migration_btn);                mainStartFABtn.setClickedListener(mClickListener);        mainMigrationBtn.setClickedListener(mClickListener);    }    private Component.ClickedListener mClickListener = new Component.ClickedListener() {        @Override        public void onClick(Component component) {            int compoentId = component.getId();            switch (compoentId){                case ResourceTable.Id_main_start_fa_btn:                    //点击后跨设备打开Fa                    //第一种写法                    Intent intent = new Intent();                    Operation op = new Intent.OperationBuilder()                            .withDeviceId(Common.getOnLineDeviceId())                            .withBundleName("com.ybzy.demo")                            .withAbilityName("com.ybzy.demo.RemoveAbility")                            .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)                            .build();                    intent.setOperation(op);                    intent.setParam("msg","我夸设备把你这个FA拉起来了!");                    startAbility(intent);                    //第二钟写法                    intent.setElement(new ElementName(Common.getOnLineDeviceId()                                            ,"com.ybzy.demo","com.ybzy.demo.RemoveAbility"));                    intent.setFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE);                    intent.setParam("msg","我夸设备把你这个FA拉起来了!");                    startAbility(intent);                    break;                case ResourceTable.Id_main_migration_btn:                    //点击后进入要迁移的Ability页面                    Intent migrationIntent = new Intent();                    migrationIntent.setElement(new ElementName("","com.ybzy.demo"                                                    ,"com.ybzy.demo.MigrationAbility"));                    startAbility(migrationIntent);                    break;                default:                    break;            }        }    };    @Override    public void onActive() {        super.onActive();    }    @Override    public void onForeground(Intent intent) {        super.onForeground(intent);    }}

 

ability_migration.xml

<?xml version="1.0" encoding="utf-8"?><DirectionalLayout    xmlns:ohos="http://schemas.huawei.com/res/ohos"    ohos:height="match_parent"    ohos:width="match_parent"    ohos:background_element="#00ffff"    ohos:orientation="vertical">    <Text        ohos:id="$+id:migration_text"        ohos:height="match_content"        ohos:width="250vp"        ohos:background_element="#0088bb"        ohos:layout_alignment="horizontal_center"        ohos:text="下面是一个可编辑的文本框"        ohos:text_size="50"        ohos:padding="5vp"        ohos:top_margin="30vp"        />    <TextField        ohos:id="$+id:migration_textfield"        ohos:height="250vp"        ohos:width="250vp"        ohos:hint="请输入..."        ohos:layout_alignment="horizontal_center"        ohos:background_element="#ffffff"        ohos:text_color="#888888"        ohos:text_size="20fp"        ohos:padding="5vp"        />    <Button        ohos:id="$+id:migration_migration_btn"        ohos:height="match_content"        ohos:width="match_content"        ohos:text="点击迁移"        ohos:text_size="20fp"        ohos:text_color="#ffffff"        ohos:background_element="$graphic:button_bg"        ohos:top_padding="8vp"        ohos:bottom_padding="8vp"        ohos:left_padding="50vp"        ohos:right_padding="50vp"        ohos:layout_alignment="horizontal_center"        ohos:top_margin="30vp"        />    <Button        ohos:id="$+id:migration_migration_back_btn"        ohos:height="match_content"        ohos:width="match_content"        ohos:text="点击迁移回来"        ohos:text_size="20fp"        ohos:text_color="#ffffff"        ohos:background_element="$graphic:button_bg"        ohos:top_padding="8vp"        ohos:bottom_padding="8vp"        ohos:left_padding="50vp"        ohos:right_padding="50vp"        ohos:layout_alignment="horizontal_center"        ohos:top_margin="30vp"        /></DirectionalLayout>

 

RemoveAbility...把接收到的值显示到页面就行,setText()

 

(3)工具类

public class Common{    public static String getDeviceId(){        List<DeviceInfo> deviceList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);        if(deviceList.isEmpty()){            return null;        }        int deviceNum = deviceList.size();        List<String> deviceIds = new ArrayList<>(deviceNum);        List<String> deviceNames = new ArrayList<>(deviceNum);        deviceList.forEach((device)->{            deviceIds.add(device.getDeviceId());            deviceNames.add(device.getDeviceName());        });        //我们这里的实验环境,就两部手机,组件还没讲        //我就直接使用deviceIds的第一个元素,做为启动远程设备的目标id        String devcieIdStr = deviceIds.get(0);        return devcieIdStr;    }    public static void myShowTip(Context context,String msg){        //提示框的核心组件文本        Text text = new Text(context);        text.setWidth(MATCH_CONTENT);        text.setHeight(MATCH_CONTENT);        text.setTextSize(16, Text.TextSizeType.FP);        text.setText(msg);        text.setPadding(30,20,30,20);        text.setMultipleLine(true);        text.setMarginLeft(30);        text.setMarginRight(30);        text.setTextColor(Color.WHITE);        text.setTextAlignment(TextAlignment.CENTER);            //给上面的文本设置一个背景样式        ShapeElement style = new ShapeElement();        style.setShape(ShapeElement.RECTANGLE);        style.setRgbColor(new RgbColor(77,77,77));        style.setCornerRadius(15);        text.setBackground(style);            //构建存放上面的text的布局        DirectionalLayout mainLayout = new DirectionalLayout(context);        mainLayout.setWidth(MATCH_PARENT);        mainLayout.setHeight(MATCH_CONTENT);        mainLayout.setAlignment(LayoutAlignment.CENTER);        mainLayout.addComponent(text);            //最后要让上面的组件绑定dialog        ToastDialog toastDialog = new ToastDialog(context);        toastDialog.setSize(MATCH_PARENT,MATCH_CONTENT);        toastDialog.setDuration(1500);        toastDialog.setAutoClosable(true);        toastDialog.setTransparent(true);        toastDialog.setAlignment(LayoutAlignment.CENTER);        toastDialog.setComponent((Component) mainLayout);        toastDialog.show();    }    }

 

(4)实现功能
MigrationAbilitySlice:

public class MigrationAbilitySlice extends AbilitySlice implements IAbilityContinuation {    TextField migrationTextField;    Button migrationMigrationBtn,migrationMigrationBackBtn;    String msg = "";    @Override    public void onStart(Intent intent) {        super.onStart(intent);        super.setUIContent(ResourceTable.Layout_ability_migration);        migrationTextField = (TextField)findComponentById(ResourceTable.Id_migration_textfield);        migrationTextField.setText(msg);        migrationMigrationBtn = (Button)findComponentById(ResourceTable.Id_migration_migration_btn);        migrationMigrationBtn.setClickedListener(component -> {            //1、要进行迁移的Ability实现接口 IAbilityContinuation,实现的方法返回值改成true            //2、要进行迁移的Ability下面关联的所有AbilitySlice都要实现接口 IAbilityContinuation,            //  实现的方法上处理数据            //3、进行迁移            String deviceId = Common.getOnLineDeviceId();            if(deviceId != null){//                continueAbility(deviceId);                 continueAbilityReversibly(deviceId);            }        });        migrationMigrationBackBtn = (Button) findComponentById(ResourceTable                                                                    .Id_migration_migration_back_btn);        migrationMigrationBackBtn.setClickedListener(component -> {            reverseContinueAbility();        });    }    @Override    public void onActive() {        super.onActive();    }    @Override    public void onForeground(Intent intent) {        super.onForeground(intent);    }    @Override    public boolean onStartContinuation() {        return true;    }    @Override    public boolean onSaveData(IntentParams intentParams) {        intentParams.setParam("msg",migrationTextField.getText());        return true;    }    @Override    public boolean onRestoreData(IntentParams intentParams) {        msg = intentParams.getParam("msg").toString();//        getUITaskDispatcher().asyncDispatch(() -> {//            migrationTextField.setText(intentParams.getParam("msg").toString());//        });        return true;    }    @Override    public void onCompleteContinuation(int i) {    }}

 

2、跨设备连接Service

启动远程设备Service的代码示例如下:
添加按钮:

<Button    ohos:id="$+id:main_start_removeService_btn"    ohos:height="match_content"    ohos:width="300vp"    ohos:text="远程启动ServiceAbility"    ohos:text_size="20fp"    ohos:text_color="#ffffff"    ohos:background_element="$graphic:button_bg"    ohos:layout_alignment="horizontal_center"    ohos:top_padding="8vp"    ohos:bottom_padding="8vp"    ohos:left_padding="40vp"    ohos:right_padding="40vp"    ohos:top_margin="20vp"    />        <Button    ohos:id="$+id:main_stop_removeService_btn"    ohos:height="match_content"    ohos:width="300vp"    ohos:text="远程关闭ServiceAbility"    ohos:text_size="20fp"    ohos:text_color="#ffffff"    ohos:background_element="$graphic:button_bg"    ohos:layout_alignment="horizontal_center"    ohos:top_padding="8vp"    ohos:bottom_padding="8vp"    ohos:left_padding="40vp"    ohos:right_padding="40vp"    ohos:top_margin="20vp"    />

 

新建RemoteServiceAbility:

public class RemoteServiceAbility extends Ability {    private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "Demo");    private Source sVideoSource;    private Player sPlayer;    @Override    public void onStart(Intent intent) {        HiLog.error(LABEL_LOG, "RmoteServiceAbility::onStart");        super.onStart(intent);        Common.myShowTip(this,"remote onstart");        sPlayer = new Player(RemoteServiceAbility.this);        new PlayerThread().start();    }    class PlayerThread extends Thread {        @Override        public void run() {            try {                File mp3FilePath = getExternalFilesDir(Environment.DIRECTORY_MUSIC);                if (!mp3FilePath.exists()) {                    mp3FilePath.mkdirs();                }                File mp3File = new File(mp3FilePath.getAbsolutePath() + "/" + "bj.mp3");                Resource res = getResourceManager()                        .getRawFileEntry("resources/rawfile/bj.mp3").openRawFile();                byte[] buf = new byte[4096];                int count = 0;                FileOutputStream fos = new FileOutputStream(mp3File);                while ((count = res.read(buf)) != -1) {                    fos.write(buf, 0, count);                }                FileDescriptor fileDescriptor = new FileInputStream(mp3File).getFD();                sVideoSource = new Source(fileDescriptor);                sPlayer.setSource(sVideoSource);                sPlayer.prepare();                sPlayer.setVolume(0.3f);                sPlayer.enableSingleLooping(true);                sPlayer.play();            } catch (IOException e) {                e.printStackTrace();            }        }    }    @Override    public void onStop() {        super.onStop();        Common.myShowTip(RemoteServiceAbility.this,"remote onStop");        sPlayer.stop();    }    @Override    public IRemoteObject onConnect(Intent intent) {        return null;    }    @Override    public void onDisconnect(Intent intent) {    }}

 

启动:

case ResourceTable.Id_main_start_remoteService_btn:    Common.myShowTip(MainAbilitySlice.this,deviceId);    Intent startRemoteServiceIntent = new Intent();    startRemoteServiceIntent.setElement(new ElementName(            deviceId,            "com.ybzy.demo",            "com.ybzy.demo.RemoteServiceAbility"    ));    startRemoteServiceIntent.setFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE);    startAbility(startRemoteServiceIntent);    break;

 

关闭远程设备Service:

case ResourceTable.Id_main_stop_remoteService_btn:    Common.myShowTip(MainAbilitySlice.this,deviceId);    Intent stopRemoteServiceIntent = new Intent();    stopRemoteServiceIntent.setElement(new ElementName(            deviceId,            "com.ybzy.demo",            "com.ybzy.demo.RemoteServiceAbility"    ));    stopRemoteServiceIntent.setFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE);    stopAbility(stopRemoteServiceIntent);    break;

 

仅通过启动和停止Service Ability两种方式对Service进行调度无法应对需长期交互的场景,
简单地说,信息就是只能去,回不来!
因此,分布式任务调度平台向开发者提供了跨设备Service连接及断开连接的能力。
链接上了,信息可去可回!
 
链接是使用connectAbility()方法,需要传入目标Service的Intent与接口IAbilityConnection的实例对象。
 
接口IAbilityConnection提供了两个方法供开发者实现:
(1)onAbilityConnectDone()用来处理连接的回调。
(2)onAbilityDisconnectDone()用来处理断开连接的回调。
 
我们可以在onAbilityConnectDone()中获取管理链接的代理,进一步为了使用该代理跨设备调度Service,
开发者需要在本地及远端分别实现对外接口一致的代理,这个接口是IRemoteBroker。
 
添加按钮:

<Button    ohos:id="$+id:main_connect_remoteService_btn"    ohos:height="match_content"    ohos:width="300vp"    ohos:text="远程链接ServiceAbility"    ohos:text_size="20fp"    ohos:text_color="#ffffff"    ohos:background_element="$graphic:button_bg"    ohos:layout_alignment="horizontal_center"    ohos:top_padding="8vp"    ohos:bottom_padding="8vp"    ohos:left_padding="40vp"    ohos:right_padding="40vp"    ohos:top_margin="20vp"    /><Button    ohos:id="$+id:main_use_remoteService_btn"    ohos:height="match_content"    ohos:width="300vp"    ohos:text="使用远程ServiceAbility"    ohos:text_size="20fp"    ohos:text_color="#ffffff"    ohos:background_element="$graphic:button_bg"    ohos:layout_alignment="horizontal_center"    ohos:top_padding="8vp"    ohos:bottom_padding="8vp"    ohos:left_padding="40vp"    ohos:right_padding="40vp"    ohos:top_margin="20vp"    /><Button    ohos:id="$+id:main_disconnect_remoteService_btn"    ohos:height="match_content"    ohos:width="300vp"    ohos:text="远程断开ServiceAbility"    ohos:text_size="20fp"    ohos:text_color="#ffffff"    ohos:background_element="$graphic:button_bg"    ohos:layout_alignment="horizontal_center"    ohos:top_padding="8vp"    ohos:bottom_padding="8vp"    ohos:left_padding="40vp"    ohos:right_padding="40vp"    ohos:top_margin="20vp"    />

 

发起连接的本地侧的代理示例如下:

public class MyRemoteProxy implements IRemoteBroker {    //IRemoteBroker:获取远程代理对象的持有者    private static final int ERR_OK = 0;    //COMMAND_PLUS表示有效消息进行通信的约定的标记,MIN_TRANSACTION_ID是这个标记可以用的最小值:1    private static final int COMMAND_PLUS = IRemoteObject.MIN_TRANSACTION_ID;    //IRemoteObject:此接口    // 可用于查询或获取接口描述符、    // 添加或删除死亡通知、    // 将对象状态转储到特定文件以及发送消息。    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:这个类提供了读写对象、接口标记、文件描述符和大数据的方法。        MessageParcel data = MessageParcel.obtain();        //obtain()创建索引为0的空MessageParcel对象        MessageParcel reply = MessageParcel.obtain();        //MessageOption:定义与sendRequest一起发送消息的选项。        // option不同的取值,决定采用同步或异步方式跨设备调用Service        // 这个例子我们需要同步获取对端Service执行加法运算后的结果,同步模式调用sendRequest接口,即MessageOption.TF_SYNC        // 对应的是异步:TF_ASYNC        MessageOption option = new MessageOption(MessageOption.TF_SYNC);        data.writeInt(a);        data.writeInt(b);        try {            remote.sendRequest(COMMAND_PLUS, data, reply, option);            //第1个参数:约定通信双方确定的消息标记。            //第2个参数:发送到对等端侧的数据包裹MessageParcel对象。            //第3个参数:对等端侧返回的数据包裹MessageParcel对象。            //第4个参数:设置发送消息,用同步还是异步模式。            int ec = reply.readInt(); //返回通信成不成功,约定的标记ERR_OK            if (ec != ERR_OK) {                throw new RemoteException();            }            int result = reply.readInt();            return result;        } catch (RemoteException e) {            throw new RemoteException();        } finally {            data.reclaim(); //reclaim()清除不再使用的MessageParcel对象。            reply.reclaim();        }    }}

 

等待连接的远端侧的代理示例如下:

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;     public MyRemote() {        super("MyService_Remote");    }     @Override    public IRemoteObject asObject() {        return this;    }     @Override    public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {        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;    }}

 

等待连接侧还需要作如下修改:

// 绑定前面定义的代理,实例化出发起链接侧需要的代理private MyRemote remote = new MyRemote();@Overridepublic IRemoteObject onConnect(Intent intent) {    //链接成功的时候,给发起链接侧返回去    return remote.asObject();}

 

 

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

private MyRemoteProxy mProxy = null;// 创建连接回调实例private IAbilityConnection conn = new IAbilityConnection() {    // 连接到Service的回调    @Override    public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int resultCode) {        // 在这里开发者可以拿到服务端传过来IRemoteObject对象,从中解析出服务端传过来的信息        mProxy = new MyRemoteProxy(iRemoteObject);        UIUtils.showTip(MainAbilitySlice.this,"拿到remoteObject:" + mProxy);    }    // 意外断开连接才会回调    @Override    public void onAbilityDisconnectDone(ElementName elementName, int resultCode) {    }};// 连接远程case ResourceTable.Id_main_connect_remoteService_btn:    //1、实现连接的本地侧的代理    //2、实现等待连接的远端侧的代理    //3、修改等待连接侧的Service    //4、在本地(发起链接侧)获取远端(被链接侧)返回过来的链接代理,创建链接后的回调函数    //5、实现链接,通过代理对象使用Service的服务    if (deviceId != null) {        Intent connectServiceIntent = new Intent();        connectServiceIntent.setElement(new ElementName(                deviceId,                "com.ybzy.demo",                "com.ybzy.demo.RemoteServiceAbility"        ));        connectServiceIntent.setFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE);        connectAbility(connectServiceIntent, iAbilityConnection);    }    break;        // 链接后使用case ResourceTable.Id_main_use_remoteService_btn:    if (mProxy != null) {        int ret = -1;        try {            ret = mProxy.plus(10, 20);        } catch (RemoteException e) {            e.printStackTrace();        }        Common.myShowTip(MainAbilitySlice.this, "获取的结果:" + ret);    }    break;    // 用完断开case ResourceTable.Id_main_disconnect_remoteService_btn:    disconnectAbility(iAbilityConnection);    break;


想了解更多关于鸿蒙的内容,请访问:

51CTO和华为官方战略合作共建的鸿蒙技术社区

https://harmonyos.51cto.com/#bkwz


©著作权归作者所有:来自51CTO博客作者HarmonyOS技术社区的原创作品,如需转载,请注明出处,否则将追究法律责任

每一份赞赏源于懂得

赞赏

0人进行了赞赏支持

更多相关文章

  1. 闪存连接时显示驱动器可能已损坏要检查并修复驱动器请问如何才能
  2. DoraOS连接Proxmox VE搭建简单桌面云
  3. 软链接于硬连接
  4. 人机融合为什么这么难?
  5. 【Arduino实验室】NB的玩法,远程控制交通信号灯-ESP8266联网
  6. Linux学习--第14周
  7. 一种提升连接Dynamics 365性能的方法
  8. 关于Hirschmann的Classic Platform Switches设备漏洞的情况通报
  9. 详解让无数初学网络的学员郁闷的FTP协议---主动模式

随机推荐

  1. 小鹿 | 谈谈我的三观!
  2. 深度学习中几种常用框架的介绍
  3. 零基础学习Python列表操作
  4. 这么用 if-else,小鹿差点被辞退!
  5. centos 用户权限管理与文件权限设定 详
  6. 60 天呕心沥血,我写完这本电子书!
  7. 鹿哥带读者做外包,它香吗?
  8. TensorFlow 零基础入门指南
  9. 一文搞定pandas的数据合并
  10. 性能优化 | 实战中几点性能优化方案!