電子產業(yè)一站式賦能平臺

PCB聯(lián)盟網

搜索
查看: 890|回復: 0
收起左側

#2020征文-手機#【鴻蒙基地】鴻蒙跨設備啟動窗口:Page Ability

[復制鏈接]

2607

主題

2607

帖子

7472

積分

高級會員

Rank: 5Rank: 5

積分
7472
跳轉到指定樓層
樓主
發(fā)表于 2021-1-25 15:56:25 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
#2020征文-手機#【鴻蒙基地】鴻蒙跨設備啟動窗口:Page Ability,   

   HarmonyOS的核心特性(或稱為賣點)之一就是軟總線技術,而Page Ability的跨設備遷移是軟總線的一個具體技術實現(xiàn)。所謂跨設備遷移Page Ability,是指設備A中的特定App調用設備B中該App的Page Ability。這有一個前提,就是設備A和設備B都安裝了同一個App。如果B設備沒有安裝App,B設備就會自動從華為應用商店下載這個App,當然,這一過程是完全靜默的。下載完后,就會自動啟動相應的Page Ability。這種技術不僅可以啟動另一個設備上的Page Ability,還可以向另一個設備中的Page Ability傳遞數(shù)據(jù)。



這種技術的一個主要應用場景是,可以將在設備A上完成了一半的工作,遷移到設備B上繼續(xù)完成。例如,在家中平板電腦上要回一封EMail,但臨時有急事,需要出門,這時可以將在平板電腦上寫了一半的EMail遷移到手機上,需要在路上完成剩下的工作。



1. 跨設備遷移前的準備工作

在進行跨設備遷移之前,需要為HARMonyOS設備做一下準備:

(1) 打開HarmonyOS設備中的藍牙;

(2)HarmonyOS設備需要連入Wi-Fi,而且多個HarmonyOS需要在同一個網段;

(3)多個HarmonyOS設備需要用同一個華為開發(fā)者賬號登錄,如圖1所示。



圖1 用同一個華為開發(fā)者賬號登錄



(4)點擊“設置”>“更多連接”>“多設備協(xié)同”,進入多設備協(xié)同窗口,打開多設備協(xié)同開關,如圖2所示。

圖2 多設備協(xié)同



(5)修改HarmonyOS設備名。點擊“設置”>“藍牙”>“設備名稱”,進入設備名稱窗口,輸入一個新的什么名稱,如圖3所示。盡管這一步不是必須的,但如果擁有多部HarmonyOS設備,可能很多HarmonyOS設備的名稱是相同或相近的。為了更好區(qū)分不同的HarmonyOS設備,建議修改HarmonyOS設備名稱。



圖3 修改HarmonyOS設備名稱



2 獲取設備列表



跨設備遷移是通過設備ID來區(qū)分不同設備的,所以首先要獲取所有可用的設備的ID。獲取設備ID需要調用DeviceManager.getDeviceList方法,該方法返回一個List對象,類型是DeviceInfo,用來描述設備的相關信息,包括設備ID、設備名稱(就是上一節(jié)設置的設備名稱)等。實現(xiàn)代碼如下:

List<DeviceInfo> deviceInfoList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);



getDeviceList方法有一個參數(shù),是一個int類型的值,表示獲取什么狀態(tài)的設備的信息。可以指定的值如下:

(1) DeviceInfo.FLAG_GET_ONLINE_DEVICE:獲取所有在線設備的信息;

(2) DeviceInfo. FLAG_GET_OFFLINE_DEVICE:獲取所有離線設備的信息;

(3) DeviceInfo. FLAG_GET_ALL_DEVICE:獲取所有設備的信息;

通常會使用第1個值,獲取所有在線設備的信息,因為只有設備在線,才能將Page Ability遷移到該設備上。



下面給出一個案例,該案例實現(xiàn)了一個通用的顯示可用設備列表的Page Ability,點擊某一個設備,會返回該設備的ID,



