HarmonyOS Sample之JavaDistributeAuthDemo分布式身份認(rèn)證功能
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
1.介紹
相信大部分關(guān)注HarmonyOS的人來說,對于HarmonyOS的特性都有一定的了解了,從官網(wǎng)我們可以看到一些關(guān)鍵的提煉:“統(tǒng)一OS,彈性部署”,“硬件互助,資源共享”,“一次開發(fā),多端部署”。
接下來幾期就想和大家一起就HarmonyOS的特性,來找一些案例進(jìn)行學(xué)習(xí)和實(shí)踐,目的是進(jìn)一步鞏固對特性的理解然后去靈活應(yīng)用。
這一期是通過分布式身份認(rèn)證的功能來了解一下 常用的通信方法。
分享的內(nèi)容:
- 在設(shè)備遷移或協(xié)同時(shí)都需要顯示可用設(shè)備列表,有一種方式不需要自己單獨(dú)獲取設(shè)備,也不需要自己定義列表布局文件就可以顯示設(shè)備窗口。
- 如何實(shí)現(xiàn)一個(gè)分布式身份認(rèn)證授權(quán)的功能。
案例來自codelabs官方示例分布式鑒權(quán)(Java) 本貼進(jìn)行了整理和分析,供學(xué)習(xí)和交流使用。
2.效果展示

3.搭建環(huán)境
安裝DevEco Studio,詳情請參考DevEco Studio下載。
設(shè)置DevEco Studio開發(fā)環(huán)境,DevEco Studio開發(fā)環(huán)境需要依賴于網(wǎng)絡(luò)環(huán)境,需要連接上網(wǎng)絡(luò)才能確保工具的正常使用,可以根據(jù)如下兩種情況來配置開發(fā)環(huán)境:
如果可以直接訪問Internet,只需進(jìn)行下載HarmonyOS SDK操作。
如果網(wǎng)絡(luò)不能直接訪問Internet,需要通過代理服務(wù)器才可以訪問,請參考配置開發(fā)環(huán)境。
下載源碼后,使用DevEco Studio 打開項(xiàng)目,模擬器運(yùn)行即可。
真機(jī)上運(yùn)行,參見真機(jī)運(yùn)行應(yīng)用
4.項(xiàng)目結(jié)構(gòu)

