1、跨設(shè)備啟動(dòng)FA、跨設(shè)備遷移、回遷
(1)權(quán)限
ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE:用于允許監(jiān)聽(tīng)分布式組網(wǎng)內(nèi)的設(shè)備狀態(tài)變化。 ohos.permission.GET_DISTRIBUTED_DEVICE_INFO:用于允許獲取分布式組網(wǎng)內(nèi)的設(shè)備列表和設(shè)備信息。 ohos.permission.GET_BUNDLE_INFO:用于查詢(xún)其他應(yīng)用的信息。 ohos.permission.DISTRIBUTED_DATASYNC:用于允許不同設(shè)備間的數(shù)據(jù)交換。 "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"} ] //主動(dòng)申明,要多設(shè)備協(xié)同,讓用戶(hù)選擇允許還是禁止 requestPermissionsFromUser(new String[]{"ohos.permission.DISTRIBUTED_DATASYNC"}, 0);
(2)界面:ability_main.xml
另外我們需要的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: //點(diǎn)擊后跨設(shè)備打開(kāi)Fa //第一種寫(xiě)法 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","我夸設(shè)備把你這個(gè)FA拉起來(lái)了!"); startAbility(intent); //第二鐘寫(xiě)法 intent.setElement(new ElementName(Common.getOnLineDeviceId() ,"com.ybzy.demo","com.ybzy.demo.RemoveAbility")); intent.setFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE); intent.setParam("msg","我夸設(shè)備把你這個(gè)FA拉起來(lái)了!"); startAbility(intent); break; case ResourceTable.Id_main_migration_btn: //點(diǎn)擊后進(jìn)入要遷移的Ability頁(yè)面 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
RemoveAbility...把接收到的值顯示到頁(yè)面就行,setText()
(3)工具類(lèi)
public class Common{ public static String getDeviceId(){ List deviceList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE); if(deviceList.isEmpty()){ return null; } int deviceNum = deviceList.size(); List deviceIds = new ArrayList<>(deviceNum); List deviceNames = new ArrayList<>(deviceNum); deviceList.forEach((device)->{ deviceIds.add(device.getDeviceId()); deviceNames.add(device.getDeviceName()); }); //我們這里的實(shí)驗(yàn)環(huán)境,就兩部手機(jī),組件還沒(méi)講 //我就直接使用deviceIds的第一個(gè)元素,做為啟動(dòng)遠(yuǎn)程設(shè)備的目標(biāo)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); //給上面的文本設(shè)置一個(gè)背景樣式 ShapeElement style = new ShapeElement(); style.setShape(ShapeElement.RECTANGLE); style.setRgbColor(new RgbColor(77,77,77)); style.setCornerRadius(15); text.setBackground(style); //構(gòu)建存放上面的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)實(shí)現(xiàn)功能 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、要進(jìn)行遷移的Ability實(shí)現(xiàn)接口 IAbilityContinuation,實(shí)現(xiàn)的方法返回值改成true //2、要進(jìn)行遷移的Ability下面關(guān)聯(lián)的所有AbilitySlice都要實(shí)現(xiàn)接口 IAbilityContinuation, // 實(shí)現(xiàn)的方法上處理數(shù)據(jù) //3、進(jìn)行遷移 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、跨設(shè)備連接Service
啟動(dòng)遠(yuǎn)程設(shè)備Service的代碼示例如下: 添加按鈕:
新建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) { } }
啟動(dòng):
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;
關(guān)閉遠(yuǎn)程設(shè)備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;
僅通過(guò)啟動(dòng)和停止Service Ability兩種方式對(duì)Service進(jìn)行調(diào)度無(wú)法應(yīng)對(duì)需長(zhǎng)期交互的場(chǎng)景, 簡(jiǎn)單地說(shuō),信息就是只能去,回不來(lái)! 因此,分布式任務(wù)調(diào)度平臺(tái)向開(kāi)發(fā)者提供了跨設(shè)備Service連接及斷開(kāi)連接的能力。 鏈接上了,信息可去可回! 鏈接是使用connectAbility()方法,需要傳入目標(biāo)Service的Intent與接口IAbilityConnection的實(shí)例對(duì)象。 接口IAbilityConnection提供了兩個(gè)方法供開(kāi)發(fā)者實(shí)現(xiàn): (1)onAbilityConnectDone()用來(lái)處理連接的回調(diào)。 (2)onAbilityDisconnectDone()用來(lái)處理斷開(kāi)連接的回調(diào)。 我們可以在onAbilityConnectDone()中獲取管理鏈接的代理,進(jìn)一步為了使用該代理跨設(shè)備調(diào)度Service, 開(kāi)發(fā)者需要在本地及遠(yuǎn)端分別實(shí)現(xiàn)對(duì)外接口一致的代理,這個(gè)接口是IRemoteBroker。 添加按鈕:
發(fā)起連接的本地側(cè)的代理示例如下:
public class MyRemoteProxy implements IRemoteBroker { //IRemoteBroker:獲取遠(yuǎn)程代理對(duì)象的持有者 private static final int ERR_OK = 0; //COMMAND_PLUS表示有效消息進(jìn)行通信的約定的標(biāo)記,MIN_TRANSACTION_ID是這個(gè)標(biāo)記可以用的最小值:1 private static final int COMMAND_PLUS = IRemoteObject.MIN_TRANSACTION_ID; //IRemoteObject:此接口 // 可用于查詢(xún)或獲取接口描述符、 // 添加或刪除死亡通知、 // 將對(duì)象狀態(tài)轉(zhuǎn)儲(chǔ)到特定文件以及發(fā)送消息。 private final IRemoteObject remote; public MyRemoteProxy(IRemoteObject remote) { this.remote = remote; } @Override public IRemoteObject asObject() {//獲取遠(yuǎn)程代理對(duì)象的方法 return remote; } public int plus(int a,int b) throws RemoteException { //MessageParcel:這個(gè)類(lèi)提供了讀寫(xiě)對(duì)象、接口標(biāo)記、文件描述符和大數(shù)據(jù)的方法。 MessageParcel data = MessageParcel.obtain(); //obtain()創(chuàng)建索引為0的空MessageParcel對(duì)象 MessageParcel reply = MessageParcel.obtain(); //MessageOption:定義與sendRequest一起發(fā)送消息的選項(xiàng)。 // option不同的取值,決定采用同步或異步方式跨設(shè)備調(diào)用Service // 這個(gè)例子我們需要同步獲取對(duì)端Service執(zhí)行加法運(yùn)算后的結(jié)果,同步模式調(diào)用sendRequest接口,即MessageOption.TF_SYNC // 對(duì)應(yīng)的是異步:TF_ASYNC MessageOption option = new MessageOption(MessageOption.TF_SYNC); data.writeInt(a); data.writeInt(b); try { remote.sendRequest(COMMAND_PLUS, data, reply, option); //第1個(gè)參數(shù):約定通信雙方確定的消息標(biāo)記。 //第2個(gè)參數(shù):發(fā)送到對(duì)等端側(cè)的數(shù)據(jù)包裹MessageParcel對(duì)象。 //第3個(gè)參數(shù):對(duì)等端側(cè)返回的數(shù)據(jù)包裹MessageParcel對(duì)象。 //第4個(gè)參數(shù):設(shè)置發(fā)送消息,用同步還是異步模式。 int ec = reply.readInt(); //返回通信成不成功,約定的標(biāo)記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對(duì)象。 reply.reclaim(); } } }
等待連接的遠(yuǎn)端側(cè)的代理示例如下:
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; } }
等待連接側(cè)還需要作如下修改:
// 綁定前面定義的代理,實(shí)例化出發(fā)起鏈接側(cè)需要的代理 private MyRemote remote = new MyRemote(); @Override public IRemoteObject onConnect(Intent intent) { //鏈接成功的時(shí)候,給發(fā)起鏈接側(cè)返回去 return remote.asObject(); }
完成上述步驟后,可以通過(guò)點(diǎn)擊事件實(shí)現(xiàn)連接、利用連接關(guān)系控制PA以及斷開(kāi)連接等行為,代碼示例如下:
private MyRemoteProxy mProxy = null; // 創(chuàng)建連接回調(diào)實(shí)例 private IAbilityConnection conn = new IAbilityConnection() { // 連接到Service的回調(diào) @Override public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int resultCode) { // 在這里開(kāi)發(fā)者可以拿到服務(wù)端傳過(guò)來(lái)IRemoteObject對(duì)象,從中解析出服務(wù)端傳過(guò)來(lái)的信息 mProxy = new MyRemoteProxy(iRemoteObject); UIUtils.showTip(MainAbilitySlice.this,"拿到remoteObject:" + mProxy); } // 意外斷開(kāi)連接才會(huì)回調(diào) @Override public void onAbilityDisconnectDone(ElementName elementName, int resultCode) { } }; // 連接遠(yuǎn)程 case ResourceTable.Id_main_connect_remoteService_btn: //1、實(shí)現(xiàn)連接的本地側(cè)的代理 //2、實(shí)現(xiàn)等待連接的遠(yuǎn)端側(cè)的代理 //3、修改等待連接側(cè)的Service //4、在本地(發(fā)起鏈接側(cè))獲取遠(yuǎn)端(被鏈接側(cè))返回過(guò)來(lái)的鏈接代理,創(chuàng)建鏈接后的回調(diào)函數(shù) //5、實(shí)現(xiàn)鏈接,通過(guò)代理對(duì)象使用Service的服務(wù) 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, "獲取的結(jié)果:" + ret); } break; // 用完斷開(kāi) case ResourceTable.Id_main_disconnect_remoteService_btn: disconnectAbility(iAbilityConnection); break;
編輯:hfy
-
鴻蒙系統(tǒng)
+關(guān)注
關(guān)注
183文章
2634瀏覽量
66348
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論