在device_ids.xml布局文件中放置了一個ListContainer組件,用于顯示獲取的所有可用設備的相關信息。實現(xiàn)代碼如下:

public class DeviceIdsAbility extends Ability {

     // 保存獲取到的所有設備的信息

     private List<DeviceInfo> deviceInfos;

     private ListContainer listContainerDeviceIds;

     // 獲取所有可用的設備的相關信息

     public static List<DeviceInfo> getAvailabLEDeviceIds() {

         List<DeviceInfo> deviceInfoList =

                 DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);

         IF (deviceInfoList == null) {

             return new ArrayList<>();

         }

         if (deviceInfoList.size() == 0) {

             return new ArrayList<>();

         }

         return deviceInfoList;

     }

     @Override

     public void onStart(Intent intent) {

         super.onStart(intent);

         super.setUIContent(ResourceTable.layout_device_ids);

         deviceInfos = getAvailableDeviceIds();

         listContainerDeviceIds = 、

         (ListContainer)findComponentById(ResourceTable.Id_listcontainer_deviceids);

         if(listContainerDeviceIds != null) {

             // 為ListContainer組件設置列表項監(jiān)聽器

             listContainerDeviceIds.setItemclickedListener(new ListContainer.ItemClickedListener() {

                 @Override

                 public void onItemClicked(ListContainer listContainer, Component component, int i, long l) {

                     // 當單擊某個列表項(設備)后,會獲取該設備的ID,并將這個ID作為Page Ability

                     // 的結果返回

                     String deviceId = deviceInfos.get(i).getDeviceId();

                     Intent intent = new Intent();

                     intent.setParam(“deviceId“, deviceId);

                     setResult(100,intent);

                     // 關閉當前的Page Ability

                     terminateAbility();



                 }

             });

             // 為ListContainer組件設置Provider

             listContainerDeviceIds.setItemProvider(new RecycleItemProvider() {

                 @Override

                 public int getCount() {

                     return deviceInfos.size();

                 }



                 @Override

                 public Object getItem(int i) {

                     return deviceInfos.get(i);

                 }



                 @Override

                 public long getItemId(int i) {

                     return i;

                 }



                 @Override

                 public Component getComponent(int i, Component component, ComponentContainer componentContainer) {

                     if(component == null) {

                         // 如果component為null,說明沒有可以利用的列表項視圖,所以要從布局文件

                         // 裝載一個新的視圖對象

                         component = (DirectionalLayout)LayoutScatter.getInstance(DeviceIdsAbility.this).parse(ResourceTable.Layout_device_id_item,null,false);

                     }

                     Text textDeviceName = (Text)component.findComponentById(ResourceTable.Id_text_device_name);

                     Text textDeviceId = (Text)component.findComponentById(ResourceTable.Id_text_device_id);

                     if(textDeviceName != null) {

                         // 顯示設備名

                         textDeviceName.setText(deviceInfos.get(i).getDeviceName());

                     }

                     if(textDeviceId != null) {

                         // 顯示設備ID

                         textDeviceId.setText(deviceInfos.get(i).getDeviceId());

                     }

                     return component;

                 }

             });



         }



     }

}



在DeviceIdsAbility類中為ListContainer組件裝載列表項時,在getComponent方法中利用了第2個參數(shù)component,該參數(shù)就是列表項的根視圖。如果component為null,表明并沒有可以利用的列表項視圖,所以要創(chuàng)建一個新的列表項視圖。如果不為null,表明可以利用其他的還沒有顯示的列表項視圖,只需要替換該視圖的Text組件中顯示的信息即可。

最后在config.json文件中添加一些與分布式相關的權限。

     “reqPermissions“: [

       {

         “name“: “ohos.permission.GET_DISTRIBUTED_DEVICE_INFO“

       },

       {

         “name“: “com.huawei.permission.ACCESS_DISTRIBUTED_ABILITY_GROUP“

       },

       {

         “name“: “ohos.permission.DISTRIBUTED_DATASYNC“

       }

]