5.代碼講解
5.1 一種顯示流轉(zhuǎn)設(shè)備列表的方法
這種方式不需要自己單獨(dú)獲取設(shè)備,也不需要定義對應(yīng)的布局文件就可以顯示設(shè)備窗口。
①向ContinuationRegisterManager注冊一個(gè)跳轉(zhuǎn)的能力,并獲得分配給該能力的注冊令牌
- /**
- * 注冊流轉(zhuǎn)能力
- * register Continuation
- *
- * @param context
- * @param deviceCallback
- * @param show 是否顯示可用流轉(zhuǎn)設(shè)備
- */
- public void registerContinuation(AbilitySlice context, DeviceCallback deviceCallback, boolean show) {
- LogUtils.info("registerContinuation");
- if (continuationRegisterManager == null) {
- this.deviceCallback = deviceCallback;
- this.show = show;
- continuationRegisterManager = context.getContinuationRegisterManager();
- //支持的設(shè)備類型
- ExtraParams params = new ExtraParams();
- String[] devTypes = new String[]{ExtraParams.DEVICETYPE_SMART_PAD,
- ExtraParams.DEVICETYPE_SMART_WATCH,
- ExtraParams.DEVICETYPE_SMART_PHONE};
- params.setDevType(devTypes);
- //向ContinuationRegisterManager注冊一個(gè)跳轉(zhuǎn)的能力,并獲得分配給該能力的注冊令牌
- //您可以使用 IContinuationDeviceCallback 來監(jiān)聽用戶選擇設(shè)備進(jìn)行能力跳躍后的設(shè)備連接狀態(tài)變化,并實(shí)現(xiàn)您自己的處理邏輯。
- continuationRegisterManager.register(context.getBundleName(), params, callback, requestCallback);
- } else {
- if (show) {
- //顯示設(shè)備列表
- showContinuationDevice();
- }
- }
- }
②完成流轉(zhuǎn)后的狀態(tài)回調(diào),提供用于偵聽設(shè)備連接狀態(tài)更改的回調(diào)
- //完成流轉(zhuǎn)后的狀態(tài)回調(diào),提供用于偵聽設(shè)備連接狀態(tài)更改的回調(diào)。
- private IContinuationDeviceCallback callback = new IContinuationDeviceCallback() {
- @Override
- public void onDeviceConnectDone(String deviceId, String val) {
- LogUtils.info("onDeviceConnectDone");
- //設(shè)備連接完成后,提交選中設(shè)備的任務(wù)到隊(duì)列,等同于點(diǎn)擊了要流轉(zhuǎn)的設(shè)備
- EventHandler eventHandler = new EventHandler(EventRunner.getMainEventRunner());
- //提交任務(wù) 到事件隊(duì)列。
- eventHandler.postTask(new Runnable() {
- @Override
- public void run() {
- if (deviceCallback != null) {
- deviceCallback.onItemClick(deviceId);
- }
- //更新指定能力成功跳轉(zhuǎn)的設(shè)備的連接狀態(tài)。
- continuationRegisterManager
- .updateConnectStatus(abilityToken,
- //表示需要更新連接狀態(tài)的設(shè)備的ID。
- deviceId,
- DeviceConnectState.IDLE.getState(),null
- );
- }
- });
- }
- @Override
- public void onDeviceDisconnectDone(String deviceId) {
- LogUtils.info("onDeviceDisconnectDone");
- }
- };
③完成流轉(zhuǎn)請求的回調(diào),顯示可流轉(zhuǎn)的設(shè)備
- //完成流轉(zhuǎn)請求的回調(diào),提供用于偵聽躍點(diǎn)任務(wù)管理服務(wù)的連接狀態(tài)變化的回調(diào)。
- private RequestCallback requestCallback = new RequestCallback() {
- @Override
- public void onResult(int result) {
- abilityToken = result;
- if (show) {
- //顯示 可流轉(zhuǎn)設(shè)備
- showContinuationDevice();
- }
- }
- };
- /**
- * 顯示 可流轉(zhuǎn)設(shè)備
- * show Continuation
- */
- private void showContinuationDevice() {
- LogUtils.info("showContinuation");
- ExtraParams extraParams = new ExtraParams();
- extraParams.setDevType(new String[]{ExtraParams.DEVICETYPE_SMART_TV,
- ExtraParams.DEVICETYPE_SMART_PAD,
- ExtraParams.DEVICETYPE_SMART_WATCH,
- ExtraParams.DEVICETYPE_SMART_PHONE});
- extraParams.setDescription("設(shè)備流轉(zhuǎn)測試");
- //顯示 可流轉(zhuǎn)設(shè)備
- continuationRegisterManager.showDeviceList(abilityToken, extraParams, null);
- }
5.2 實(shí)現(xiàn)一個(gè)分布式身份認(rèn)證授權(quán)的功能
為了方便理解,把發(fā)送請求的設(shè)備成為 請求授權(quán)設(shè)備,進(jìn)行授權(quán)操作的設(shè)備成為 授權(quán)設(shè)備。
RegisterManager 自定義了CommonEvent 接口,MainAbilitySlice實(shí)現(xiàn)了該接口,所以RegisterManager具備了 到 MainAbilitySlice方向的通信能力。
RegisterManager 完成了對 ConstUtil.ORDER_CODE 類型公共事件的訂閱,所以就能夠接收到該類型的公共事件。
認(rèn)證授權(quán)的完整過程:
①在請求授權(quán)設(shè)備上,RegisterManager提供了注冊設(shè)備流轉(zhuǎn)能力的函數(shù),在設(shè)備連接完成的狀態(tài)回調(diào)中 提交了一個(gè)“點(diǎn)擊設(shè)備”的任務(wù)到執(zhí)行隊(duì)列。
- //完成流轉(zhuǎn)后的狀態(tài)回調(diào),提供用于偵聽設(shè)備連接狀態(tài)更改的回調(diào)。
- private IContinuationDeviceCallback callback = new IContinuationDeviceCallback() {
- @Override
- public void onDeviceConnectDone(String deviceId, String val) {
- LogUtils.info("onDeviceConnectDone");
- //設(shè)備連接完成后,提交選中設(shè)備的任務(wù)到隊(duì)列,等同于點(diǎn)擊了要流轉(zhuǎn)的設(shè)備
- EventHandler eventHandler = new EventHandler(EventRunner.getMainEventRunner());
- //提交任務(wù) 到事件隊(duì)列。
- eventHandler.postTask(new Runnable() {
- @Override
- public void run() {
- if (deviceCallback != null) {
- deviceCallback.onItemClick(deviceId);
- }
- //更新指定能力成功跳轉(zhuǎn)的設(shè)備的連接狀態(tài)。
- continuationRegisterManager
- .updateConnectStatus(abilityToken,
- //表示需要更新連接狀態(tài)的設(shè)備的ID。
- deviceId,
- DeviceConnectState.IDLE.getState(),null
- );
- }
- });
- }
在MainAbilitySlice中,在完成流轉(zhuǎn)能力注冊完成后,在“點(diǎn)擊設(shè)備” 的回調(diào)中,打開了遠(yuǎn)端授權(quán)設(shè)備上的AuthrRemoteSlice頁,同時(shí)傳遞了ConstUtil.DEVICE_ID和ConstUtil.ORDER_CODE(ConstUtil.START_ORDER)參數(shù)過去,其中ConstUtil.START_ORDER并沒有使用。
- /**
- * 注冊協(xié)同能力
- *
- * @param show
- */
- private void registerContinuation(boolean show) {
- LogUtils.info("registerContinuation");
- registerManager.registerContinuation(this,
- new RegisterManager.DeviceCallback() {
- @Override
- public void onItemClick(String deviceId) {
- LogUtils.info("onItemClick,deviceId:" + deviceId);
- //啟動(dòng)遠(yuǎn)端Ablity
- startRemoteAbility(deviceId);
- }
- }, show);
- }
- /**
- * 啟動(dòng)遠(yuǎn)端FA
- *
- * @param deviceId
- */
- private void startRemoteAbility(String deviceId) {
- LogUtils.info("startRemoteAbility");
- DialogUtil.showToast(getContext(), "請求已經(jīng)發(fā)送,等待對方確認(rèn)。");
- //
- String localDeviceId = KvManagerFactory.getInstance().createKvManager(
- new KvManagerConfig(this)).getLocalDeviceInfo().getId();
- Intent intent = new Intent();
- Operation operation =
- new Intent.OperationBuilder()
- .withDeviceId(deviceId)
- .withBundleName(getBundleName())
- .withAbilityName(MainAbility.class.getName())
- //指向授權(quán)設(shè)備的認(rèn)證頁面路由
- .withAction(MainAbility.ACTION)
- .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
- .build();
- intent.setOperation(operation);
- intent.setParam(ConstUtil.DEVICE_ID, localDeviceId);
- //啟動(dòng)遠(yuǎn)端FA指令代碼
- intent.setParam(ConstUtil.ORDER_CODE, ConstUtil.START_ORDER);
- startAbility(intent);
- }
②在授權(quán)設(shè)備上的AuthrRemoteSlice頁被打開后,點(diǎn)擊允許或不允許時(shí),請求分布式權(quán)限后,又打開了請求授權(quán)設(shè)備的 MainAbility。
- private void initViewData() {
- LogUtils.info("initViewData");
- findComponentById(ResourceTable.Id_yes_btn).setClickedListener(component -> {
- sendMessage(AUTH_TYPE1);
- });
- findComponentById(ResourceTable.Id_no_btn).setClickedListener(component -> {
- sendMessage(AUTH_TYPE2);
- });
- }
- private void sendMessage(int type) {
- LogUtils.info("sendMessage");
- //從MainAbility獲取HPermission實(shí)例
- HPermission hPermission = ((MainAbility) getAbility()).getPermission();
- //如果用戶已允許分布式權(quán)限,設(shè)置按鈕可用
- hPermission.requestPermissions(this, () -> {
- // button Enabled
- findComponentById(ResourceTable.Id_yes_btn).setEnabled(false);
- findComponentById(ResourceTable.Id_no_btn).setEnabled(false);
- //打開請求側(cè)的頁面
- startRemoteAbility(type);
- });
- }
- /**
- * 打開請求授權(quán)側(cè)的MainAbility
- *
- * @param type 是否同意授權(quán)
- */
- private void startRemoteAbility(int type) {
- LogUtils.info("startRemoteAbility");
- DialogUtil.showToast(getContext(), type == AUTH_TYPE1 ? "允許玩游戲" : "已拒絕玩游戲");
- Intent intent = new Intent();
- Operation operation =
- new Intent.OperationBuilder()
- .withDeviceId(remoteDeviceId == null ? "" : remoteDeviceId)
- .withBundleName(getBundleName())
- .withAbilityName(MainAbility.class.getName())
- .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
- .build();
- intent.setOperation(operation);
- //授權(quán)碼
- intent.setParam(ORDER_CODE, type);
- startAbility(intent);
- //關(guān)閉當(dāng)前宿主 Ability
- getUITaskDispatcher().delayDispatch(() -> terminateAbility(), DELAY);
- }
③在請求授權(quán)設(shè)備上,由于MainAbility設(shè)置為singleton模式(“launchType”: “singleton”)而且已經(jīng)實(shí)例過,所以請求進(jìn)入到onNewIntent函數(shù)。
config.json
- {
- ...
- "orientation": "unspecified",
- "visible": true,
- "name": "com.buty.javadistributedemo.MainAbility",
- "icon": "$media:icon",
- "description": "$string:mainability_description",
- "label": "$string:entry_MainAbility",
- "type": "page",
- "launchType": "singleton"
- }
在onNewIntent函數(shù)中,通過 CommonEventManager發(fā)布一個(gè)ConstUtil.ORDER_CODE類型的事件,該事件被RegisterManager收到并進(jìn)行了處理,如何處理的呢,又通過RegisterManager.CommonEvent 把事件傳遞給了實(shí)現(xiàn)了RegisterManager.CommonEvent接口MainAbilitySlice,最終顯示對端設(shè)備的授權(quán)結(jié)果(允許/不允許)
- /**
- * Ability設(shè)置為singleton模式
- * 當(dāng)創(chuàng)建時(shí),如果實(shí)例已存在,觸發(fā)該函數(shù)
- *
- * @param intent
- */
- @Override
- protected void onNewIntent(Intent intent) {
- LogUtils.info("onNewIntent");
- super.onNewIntent(intent);
- //是否允許
- int code = intent.getIntParam(ConstUtil.ORDER_CODE, 0);
- //
- String deviceId = intent.getStringParam(ConstUtil.DEVICE_ID);
- sendCommonEvent(code, deviceId);
- }
MainAbilitySlice收到消息
- /**
- * 實(shí)現(xiàn) RegisterManager的 CommonEvent接口
- *
- * @param code code
- * @param deviceId deviceId
- */
- @Override
- public void onReceiveEvent(int code, String deviceId) {
- LogUtils.info("onReceiveEvent,code:"+code);
- switch (code) {
- // Agree to allow games to be played
- case ConstUtil.AUTH_TYPE1:
- //start.setVisibility(Component.HIDE);
- tips.setVisibility(Component.VISIBLE);
- tips.setText("已授權(quán),可以開始游戲");
- break;
- // Refuse to play games
- case ConstUtil.AUTH_TYPE2:
- tips.setVisibility(Component.VISIBLE);
- //DialogUtil.exitDialog(getAbility());
- tips.setText("已拒絕,不可以游戲");
- break;
- default:
- break;
- }
- }
6.思考總結(jié)
分布式中常用的通信方式:
1.Intent 直接傳遞參數(shù)(intent.setParam(ORDER_CODE, type))
2.公共事件訂閱/發(fā)布的方式(Intent封裝到CommonEventData)
3.自定義接口的方式(RegisterManager.CommonEvent)
文章相關(guān)附件可以點(diǎn)擊下面的原文鏈接前往下載
https://harmonyos.51cto.com/resource/1574
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)