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:用于查詢其他應(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é)同,讓用戶選擇允許還是禁止 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
//第一種寫法
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);
//第二鐘寫法
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)工具類
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:此接口
// 可用于查詢或獲取接口描述符、
// 添加或刪除死亡通知、
// 將對(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è)類提供了讀寫對(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文章
2642瀏覽量
69875
發(fā)布評(píng)論請(qǐng)先 登錄
名單公布!【書籍評(píng)測(cè)活動(dòng)NO.53】鴻蒙操作系統(tǒng)設(shè)計(jì)原理與架構(gòu)
科普干貨|談?wù)?b class='flag-5'>鴻蒙LiteOS-M與HUAWEI LiteOS內(nèi)核的幾大不同之處
如何基于分布式軟總線進(jìn)行“三步走”極簡(jiǎn)開(kāi)發(fā)
軟通動(dòng)力鴻蒙生態(tài)建設(shè)再進(jìn)一步 分布式技術(shù)搶先體驗(yàn)
深度解讀設(shè)備的“萬(wàn)能語(yǔ)言”鴻蒙系統(tǒng)的分布式軟總線能力 精選資料推薦
鴻蒙軟總線的簡(jiǎn)單使用
5分鐘深度解析鴻蒙基礎(chǔ)架構(gòu),附原文檔!
華為的鴻蒙OS是否適配小米手機(jī)的操作系統(tǒng)
華為鴻蒙操作系統(tǒng)分布式能力實(shí)現(xiàn)跨設(shè)備使用
OpenHarmony軟總線設(shè)計(jì)理念
一文詳解OpenHarmony軟總線
跨網(wǎng)段訪問(wèn)網(wǎng)關(guān)有什么功能及作用
鴻蒙軟總線跨設(shè)備訪問(wèn)解析
評(píng)論