運行程序,會看到如圖4所示的設備列表。





                                                                                                                           圖4 獲取可用設備的ID



要注意的是,通過DeviceManager.getDeviceList方法只能獲取其他設備的信息,不能獲取自身的信息,例如,有設備A、設備B和設備C。在設備A中只能獲取設備B和設備C的信息,而不能獲取設備A的信息。在設備B和設備C中的表現(xiàn)也類似。



3 根據(jù)設備ID調用Page Ability



一個Page Ability要想跨設備訪問,必須實現(xiàn)IAbilityContinuation接口,否則會拋出異常。該接口必須實現(xiàn)的有4個方法,他們的含義如下:

public inteRFace IAbilityContinuation {

     // 開始遷移,如果返回true,表示可以開始遷移

     boolean onStartContinuation();

     // 開始傳遞數(shù)據(jù),如果返回true,表示成功傳遞數(shù)據(jù)

     boolean onSaveData(IntentParams var1);

     // 開始恢復數(shù)據(jù),如果返回true,表示成功恢復數(shù)據(jù)

     boolean onRestoreData(IntentParams var1);

     // 已經完成Page Ability遷移

     void onCompleteContinuation(int var1);

}



假設在設備A上將Page Ability遷移到設備B。onStartContinuation方法和onSaveData方法是在設備A上被調用的,而onRestoreData方法和onCompleteContinuation方法是在設備B上被調用的。為了遷移Page Ability,需要在設備A上執(zhí)行下面的代碼:

continueAbility(deviceId);



其中deviceID是設備ID。當調用該方法后,在設備A上就會依次調用onStartContinuation方法和onSaveData方法,在設備B上會依次調用onRestoreData方法和onCompleteContinuation方法。其中onSaveData方法和onRestoreData方法都有一個IntentParams類型的參數(shù),通過該參數(shù)可以在設備A和設備B之間通過Page Ability傳遞數(shù)據(jù)(使用方式與Intent類似)。通常在onRestoreData方法中恢復Page Ability從設備A上遷移到設備B上時的數(shù)據(jù)。



下面給出一個實際的案例,在Page Ability上放置了一個TextField組件,并在該組件中輸入了一些文本,然后點擊按鈕,將該Page Ability遷移到另一部HarmonyOS手機上,并恢復遷移時的數(shù)據(jù)。



實現(xiàn)代碼如下:

public class CrossDevicePageAbility extends Ability implements IAbilityContinuation {

     private List<DeviceInfo> deviceInfos;

     private ListContainer listContainerDeviceIds;

     private TextField textFieldContent;

     private String content;

     // 授權方法

     private void requestPermission() {

         // 實現(xiàn)Page Ability跨設備遷移,必須用Java代碼申請下面的權限

         // 否則不會有任何反應

         String[] permission = {

                 “ohos.permission.DISTRIBUTED_DATASYNC“};

         List<String> applyPermissions = new ArrayList<>();

         for (String element : permission) {

             // 驗證自身是否已經獲得了該權限

             if (verifySelfPermission(element) != 0) {

                 if (canRequestPermission(element)) {

                     // 如果未獲得權限,將該權限添加到權限列表

                     applyPermissions.add(element);

                 } else {

                 }

             } else {

             }

         }

         // 申請相應權限

         requestPermissionsFromUser(applyPermissions.toArray(new String[0]), 0);

     }

     // 要想成功跨設備遷移Page Ability,該方法必須返回true

     @Override

     public boolean onStartContinuation() {

         return true;

     }   

     @Override

     public boolean onSaveData(IntentParams intentParams) {

         // 保存要傳遞的數(shù)據(jù)

         intentParams.setParam(“content“,textFieldContent.getText());

         return true;

     }

     @Override

     public boolean onRestoreData(IntentParams intentParams) {

         // 獲取傳遞過來的數(shù)據(jù)

         content = String.valueOf(intentParams.getParam(“content“));

         return true;

     }

     @Override

     public void onCompleteContinuation(int i) {



     }

