1、跨設備啟動FA、跨設備遷移、回遷

(1)權(quán)限
ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE:用于允許監(jiān)聽分布式組網(wǎng)內(nèi)的設備狀態(tài)變化。 ohos.permission.GET_DISTRIBUTED_DEVICE_INFO:用于允許獲取分布式組網(wǎng)內(nèi)的設備列表和設備信息。 ohos.permission.GET_BUNDLE_INFO:用于查詢其他應用的信息。 ohos.permission.DISTRIBUTED_DATASYNC:用于允許不同設備間的數(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"} ] //主動申明,要多設備協(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:
//點擊后跨設備打開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","我夸設備把你這個FA拉起來了!");
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","我夸設備把你這個FA拉起來了!");
startAbility(intent);
break;
case ResourceTable.Id_main_migration_btn:
//點擊后進入要遷移的Ability頁面
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...把接收到的值顯示到頁面就行,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());
});
//我們這里的實驗環(huán)境,就兩部手機,組件還沒講
//我就直接使用deviceIds的第一個元素,做為啟動遠程設備的目標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);
//給上面的文本設置一個背景樣式
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)實現(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、要進行遷移的Ability實現(xiàn)接口 IAbilityContinuation,實現(xiàn)的方法返回值改成true
//2、要進行遷移的Ability下面關(guān)聯(lián)的所有AbilitySlice都要實現(xiàn)接口 IAbilityContinuation,
// 實現(xiàn)的方法上處理數(shù)據(jù)
//3、進行遷移
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、跨設備連接Service
啟動遠程設備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) {
}
}
啟動:
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)閉遠程設備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;
僅通過啟動和停止Service Ability兩種方式對Service進行調(diào)度無法應對需長期交互的場景, 簡單地說,信息就是只能去,回不來! 因此,分布式任務調(diào)度平臺向開發(fā)者提供了跨設備Service連接及斷開連接的能力。 鏈接上了,信息可去可回! 鏈接是使用connectAbility()方法,需要傳入目標Service的Intent與接口IAbilityConnection的實例對象。 接口IAbilityConnection提供了兩個方法供開發(fā)者實現(xiàn): (1)onAbilityConnectDone()用來處理連接的回調(diào)。 (2)onAbilityDisconnectDone()用來處理斷開連接的回調(diào)。 我們可以在onAbilityConnectDone()中獲取管理鏈接的代理,進一步為了使用該代理跨設備調(diào)度Service, 開發(fā)者需要在本地及遠端分別實現(xiàn)對外接口一致的代理,這個接口是IRemoteBroker。 添加按鈕:

