鴻蒙應用開發(fā)入門(七):實現(xiàn)跨設備遷移
https://harmonyos.51cto.com/#zz
6.2 跨設備遷移
1. 分布式任務調度概述
在HarmonyOS中,分布式任務調度平臺對搭載HarmonyOS的多設備構筑的“超級虛擬終端”提供統(tǒng)一的組件管理能力,為應用定義統(tǒng)一的能力基線、接口形式、數據結構、服務描述語言,屏蔽硬件差異;支持遠程啟動、遠程調用、業(yè)務無縫遷移等分布式任務。
2. 實現(xiàn)調度的約束與限制
1)遠程調用PA/FA,開發(fā)者需要在Intent中設置支持分布式的標記(例如:Intent.FLAG_ABILITYSLICE_MULTI_DEVICE表示該應用支持分布式調度),否則將無法獲得分布式能力。
2)開發(fā)者通過在config.json中的reqPermissions字段里添加權限申請:
(1)以獲取跨設備連接的能力和分布式數據傳輸的權限。
分布式數據傳輸的權限:
- {"name": "ohos.permission.servicebus.ACCESS_SERVICE"}
三方應用使用權限:
- {"name": "ohos.permission.servicebus.DISTRIBUTED_DATASYNC"}
系統(tǒng)應用使用權限:
- {"name": "com.huawei.hwddmp.servicebus.BIND_SERVICE"}
(2)另外還有三個獲取分布式設備信息需要的權限:
- {"name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE"},
- {"name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO" },
- { "name": "ohos.permission.GET_BUNDLE_INFO"}
注意:還需要在開發(fā)的時候,要在Ability里主動聲明,要用到的權限。
3)FA(Feature Ability,Page模板的Ability)的調用支持啟動和遷移行為,在進行調度時:
(1)當啟動FA時,需要開發(fā)者在Intent中指定對端設備的deviceId、bundleName和abilityName。
(2)FA的遷移實現(xiàn)相同bundleName和abilityName的FA跨設備遷移,因此需要指定遷移設備的deviceId。
3. 實現(xiàn)場景介紹
下面以設備A(本地設備)和設備B(遠端設備)為例,介紹下面我們要實現(xiàn)的場景:
1)設備A啟動設備B的FA:在設備A上通過本地應用提供的啟動按鈕,啟動設備B上對應的FA。
2)設備A的FA遷移至設備B:設備A上通過本地應用提供的遷移按鈕,將設備A的業(yè)務無縫遷移到設備B中。
3)設備A的FA遷移至設備B,還可以實現(xiàn)主動撤回遷移。
4. 具體實現(xiàn)前先了解要用的接口
1)啟動遠程FA
startAbility(Intent intent)接口提供啟動指定設備上FA和PA的能力,Intent中指定待啟動FA的設備deviceId、bundleName和abilityName。
2)遷移FA
continueAbility(String deviceId)接口提供將本地FA遷移到指定設備上的能力,continueAbilityReversibly(String deviceId) 接口提供將本地FA遷移到指定設備上的能力,這種遷移可撤回, reverseContinueAbility()接口提供撤回遷移的能力。
5. 實戰(zhàn)遠程啟動FA頁面
1)編程實現(xiàn)上面場景的界面:
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:migration_btn_01"
- 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:migration_btn_02"
- 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"
- />
- <Button
- ohos:id="$+id:migration_btn_03"
- ohos:height="match_content"
- ohos:width="300vp"
- ohos:text="3.可遷回的遷移遠程設備"
- 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"
- />
- </DirectionalLayou
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"/>
- </
MigrationAbility和MigrationBackAbility
- // 調用AbilitySlice模板實現(xiàn)一個用于控制基礎功能的FA
- // Ability和AbilitySlice類均需要實現(xiàn)IAbilityContinuation及其方法,才可以實現(xiàn)FA遷移。AbilitySlice的代碼示例如下
- public class SampleSlice extends AbilitySlice implements IAbilityContinuation {
- @Override
- public void onStart(Intent intent) {
- super.onStart(intent);
- super.setUIContent(layout);
- }
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:text_title"
- 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:textfield_back"
- 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_button"
- 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"
- />
- </DirectionalLayou
ability_migration_back.xml比ability_migration.xml多一個遷回按鈕,另外主頁上點擊按鈕跳轉等,略...
2)使用分布式能力要求開發(fā)者在Ability對應的config.json中聲明多設備協(xié)同訪問的權限:
(1)三方應用部署權限、分布式數據傳輸的權限、系統(tǒng)應用使用權限的申請。
- {
- "reqPermissions": [
- {"name": "ohos.permission.DISTRIBUTED_DATASYNC"},
- {"name": "ohos.permission.servicebus.ACCESS_SERVICE"},
- {"name": "com.huawei.hwddmp.servicebus.BIND_SERVICE"}
- ]
- }
(2)聲明分布式獲取設備列表及設備信息的權限,如下所示:
- {
- "reqPermissions": [
- {"name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE"},
- {"name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO" },
- {"name": "ohos.permission.GET_BUNDLE_INFO"}
- ]
- }
(3)對于三方應用還要求在實現(xiàn)Ability的代碼中顯式聲明需要使用的權限。
- public class SampleSlice extends AbilitySlice implements IAbilityContinuation {
- @Override
- public void onStart(Intent intent) {
- // 開發(fā)者顯示聲明需要使用的權限
- requestPermissionsFromUser(new String[]{"ohos.permission.DISTRIBUTED_DATASYNC",
- "ohos.permission.servicebus.ACCESS_SERVICE",
- "com.huawei.hwddmp.servicebus.BIND_SERVICE"}, 0);
- super.onStart(intent);
- }
- }
3) 為啟動遠程FA的按鈕添加點擊事件,獲取設備信息,實現(xiàn)啟動遠程FA的能力。
- Button btn1 = (Button) findComponentById(ResourceTable.Id_migration_btn_01);
- btn1.setClickedListener(new Component.ClickedListener() {
- @Override
- public void onClick(Component component) {
- // 調用DeviceManager的getDeviceList接口,通過FLAG_GET_ONLINE_DEVICE標記獲得在線設備列表
- List<DeviceInfo> onlineDevices = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);
- // 判斷組網設備是否為空
- if (onlineDevices.isEmpty()) {
- return;
- }
- int numDevices = onlineDevices.size();
- ArrayList<String> deviceIds = new ArrayList<>(numDevices);
- ArrayList<String> deviceNames = new ArrayList<>(numDevices);
- onlineDevices.forEach((device) -> {
- deviceIds.add(device.getDeviceId());
- deviceNames.add(device.getDeviceName());
- });
- // 我們這里只有兩個設備,所以選擇首個設備作為目標設備
- // 開發(fā)者也可按照具體場景,通過別的方式進行設備選擇
- String selectDeviceId = deviceIds.get(0);
- //獲取設備ID,最好放到工具類里,很多地方要用!
- if(selectDeviceId!=null){
- Intent intent2 = new Intent();
- Operation operation = new Intent.OperationBuilder()
- .withDeviceId(selectDeviceId)
- .withBundleName("cn.ybzy.hmsdemo")
- .withAbilityName("cn.ybzy.hmsdemo.RemoteAbility")
- .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
- .build();
- intent2.setOperation(operation);
- // 通過AbilitySlice包含的startAbility接口實現(xiàn)跨設備啟動FA
- startAbility(intent2);
- }
- }
- });
6. 實戰(zhàn)將設備A運行時的FA遷移到設備B,實現(xiàn)業(yè)務在設備間無縫遷移。
MigrationAbility
- public class MigrationAbility extends Ability implements IAbilityContinuation {
- @Override
- public void onStart(Intent intent) {
- super.onStart(intent);
- super.setMainRoute(MigrationAbilitySlice.class.getName());
- }
- @Override
- public boolean onStartContinuation() {
- return true;
- }
- @Override
- public boolean onSaveData(IntentParams intentParams) {
- return true;
- }
- @Override
- public boolean onRestoreData(IntentParams intentParams) {
- return true;
- }
- @Override
- public void onCompleteContinuation(int i) {
- }
- }
MigrationAbilitySlice
- public class MigrationAbilitySlice extends AbilitySlice implements IAbilityContinuation {
- TextField textField;
- String textStr = "請輸入數據...";
- @Override
- public void onStart(Intent intent) {
- super.onStart(intent);
- super.setUIContent(ResourceTable.Layout_ability_migration);
- textField = (TextField)findComponentById(ResourceTable.Id_textfield_migration);
- textField.setText(textStr);
- Button btn = (Button) findComponentById(ResourceTable.Id_migration_button);
- btn.setClickedListener(new Component.ClickedListener() {
- @Override
- public void onClick(Component component) {
- String deviceId = getDeviceId();
- if(deviceId!=null){
- continueAbility(deviceId);
- }
- }
- });
- }
- private String getDeviceId(){
- // 調用DeviceManager的getDeviceList接口,通過FLAG_GET_ONLINE_DEVICE標記獲得在線設備列表
- List<DeviceInfo> onlineDevices = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);
- // 判斷組網設備是否為空
- if (onlineDevices.isEmpty()) {
- return null;
- }
- int numDevices = onlineDevices.size();
- ArrayList<String> deviceIds = new ArrayList<>(numDevices);
- ArrayList<String> deviceNames = new ArrayList<>(numDevices);
- onlineDevices.forEach((device) -> {
- deviceIds.add(device.getDeviceId());
- deviceNames.add(device.getDeviceName());
- });
- // 我們這里只有兩個設備,所以選擇首個設備作為目標設備
- // 開發(fā)者也可按照具體場景,通過別的方式進行設備選擇
- String selectDeviceId = deviceIds.get(0);
- return selectDeviceId;
- }
- @Override
- public boolean onStartContinuation() {
- return true;
- }
- @Override
- public boolean onSaveData(IntentParams intentParams) {
- intentParams.setParam("data",textField.getText());
- return true;
- }
- @Override
- public boolean onRestoreData(IntentParams intentParams) {
- textStr = intentParams.getParam("data").toString();
- return true;
- }
- @Override
- public void onCompleteContinuation(int i) {
- }
- @Override
- public void onRemoteTerminated() {
- }
- }
此外,不同于啟動行為,F(xiàn)A的遷移還涉及到狀態(tài)數據的傳遞。為此,繼承的IAbilityContinuation接口為開發(fā)者提供遷移過程中特定事件的管理能力。通過自定義遷移事件相關的行為,最終實現(xiàn)對Ability的遷移。主要以較為常用的兩個事件,包括遷移發(fā)起端完成遷移的回調onCompleteContinuation(int result)以及接收到遠端遷移行為傳遞數據的回調onRestoreData(IntentParams restoreData)。其他還包括遷移到遠端設備的FA關閉的回調onRemoteTerminated()、用于本地遷移發(fā)起時保存狀態(tài)數據的回調onSaveData(IntentParams saveData)和本地發(fā)起遷移的回調onStartContinuation()。
7. 請求回遷
- Button btn1 = (Button) findComponentById(ResourceTable.Id_migration_button_back);
- btn1.setClickedListener(new Component.ClickedListener() {
- @Override
- public void onClick(Component component) {
- String deviceId = DeviceUtils.getDeviceId();
- if(deviceId!=null){
- continueAbilityReversibly(deviceId); //可撤回遷移
- }
- }
- });
- Button btn2 = (Button) findComponentById(ResourceTable.Id_migration_button_back2);
- btn2.setClickedListener(new Component.ClickedListener() {
- @Override
- public void onClick(Component component) {
- reverseContinueAbility(); //撤回遷移
- }
- });
1)設備A上的Page請求回遷。
2)系統(tǒng)回調設備B上Page及其AbilitySlice棧中所有AbilitySlice實例的IAbilityContinuation.onStartContinuation()方法,以確認當前是否可以立即遷移。
3)如果可以立即遷移,則系統(tǒng)回調設備B上Page及其AbilitySlice棧中所有AbilitySlice實例的IAbilityContinuation.onSaveData()方法,以便保存回遷后恢復狀態(tài)必須的數據。
4)如果保存數據成功,則系統(tǒng)在設備A上Page恢復AbilitySlice棧,然后回調IAbilityContinuation.onRestoreData()方法,傳遞此前保存的數據。
5)如果數據恢復成功,則系統(tǒng)終止設備B上Page的生命周期。
©著作權歸作者和HarmonyOS技術社區(qū)共同所有,如需轉載,請注明出處,否則將追究法律責任
https://harmonyos.51cto.com/#zz