     @Override

     protected void onAbilityResult(int requestCode, int resultCode, Intent resultData) {

         // 當選擇設備后,利用返回的設備ID遷移Page Ability

         if(resultCode == 100 && requestCode == 99) {

             // 獲取設備ID

             String deviceId = resultData.getStringParam(“deviceId“);

             Tools.showTip(this, deviceId);

             // 跨設備遷移Page Ability

             continueAbility(deviceId);

         }

     }



     @Override

     public void onStart(Intent intent) {

         super.onStart(intent);

         super.setUIContent(ResourceTable.Layout_cross_device_page_ability);

         // 申請權限

         requestPermission();

         Button button =

           (Button)findComponentById(ResourceTable.Id_button_cross_device_page_ability);

         if(button != null) {

             button.setClickedListener(new Component.ClickedListener() {

                 @Override

                 public void onClick(Component component) {

                     // 顯示列表列表窗口

                     Intent intentPageAbility = new Intent();

                     Operation operation = new Intent.OperationBuilder()

                             .withBundleName(“com.unitymarvel.demo“)

                             .withAbilityName(“com.unitymarvel.demo.ability.DeviceIdsAbility“)

                             .build();

                     intentPageAbility.setOperation(operation);

                     startAbilityForResult(intentPageAbility,99);

                 }

             });

         }



         textFieldContent = (TextField)findComponentById(ResourceTable.Id_textfield_content);

         if(textFieldContent != null) {

             // 恢復TextField組件中的數(shù)據(jù)

             textFieldContent.setText(content);

         }

     }

}



閱讀這段代碼,需要了解下面幾點:



(1)要想成功遷移Page Ability,并成功傳遞數(shù)據(jù)。onStartContinuation方法、onSaveData方法和onRestoreData方法都必須返回true,如果讀者使用IDE的自動生成代碼功能,默認這幾個方法都會返回false,請將他們的返回值改成true;

(2)在HarmonyOS中有一些權限,并不是在config.json中聲明就可以了,還需要使用Java代碼申請,例如,Page Ability跨設備遷移就需要使用Java代碼申請ohos.permission.DISTRIBUTED_DATASYNC權限。如果是第一次申請,會彈出如圖5的授權對話框,點擊“始終允許”按鈕關閉該對話框,第2次申請權限,就不會彈出該對話框了;

(3)由于onRestoreData方法在onStart方法之前調用,所以不能直接在onRestoreData方法中使用組件對象,因為組件對象通常都是在onStart方法中創(chuàng)建的。所以在onRestoreData方法被調用時,這些組件對象還都是空。正確的做法是在onRestoreData方法中將要恢復的數(shù)據(jù)保存到成員變量中,然后在onStart方法中創(chuàng)建完組件對象后,用這些變量恢復組件中的數(shù)據(jù)。

(4)本例考慮了多部HarmonyOS設備遷移的問題,所以使用了上一節(jié)編寫的設備列表窗口。在開始跨設備遷移Page Ability之前,會先彈出一個設備列表窗口,當用戶選擇一個設備后,會返回該設備的ID,然后在onAbilityResult方法中獲取這個返回的設備ID,最后使用continueAbility方法遷移Page Ability;

                                                                                                                                圖5 授權對話框

現(xiàn)在運行程序,關閉授權對話框,并在TextField組件中輸入一些內容,最后點擊“跨設備遷移Page Ability”按鈕,會彈出一個設備列表窗口,選擇相應的設備后,會在選中的設備中彈出同樣的Page Ability,并且TextField組件的數(shù)據(jù)與原設備上的完全相同,如圖6所示。注意,只要被調用方安裝了App,不管設備是否已經啟動了App,否會自動彈出這個被遷移的Page Ability。

                                                                                                                圖6 跨設備遷移Page Ability的效果



原文來自51CTO
回復

使用道具 舉報

發(fā)表回復

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則


聯(lián)系客服 關注微信 下載APP 返回頂部 返回列表