How to play Hongmeng Softbus's cross-device access-small summary

How to play Hongmeng Softbus's cross-device access-small summary

table of Contents:

1. Start FA across devices, migrate across devices, and migrate back

2. Connect Service across devices

3. More articles

Key code:

1. Start FA across devices, migrate across devices, and migrate back

(1) Permission

ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE: Used to allow monitoring of device status changes in the distributed networking. ohos.permission.GET_DISTRIBUTED_DEVICE_INFO: used to allow access to the device list and device information in the distributed networking. ohos.permission.GET_BUNDLE_INFO: used to query information of other applications. ohos.permission.DISTRIBUTED_DATASYNC: Used to allow data exchange between different devices. "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"} ] //Proactively declare that multiple devices should be coordinated, allowing users to choose whether to allow or prohibit requestPermissionsFromUser(new String[]{"ohos.permission.DISTRIBUTED_DATASYNC"}, 0); Copy code

(2) Interface: ability_main.xml

<?xml version="1.0" encoding="utf-8"?> <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="match_parent" ohos:width="match_parent" ohos:orientation="vertical"> <Button ohos:id="$+id:main_start_fa_btn" ohos:height="match_content" ohos:width="300vp" ohos:text="1. Start the FA of the remote device" ohos:text_size="20fp" ohos:text_color="#ffffff" ohos:background_element="$graphic:button_bg" ohos:layout_alignment="horizontal_center" ohos:top_padding="8vp" ohos:bottom_padding="8vp" ohos:left_padding="40vp" ohos:right_padding="40vp" ohos:top_margin="20vp" /> <Button ohos:id="$+id:main_migration_btn" ohos:height="match_content" ohos:width="300vp" ohos:text="2. Migrate to remote device" ohos:text_size="20fp" ohos:text_color="#ffffff" ohos:background_element="$graphic:button_bg" ohos:layout_alignment="horizontal_center" ohos:top_padding="8vp" ohos:bottom_padding="8vp" ohos:left_padding="40vp" ohos:right_padding="40vp" ohos:top_margin="20vp" /> </DirectionalLayout> Copy code

button_bg.xml

<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <solid ohos:color="#007DFF"/> <corners ohos:radius="40"/> </shape> Copy code

In addition, we need 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: //Open Fa across devices after clicking //The first way to write 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","I praise the device for pulling up your FA!"); startAbility(intent); //How to write the second clock intent.setElement(new ElementName(Common.getOnLineDeviceId() ,"com.ybzy.demo","com.ybzy.demo.RemoveAbility")); intent.setFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE); intent.setParam("msg","I praise the device for pulling up your FA!"); startAbility(intent); break; case ResourceTable.Id_main_migration_btn: //Click to enter the Ability page to be migrated 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); } } Copy code

ability_migration.xml

<?xml version="1.0" encoding="utf-8"?> <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="match_parent" ohos:width="match_parent" ohos:background_element="#00ffff" ohos:orientation="vertical"> <Text ohos:id="$+id:migration_text" ohos:height="match_content" ohos:width="250vp" ohos:background_element="#0088bb" ohos:layout_alignment="horizontal_center" ohos:text="Below is an editable text box" ohos:text_size="50" ohos:padding="5vp" ohos:top_margin="30vp" /> <TextField ohos:id="$+id:migration_textfield" ohos:height="250vp" ohos:width="250vp" ohos:hint="Please enter..." ohos:layout_alignment="horizontal_center" ohos:background_element="#ffffff" ohos:text_color="#888888" ohos:text_size="20fp" ohos:padding="5vp" /> <Button ohos:id="$+id:migration_migration_btn" ohos:height="match_content" ohos:width="match_content" ohos:text="click to migrate" ohos:text_size="20fp" ohos:text_color="#ffffff" ohos:background_element="$graphic:button_bg" ohos:top_padding="8vp" ohos:bottom_padding="8vp" ohos:left_padding="50vp" ohos:right_padding="50vp" ohos:layout_alignment="horizontal_center" ohos:top_margin="30vp" /> <Button ohos:id="$+id:migration_migration_back_btn" ohos:height="match_content" ohos:width="match_content" ohos:text="Click to migrate back" ohos:text_size="20fp" ohos:text_color="#ffffff" ohos:background_element="$graphic:button_bg" ohos:top_padding="8vp" ohos:bottom_padding="8vp" ohos:left_padding="50vp" ohos:right_padding="50vp" ohos:layout_alignment="horizontal_center" ohos:top_margin="30vp" /> </DirectionalLayout> Copy code