發(fā)起連接的本地側(cè)的代理示例如下:
public class MyRemoteProxy implements IRemoteBroker {
//IRemoteBroker:獲取遠程代理對象的持有者
private static final int ERR_OK = 0;
//COMMAND_PLUS表示有效消息進行通信的約定的標記,MIN_TRANSACTION_ID是這個標記可以用的最小值:1
private static final int COMMAND_PLUS = IRemoteObject.MIN_TRANSACTION_ID;
//IRemoteObject:此接口
// 可用于查詢或獲取接口描述符、
// 添加或刪除死亡通知、
// 將對象狀態(tài)轉(zhuǎn)儲到特定文件以及發(fā)送消息。
private final IRemoteObject remote;
public MyRemoteProxy(IRemoteObject remote) {
this.remote = remote;
}
@Override
public IRemoteObject asObject() {//獲取遠程代理對象的方法
return remote;
}
public int plus(int a,int b) throws RemoteException {
//MessageParcel:這個類提供了讀寫對象、接口標記、文件描述符和大數(shù)據(jù)的方法。
MessageParcel data = MessageParcel.obtain();
//obtain()創(chuàng)建索引為0的空MessageParcel對象
MessageParcel reply = MessageParcel.obtain();
//MessageOption:定義與sendRequest一起發(fā)送消息的選項。
// option不同的取值,決定采用同步或異步方式跨設備調(diào)用Service
// 這個例子我們需要同步獲取對端Service執(zhí)行加法運算后的結(jié)果,同步模式調(diào)用sendRequest接口,即MessageOption.TF_SYNC
// 對應的是異步:TF_ASYNC
MessageOption option = new MessageOption(MessageOption.TF_SYNC);
data.writeInt(a);
data.writeInt(b);
try {
remote.sendRequest(COMMAND_PLUS, data, reply, option);
//第1個參數(shù):約定通信雙方確定的消息標記。
//第2個參數(shù):發(fā)送到對等端側(cè)的數(shù)據(jù)包裹MessageParcel對象。
//第3個參數(shù):對等端側(cè)返回的數(shù)據(jù)包裹MessageParcel對象。
//第4個參數(shù):設置發(fā)送消息,用同步還是異步模式。
int ec = reply.readInt(); //返回通信成不成功,約定的標記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對象。
reply.reclaim();
}
}
}
等待連接的遠端側(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è)還需要作如下修改:
// 綁定前面定義的代理,實例化出發(fā)起鏈接側(cè)需要的代理
private MyRemote remote = new MyRemote();
@Override
public IRemoteObject onConnect(Intent intent) {
//鏈接成功的時候,給發(fā)起鏈接側(cè)返回去
return remote.asObject();
}
完成上述步驟后,可以通過點擊事件實現(xiàn)連接、利用連接關(guān)系控制PA以及斷開連接等行為,代碼示例如下:
private MyRemoteProxy mProxy = null;
// 創(chuàng)建連接回調(diào)實例
private IAbilityConnection conn = new IAbilityConnection() {
// 連接到Service的回調(diào)
@Override
public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int resultCode) {
// 在這里開發(fā)者可以拿到服務端傳過來IRemoteObject對象,從中解析出服務端傳過來的信息
mProxy = new MyRemoteProxy(iRemoteObject);
UIUtils.showTip(MainAbilitySlice.this,"拿到remoteObject:" + mProxy);
}
// 意外斷開連接才會回調(diào)
@Override
public void onAbilityDisconnectDone(ElementName elementName, int resultCode) {
}
};
// 連接遠程
case ResourceTable.Id_main_connect_remoteService_btn:
//1、實現(xiàn)連接的本地側(cè)的代理
//2、實現(xiàn)等待連接的遠端側(cè)的代理
//3、修改等待連接側(cè)的Service
//4、在本地(發(fā)起鏈接側(cè))獲取遠端(被鏈接側(cè))返回過來的鏈接代理,創(chuàng)建鏈接后的回調(diào)函數(shù)
//5、實現(xiàn)鏈接,通過代理對象使用Service的服務
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;
// 用完斷開
case ResourceTable.Id_main_disconnect_remoteService_btn:
disconnectAbility(iAbilityConnection);
break;
編輯:hfy
-
鴻蒙系統(tǒng)
+關(guān)注
關(guān)注
183文章
2642瀏覽量
69838
發(fā)布評論請先 登錄
名單公布!【書籍評測活動NO.53】鴻蒙操作系統(tǒng)設計原理與架構(gòu)
科普干貨|談談鴻蒙LiteOS-M與HUAWEI LiteOS內(nèi)核的幾大不同之處
如何基于分布式軟總線進行“三步走”極簡開發(fā)
軟通動力鴻蒙生態(tài)建設再進一步 分布式技術(shù)搶先體驗
深度解讀設備的“萬能語言”鴻蒙系統(tǒng)的分布式軟總線能力 精選資料推薦
5分鐘深度解析鴻蒙基礎架構(gòu),附原文檔!
華為的鴻蒙OS是否適配小米手機的操作系統(tǒng)
華為鴻蒙操作系統(tǒng)分布式能力實現(xiàn)跨設備使用
OpenHarmony軟總線設計理念
一文詳解OpenHarmony軟總線
跨網(wǎng)段訪問網(wǎng)關(guān)有什么功能及作用
鴻蒙軟總線跨設備訪問解析
評論