RemoveAbility... Just display the received value on the page, setText()

(3) Tools

public class Common{ public static String getDeviceId(){ List<DeviceInfo> deviceList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE); if(deviceList.isEmpty()){ return null; } int deviceNum = deviceList.size(); List<String> deviceIds = new ArrayList<>(deviceNum); List<String> deviceNames = new ArrayList<>(deviceNum); deviceList.forEach((device)->{ deviceIds.add(device.getDeviceId()); deviceNames.add(device.getDeviceName()); }); //Our experimental environment here is just two mobile phones, the components have not been discussed yet //I will directly use the first element of deviceIds as the target id to start the remote device String devcieIdStr = deviceIds.get(0); return devcieIdStr; } public static void myShowTip(Context context,String msg){ //The core component text of the prompt box 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); //Set a background style for the above text ShapeElement style = new ShapeElement(); style.setShape(ShapeElement.RECTANGLE); style.setRgbColor(new RgbColor(77,77,77)); style.setCornerRadius(15); text.setBackground(style); //Build the layout to store the above text DirectionalLayout mainLayout = new DirectionalLayout(context); mainLayout.setWidth(MATCH_PARENT); mainLayout.setHeight(MATCH_CONTENT); mainLayout.setAlignment(LayoutAlignment.CENTER); mainLayout.addComponent(text); //Finally, let the above components bind 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(); } } Copy code

(4) Realize the function
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. The Ability to be migrated implements the interface IAbilityContinuation, and the return value of the implemented method is changed to true //2, all Ability Slices associated with the Ability to be migrated must implement the interface IAbilityContinuation, //Process data on the implemented method //3, perform migration 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) { } } Copy code

2. Connect Service across devices

The code example to start the remote device Service is as follows:
Add button:

<Button ohos:id="$+id:main_start_removeService_btn" ohos:height="match_content" ohos:width="300vp" ohos:text="Remote start ServiceAbility" ohos:text_size="20fp" ohos:text_color="#ffffff" ohos:background_element="$graphic:button_bg" ohos:layout_alignment="horizontal_center" ohos:top_padding="8vp" ohos:bottom_padding="8vp" ohos:left_padding="40vp" ohos:right_padding="40vp" ohos:top_margin="20vp" /> <Button ohos:id="$+id:main_stop_removeService_btn" ohos:height="match_content" ohos:width="300vp" ohos:text="Remotely close ServiceAbility" ohos:text_size="20fp" ohos:text_color="#ffffff" ohos:background_element="$graphic:button_bg" ohos:layout_alignment="horizontal_center" ohos:top_padding="8vp" ohos:bottom_padding="8vp" ohos:left_padding="40vp" ohos:right_padding="40vp" ohos:top_margin="20vp" /> Copy code

New 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) { } } Copy code

start up:

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; Copy code

Close the remote device 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; Copy code

Only by starting and stopping Service Ability two ways to schedule Service cannot cope with scenarios that require long-term interaction.
Simply put, information can only go, not return!
Therefore, the distributed task scheduling platform provides developers with the ability to connect and disconnect across device services.
The link is up, the information can go and return!

The link is to use the connectAbility() method, which needs to pass in the Intent of the target Service and the instance object of the interface IAbilityConnection.

The interface IAbilityConnection provides two methods for developers to implement:
(1) onAbilityConnectDone() is used to handle connection callbacks.
(2) onAbilityDisconnectDone() is used to handle the callback of disconnection.

We can get the agent of the management link in onAbilityConnectDone(). Further, in order to use the agent to dispatch the Service across devices,
developers need to implement agents with the same external interface locally and remotely. This interface is IRemoteBroker.

Add button:

<Button ohos:id="$+id:main_connect_remoteService_btn" ohos:height="match_content" ohos:width="300vp" ohos:text="Remote Link ServiceAbility" ohos:text_size="20fp" ohos:text_color="#ffffff" ohos:background_element="$graphic:button_bg" ohos:layout_alignment="horizontal_center" ohos:top_padding="8vp" ohos:bottom_padding="8vp" ohos:left_padding="40vp" ohos:right_padding="40vp" ohos:top_margin="20vp" /> <Button ohos:id="$+id:main_use_remoteService_btn" ohos:height="match_content" ohos:width="300vp" ohos:text="Use remote ServiceAbility" ohos:text_size="20fp" ohos:text_color="#ffffff" ohos:background_element="$graphic:button_bg" ohos:layout_alignment="horizontal_center" ohos:top_padding="8vp" ohos:bottom_padding="8vp" ohos:left_padding="40vp" ohos:right_padding="40vp" ohos:top_margin="20vp" /> <Button ohos:id="$+id:main_disconnect_remoteService_btn" ohos:height="match_content" ohos:width="300vp" ohos:text="Remote Disconnect ServiceAbility" ohos:text_size="20fp" ohos:text_color="#ffffff" ohos:background_element="$graphic:button_bg" ohos:layout_alignment="horizontal_center" ohos:top_padding="8vp" ohos:bottom_padding="8vp" ohos:left_padding="40vp" ohos:right_padding="40vp" ohos:top_margin="20vp" /> Copy code

An example of the proxy on the local side that initiates the connection is as follows:

public class MyRemoteProxy implements IRemoteBroker { //IRemoteBroker: Get the holder of the remote agent object private static final int ERR_OK = 0; //COMMAND_PLUS represents the agreed mark of effective message communication, MIN_TRANSACTION_ID is the minimum value that this mark can use: 1 private static final int COMMAND_PLUS = IRemoteObject.MIN_TRANSACTION_ID; //IRemoteObject: this interface //Can be used to query or obtain interface descriptors, //add or delete death notices, //Dump the object state to a specific file and send a message. private final IRemoteObject remote; public MyRemoteProxy(IRemoteObject remote) { this.remote = remote; } @Override public IRemoteObject asObject() {//Method of obtaining remote proxy object return remote; } public int plus(int a,int b) throws RemoteException { //MessageParcel: This class provides methods for reading and writing objects, interface tags, file descriptors and big data. MessageParcel data = MessageParcel.obtain(); //obtain() creates an empty MessageParcel object with index 0 MessageParcel reply = MessageParcel.obtain(); //MessageOption: defines the option to send a message together with sendRequest. //Different values of option, decide to use synchronous or asynchronous method to call Service across devices //In this example, we need to synchronously obtain the result of the addition operation performed by the opposite Service, and call the sendRequest interface in synchronous mode, which is MessageOption.TF_SYNC //Corresponds to asynchronous: TF_ASYNC MessageOption option = new MessageOption(MessageOption.TF_SYNC); data.writeInt(a); data.writeInt(b); try { remote.sendRequest(COMMAND_PLUS, data, reply, option); //The first parameter: the message tag determined by the agreed communication parties. //The second parameter: the data package MessageParcel object sent to the peer side. //The third parameter: The data returned by the peer side wraps the MessageParcel object. //The fourth parameter: Set whether to send a message in synchronous or asynchronous mode. int ec = reply.readInt();//returns the communication as unsuccessful, the agreed mark 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() Clear MessageParcel objects that are no longer used. reply.reclaim(); } } } Copy code

An example of a proxy on the remote side waiting to connect is as follows:

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; } } Copy code

The side waiting for the connection also needs to be modified as follows:

//Bind the previously defined agent to instantiate the agent needed to initiate the link private MyRemote remote = new MyRemote(); @Override public IRemoteObject onConnect(Intent intent) { //When the link is successful, return to the side that initiated the link return remote.asObject(); } Copy code

After completing the above steps, you can use click events to connect, use the connection relationship to control the PA, and disconnect and other behaviors. The code example is as follows:

private MyRemoteProxy mProxy = null; //Create connection callback instance private IAbilityConnection conn = new IAbilityConnection() { //Connect to the callback of the Service @Override public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int resultCode) { //Here the developer can get the IRemoteObject object passed from the server, and parse the information from the server from it mProxy = new MyRemoteProxy(iRemoteObject); UIUtils.showTip(MainAbilitySlice.this, "get remoteObject:" + mProxy); } //Call back when the connection is accidentally disconnected @Override public void onAbilityDisconnectDone(ElementName elementName, int resultCode) { } }; //connect remote case ResourceTable.Id_main_connect_remoteService_btn: //1, the proxy on the local side that realizes the connection //2. Realize the proxy on the remote side waiting for the connection //3, modify the Service waiting for the connection side //4. Obtain the link proxy returned from the remote end (the linked side) locally (the link originating side), and create the callback function after the link //5. Realize the link and use the service of Service through the proxy object 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; //use after link 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, "Result obtained:" + ret); } break; //Use up and disconnect case ResourceTable.Id_main_disconnect_remoteService_btn: disconnectAbility(iAbilityConnection); break; Copy code

Author: zhonghongfa

For more information, please visit the Hongmeng community jointly built by 51CTO and Huawei: harmonyos.51cto.com