ServiceAbility是在后台运行无界面应用程序的解决方案。
com.minwei.basicservice右键
New
Ability
Empty Service Ability
Service Name: ServiceAbility
Package name: com.minwei.basicservice
Enable background mode: Off
自动生成entry\src\main\java\com\minwei\basicservice\ServiceAbility.java文件,其中包含Ability类的子类ServiceAbility:
public class ServiceAbility extends Ability { @Override public void onStart(Intent intent) { super.onStart(intent); } @Override public void onCommand(Intent intent, boolean restart, int startId) { } @Override public IRemoteObject onConnect(Intent intent) { return null; } @Override public void onDisconnect(Intent intent) { } @Override public void onBackground() { super.onBackground(); } @Override public void onStop() { super.onStop(); } }
同时自动在config.json中添加相关配置:
{ "name": "com.minwei.basicservice.ServiceAbility", "icon": "$media:icon", "description": "$string:serviceability_description", "type": "service" <- 这是一个服务,与一般应用"page"不同 }
Intent intent = new Intent(); Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName(getBundleName()) .withAbilityName(ServiceAbility.class.getName()) .build(); intent.setOperation(operation); startAbility(intent);
Intent intent = new Intent(); Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName(getBundleName()) .withAbilityName(ServiceAbility.class.getName()) .build(); intent.setOperation(operation); stopAbility(intent);
|启动服务
v
onStart()
|
v
onCommand()
|启动服务
v
onCommand()
|停止服务
v
onBackground()
|
v
onStop()
onStop
初始态<----------------
|onStart |
v |
非活动态------------->后台态
onCommand|^ onBackground
v|
活动态
定义远程对象类:
public class RemoteObject extends LocalRemoteObject { private static final HiLogLabel label = new HiLogLabel( HiLog.LOG_APP, 0x00101, RemoteObject.class.getCanonicalName()); public RemoteObject() { HiLog.info(label, "RemoteObject()"); } public void manipulateService() { <------------------------ HiLog.info(label, "manipulateService()"); 5| } | } |
在ServiceAbility类的onConnect()回调方法中返回一个远程对象:
public class ServiceAbility extends Ability { | ... | OS @Override | 2| public IRemoteObject onConnect(Intent intent) { <------------| ... | 3| return new RemoteObject(); ----------------------------->| } | | ... | | } | |
创建一个连接对象:
IAbilityConnection connection = new IAbilityConnection() { | | @Override | | public void onAbilityConnectDone(ElementName elementName, | 4| IRemoteObject iRemoteObject, int i) { <------------------| RemoteObject object = (RemoteObject)iRemoteObject; | | object.manipulateService(); --------------------------- | } | | @Override | public void onAbilityDisconnectDone( | ElementName elementName, int i) { | } | }; |
以连接对象为参数连接服务:
Intent intent = new Intent(); | Operation operation = new Intent.OperationBuilder() | .withDeviceId("") | .withBundleName(getBundleName()) | .withAbilityName(ServiceAbility.class.getName()) | .build(); | intent.setOperation(operation); | connectAbility(intent, connection); | \________/ | | 1| ----------------------------------------->| OS
disconnectAbility(connection);
|连接服务
v
onStart()
|
v
onConnect()
|
v
RemoteObject()
|
v
manipulateService()
|断开服务
v
onDisconnect()
|
v
onBackground()
|
v
onStop()
onStop
初始态<----------------
|onStart |
v |
非活动态------------->后台态
onConnect|^ onBackground
v|onDisconnect
活动态
例程:BasicService
...\BasicService\entry\src\main\resources\base\graphic\background_button_start.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <corners ohos:radius="100"/> <solid ohos:color="#00a2e8"/> </shape>
...\BasicService\entry\src\main\resources\base\graphic\background_button_stop.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <corners ohos:radius="100"/> <solid ohos:color="#22b14c"/> </shape>
...\BasicService\entry\src\main\resources\base\graphic\background_button_connect.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <corners ohos:radius="100"/> <solid ohos:color="#ff7f27"/> </shape>
...\BasicService\entry\src\main\resources\base\graphic\background_button_disconnect.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <corners ohos:radius="100"/> <solid ohos:color="#ee1c24"/> </shape>
...\BasicService\entry\src\main\resources\base\graphic\background_toast_dialog.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <solid ohos:color="#80000000"/> <corners ohos:radius="12vp"/> </shape>
...\BasicService\entry\src\main\resources\base\layout\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:left_padding="60vp" ohos:right_padding="60vp" ohos:alignment="center" ohos:orientation="vertical"> <Button ohos:id="$+id:btnStart" ohos:height="match_content" ohos:width="match_parent" ohos:padding="$ohos:float:button_radius" ohos:background_element="$graphic:background_button_start" ohos:text="启动服务" ohos:text_size="28fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnStop" ohos:height="match_content" ohos:width="match_parent" ohos:padding="$ohos:float:button_radius" ohos:top_margin="20vp" ohos:background_element="$graphic:background_button_stop" ohos:text="停止服务" ohos:text_size="28fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnConnect" ohos:height="match_content" ohos:width="match_parent" ohos:padding="$ohos:float:button_radius" ohos:top_margin="20vp" ohos:background_element="$graphic:background_button_connect" ohos:text="连接服务" ohos:text_size="28fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnDisconnect" ohos:height="match_content" ohos:width="match_parent" ohos:padding="$ohos:float:button_radius" ohos:top_margin="20vp" ohos:background_element="$graphic:background_button_disconnect" ohos:text="断开服务" ohos:text_size="28fp" ohos:text_color="#ffffff" /> </DirectionalLayout>
...\BasicService\entry\src\main\resources\base\layout\toast_dialog.xml
<?xml version="1.0" encoding="utf-8"?> <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="match_content" ohos:width="match_content" ohos:left_padding="20vp" ohos:top_padding="5vp" ohos:right_padding="20vp" ohos:bottom_padding="5vp" ohos:background_element="$graphic:background_toast_dialog"> <Text ohos:id="$+id:txt" ohos:height="match_content" ohos:width="match_content" ohos:text_size="20fp" ohos:text_color="#ffffff" /> </DirectionalLayout>
...\BasicService\entry\src\main\java\com\minwei\basicservice\RemoteObject.java
public class RemoteObject extends LocalRemoteObject { private static final HiLogLabel label = new HiLogLabel( HiLog.LOG_APP, 0x00101, RemoteObject.class.getCanonicalName()); public RemoteObject() { HiLog.info(label, "RemoteObject()"); } public void manipulateService() { HiLog.info(label, "manipulateService()"); } }
...\BasicService\entry\src\main\java\com\minwei\basicservice\ServiceAbility.java
public class ServiceAbility extends Ability { private static final HiLogLabel label = new HiLogLabel( HiLog.LOG_APP, 0x00101, ServiceAbility.class.getCanonicalName()); @Override public void onStart(Intent intent) { super.onStart(intent); HiLog.info(label, "onStart()"); } @Override public void onCommand(Intent intent, boolean restart, int startId) { HiLog.info(label, "onCommand()"); } @Override public IRemoteObject onConnect(Intent intent) { HiLog.info(label, "onConnect()"); return new RemoteObject(); } @Override public void onDisconnect(Intent intent) { HiLog.info(label, "onDisconnect()"); } @Override public void onBackground() { super.onBackground(); HiLog.info(label, "onBackground()"); } @Override public void onStop() { super.onStop(); HiLog.info(label, "onStop()"); } }
...\BasicService\entry\src\main\java\com\minwei\basicservice\slice\MainAbilitySlice.java
public class MainAbilitySlice extends AbilitySlice implements ClickedListener { @Override public void onStart(Intent intent) { ... findComponentById(ResourceTable.Id_btnStart) .setClickedListener(this); findComponentById(ResourceTable.Id_btnStop) .setClickedListener(this); findComponentById(ResourceTable.Id_btnConnect) .setClickedListener(this); findComponentById(ResourceTable.Id_btnDisconnect) .setClickedListener(this); } ... @Override public void onClick(Component component) { switch (component.getId()) { case ResourceTable.Id_btnStart: startService(); break; case ResourceTable.Id_btnStop: stopService(); break; case ResourceTable.Id_btnConnect: connectService(); break; case ResourceTable.Id_btnDisconnect: disconnectService(); break; } } private void startService() { showToast("启动服务"); Intent intent = new Intent(); Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName(getBundleName()) .withAbilityName(ServiceAbility.class.getName()) .build(); intent.setOperation(operation); startAbility(intent); } private void stopService() { showToast("停止服务"); Intent intent = new Intent(); Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName(getBundleName()) .withAbilityName(ServiceAbility.class.getName()) .build(); intent.setOperation(operation); stopAbility(intent); } private IAbilityConnection connection = new IAbilityConnection() { @Override public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int i) { RemoteObject object = (RemoteObject)iRemoteObject; object.manipulateService(); } @Override public void onAbilityDisconnectDone( ElementName elementName, int i) { } }; private void connectService() { showToast("连接服务"); Intent intent = new Intent(); Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName(getBundleName()) .withAbilityName(ServiceAbility.class.getName()) .build(); intent.setOperation(operation); connectAbility(intent, connection); } private void disconnectService() { showToast("断开服务"); disconnectAbility(connection); } private void showToast(String text) { Component component = LayoutScatter.getInstance(this).parse( ResourceTable.Layout_toast_dialog, null, false); ((Text)component.findComponentById(ResourceTable.Id_txt)) .setText(text); new ToastDialog(this) .setContentCustomComponent(component) .setSize(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT) .setDuration(5000) .setAlignment(LayoutAlignment.BOTTOM) .setOffset(0, AttrHelper.vp2px(60, this)) .show(); } }
运行效果如下图所示:
![]() |
![]() |
|
![]() |
应用程序一旦进入后台,出于节约资源的考虑,系统会杀死由该应用程序启动的所有Service。如果其中某个Service正在执行任务,如播放音乐或导航等,该任务即随之中断,给用户带来不好的使用体验。
为此,可将某些Service设置为前台Service,即使启动该Service的应用程序已经退至后台,该Service也不会被杀死,而是继续运行,持久地为用户提供服务,直到用户主动关闭该应用程序。
前台Service也是应用程序处于后台时保活的重要技术手段。同时,为了防止某些应用程序利用前台Service恶意侵占系统资源,威胁用户的信息安全,非法监控用户的行为,前台Service必须显示在通知栏中,保持对用户可见。
com.minwei.foregroundservice右键
New
Ability
Empty Service Ability
Service Name: ServiceAbility
Package name: com.minwei.foregroundservice
Enable background mode: On <- 在后台保持运行的Service叫做前台Service
选择应用场景:On
自动生成entry\src\main\java\com\minwei\foregroundservice\ServiceAbility.java文件,其中包含Ability类的子类ServiceAbility:
public class ServiceAbility extends Ability { @Override public void onStart(Intent intent) { super.onStart(intent); } @Override public void onCommand(Intent intent, boolean restart, int startId) { } @Override public IRemoteObject onConnect(Intent intent) { return null; } @Override public void onDisconnect(Intent intent) { } @Override public void onBackground() { super.onBackground(); } @Override public void onStop() { super.onStop(); } }
同时自动在config.json中添加相关配置:
{ "backgroundModes": [ "dataTransfer" <- 在后台保持运行的Service叫做前台Service ], "name": "com.minwei.foregroundservice.ServiceAbility", "icon": "$media:icon", "description": "$string:serviceability_description", "type": "service" <- 这是一个服务,与一般应用"page"不同 }
在module中添加"保持后台运行"权限请求:
"reqPermissions": [ { "name": "ohos.permission.KEEP_BACKGROUND_RUNNING" } ]
在ServiceAbility类的onStart()方法中发布通知并保持后台运行:
NotificationNormalContent normal = new NotificationNormalContent() .setTitle("前台服务") .setText("我还在运行……") .setAdditionalText("常驻后台"); NotificationContent content = new NotificationContent(normal); NotificationRequest request = new NotificationRequest(1001) .setContent(content); keepBackgroundRunning(1001, request);
启动服务:
Intent intent = new Intent(); Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName(getBundleName()) .withAbilityName(ServiceAbility.class.getName()) .build(); intent.setOperation(operation); startAbility(intent);
停止服务:
Intent intent = new Intent(); Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName(getBundleName()) .withAbilityName(ServiceAbility.class.getName()) .build(); intent.setOperation(operation); stopAbility(intent);
例程:ForegroundService
...\ForegroundService\entry\src\main\config.json
{ ... "module": { ... "reqPermissions": [ { "name": "ohos.permission.KEEP_BACKGROUND_RUNNING" } ], ... } }
...\ForegroundService\entry\src\main\resources\base\graphic\background_button_start.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <corners ohos:radius="100"/> <solid ohos:color="#00a2e8"/> </shape>
...\ForegroundService\entry\src\main\resources\base\graphic\background_button_stop.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <corners ohos:radius="100"/> <solid ohos:color="#22b14c"/> </shape>
...\ForegroundService\entry\src\main\resources\base\graphic\background_toast_dialog.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <solid ohos:color="#80000000"/> <corners ohos:radius="12vp"/> </shape>
...\ForegroundService\entry\src\main\resources\base\layout\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:left_padding="60vp" ohos:right_padding="60vp" ohos:alignment="center" ohos:orientation="vertical"> <Button ohos:id="$+id:btnStart" ohos:height="match_content" ohos:width="match_parent" ohos:padding="$ohos:float:button_radius" ohos:background_element="$graphic:background_button_start" ohos:text="启动服务" ohos:text_size="28fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnStop" ohos:height="match_content" ohos:width="match_parent" ohos:padding="$ohos:float:button_radius" ohos:top_margin="20vp" ohos:background_element="$graphic:background_button_stop" ohos:text="停止服务" ohos:text_size="28fp" ohos:text_color="#ffffff" /> </DirectionalLayout>
...\ForegroundService\entry\src\main\resources\base\layout\toast_dialog.xml
<?xml version="1.0" encoding="utf-8"?> <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="match_content" ohos:width="match_content" ohos:left_padding="20vp" ohos:top_padding="5vp" ohos:right_padding="20vp" ohos:bottom_padding="5vp" ohos:background_element="$graphic:background_toast_dialog"> <Text ohos:id="$+id:txt" ohos:height="match_content" ohos:width="match_content" ohos:text_size="20fp" ohos:text_color="#ffffff" /> </DirectionalLayout>
...\ForegroundService\entry\src\main\java\com\minwei\foregroundservice\ServiceAbility.java
public class ServiceAbility extends Ability { private static final HiLogLabel label = new HiLogLabel( HiLog.LOG_APP, 0x00101, ServiceAbility.class.getCanonicalName()); @Override public void onStart(Intent intent) { super.onStart(intent); HiLog.info(label, "onStart()"); NotificationNormalContent normal = new NotificationNormalContent() .setTitle("前台服务") .setText("保持运行……") .setAdditionalText("常驻后台"); NotificationContent content = new NotificationContent(normal); NotificationRequest request = new NotificationRequest(1001) .setContent(content); keepBackgroundRunning(1001, request); } @Override public void onCommand(Intent intent, boolean restart, int startId) { HiLog.info(label, "onCommand()"); } @Override public IRemoteObject onConnect(Intent intent) { HiLog.info(label, "onConnect()"); return null; } @Override public void onDisconnect(Intent intent) { HiLog.info(label, "onDisconnect()"); } @Override public void onBackground() { super.onBackground(); HiLog.info(label, "onBackground()"); } @Override public void onStop() { super.onStop(); HiLog.info(label, "onStop()"); } }
...\ForegroundService\entry\src\main\java\com\minwei\foregroundservice\slice\MainAbilitySlice.java
public class MainAbilitySlice extends AbilitySlice implements ClickedListener { @Override public void onStart(Intent intent) { ... findComponentById(ResourceTable.Id_btnStart) .setClickedListener(this); findComponentById(ResourceTable.Id_btnStop) .setClickedListener(this); } ... @Override public void onClick(Component component) { switch (component.getId()) { case ResourceTable.Id_btnStart: startService(); break; case ResourceTable.Id_btnStop: stopService(); break; } } private void startService() { showToast("启动服务"); Intent intent = new Intent(); Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName(getBundleName()) .withAbilityName(ServiceAbility.class.getName()) .build(); intent.setOperation(operation); startAbility(intent); } private void stopService() { showToast("停止服务"); Intent intent = new Intent(); Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName(getBundleName()) .withAbilityName(ServiceAbility.class.getName()) .build(); intent.setOperation(operation); stopAbility(intent); } private void showToast(String text) { Component component = LayoutScatter.getInstance(this).parse( ResourceTable.Layout_toast_dialog, null, false); ((Text)component.findComponentById(ResourceTable.Id_txt)) .setText(text); new ToastDialog(this) .setContentCustomComponent(component) .setSize(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT) .setDuration(5000) .setAlignment(LayoutAlignment.BOTTOM) .setOffset(0, AttrHelper.vp2px(60, this)) .show(); } }
运行效果如下图所示:
创建基于JS UI的应用,修改UI。
创建服务:
com.minwei.computingservice右键
New
Ability
Empty Service Ability
Service Name: ServiceAbility
Package name: com.minwei.computingservice
Enable background mode: Off
定义远程代理类:
public class RemoteBroker extends RemoteObject implements IRemoteBroker { public RemoteBroker() { super(""); } @Override public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) { // 请求代码 switch (code) { case 1001: // 获取参数 ZSONObject params = ZSONObject.stringToZSON( data.readString()); int x = params.getInteger("x"); int y = params.getInteger("y"); // 加法计算 int z = x + y; // 输出结果 ZSONObject result = new ZSONObject(); result.put("code", 1002); result.put("z", z); reply.writeString(ZSONObject.toZSONString(result)); break; default: reply.writeString("无效的请求代码"); return false; } return true; } @Override public IRemoteObject asObject() { return this; } }
在ServiceAbility类的onConnect()方法中创建并返回远程代理对象:
public IRemoteObject onConnect(Intent intent) { return new RemoteBroker().asObject(); }
在index.js中调用ServiceAbility:
export default { data: { x: 123, y: 456, z: "?" }, async onClick() { var action = { "bundleName" : "com.minwei.computingservice", "abilityName" : "com.minwei.computingservice.ServiceAbility", "abilityType" : 0, "syncOption" : 0, "messageCode" : 1001, "data" : {"x": this.x, "y": this.y} }; var result = JSON.parse(await FeatureAbility.callAbility(action)); if (result.code == 1002) this.z = result.z; } }
例程:ComputingService
...\ComputingService\entry\src\main\js\default\pages\index\index.hml
<div class="container"> <text class="title" onclick="onClick" > {{ x }} + {{ y }} = {{ z }} </text> </div>
...\ComputingService\entry\src\main\js\default\pages\index\index.css
.container { flex-direction: column; justify-content: center; align-items: center; } .title { font-size: 40px; color: #00a2e8; opacity: 1.0; } @media screen and (device-type: tablet) and (orientation: landscape){ .title { font-size: 100px; } } @media screen and (device-type: wearable) { .title { font-size: 28px; color: #FFFFFF; } } @media screen and (device-type: tv) { .container { background-image: url("../../common/images/Wallpaper.png"); background-size: cover; background-repeat: no-repeat; background-position: center; } .title { font-size: 100px; color: #FFFFFF; } } @media screen and (device-type: phone) and (orientation: landscape) { .title { font-size: 60px; } }
...\ComputingService\entry\src\main\js\default\pages\index\index.js
export default { data: { x: 123, y: 456, z: "?" }, async onClick() { var action = { "bundleName" : "com.minwei.computingservice", "abilityName" : "com.minwei.computingservice.ServiceAbility", "abilityType" : 0, "syncOption" : 0, "messageCode" : 1001, "data" : {"x": this.x, "y": this.y} }; var result = JSON.parse(await FeatureAbility.callAbility(action)); if (result.code == 1002) this.z = result.z; } }
...\ComputingService\entry\src\main\java\com\minwei\computingservice\RemoteBroker.java
public class RemoteBroker extends RemoteObject implements IRemoteBroker { public RemoteBroker() { super(""); } @Override public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) { // 请求代码 switch (code) { case 1001: // 获取参数 ZSONObject params = ZSONObject.stringToZSON( data.readString()); int x = params.getInteger("x"); int y = params.getInteger("y"); // 加法计算 int z = x + y; // 输出结果 ZSONObject result = new ZSONObject(); result.put("code", 1002); result.put("z", z); reply.writeString(ZSONObject.toZSONString(result)); break; default: reply.writeString("无效的请求代码"); return false; } return true; } @Override public IRemoteObject asObject() { return this; } }
...\ComputingService\entry\src\main\java\com\minwei\computingservice\ServiceAbility.java
public class ServiceAbility extends Ability { private static final HiLogLabel label = new HiLogLabel( HiLog.LOG_APP, 0x00101, ServiceAbility.class.getCanonicalName()); @Override public void onStart(Intent intent) { super.onStart(intent); HiLog.info(label, "onStart()"); } @Override public void onCommand(Intent intent, boolean restart, int startId) { HiLog.info(label, "onCommand()"); } @Override public IRemoteObject onConnect(Intent intent) { HiLog.info(label, "onConnect()"); return new RemoteBroker().asObject(); } @Override public void onDisconnect(Intent intent) { HiLog.info(label, "onDisconnect()"); } @Override public void onBackground() { super.onBackground(); HiLog.info(label, "onBackground()"); } @Override public void onStop() { super.onStop(); HiLog.info(label, "onStop()"); } }
运行效果如下图所示:
打开和关闭远程设备上的FA与操作本地FA的方法基本相同,需要为Intent对象设置参数,并分别通过startAbility()和stopAbility()方法打开和关闭FA。但是,操作远程设备上的FA需要额外注意以下三点:
Intent intent = new Intent(); Operation operation = new Intent.OperationBuilder() .withDeviceId(<远程设备的UDID>) .withBundleName(<包名>) .withAbilityName(<FA名>) .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) .build(); intent.setOperation(operation); startAbility(intent);
Intent intent = new Intent(); Operation operation = new Intent.OperationBuilder() .withDeviceId(<远程设备的UDID>) .withBundleName(<包名>) .withAbilityName(<FA名>) .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) .build(); intent.setOperation(operation); stopAbility(intent);
应用迁移无需借助Intent对象,可以将设备中的某个FA直接迁移到另一个设备上运行,是应用程序流转的主要实现方法。
与FA一样,调用远程设备上的ServiceAbility也需要Intent对象参与。不同的是,不仅可以通过startAbility()和stopAbility()方法打开和关闭远程ServiceAbility,还可以通过connectAbility()和disconnectAbility()方法连接和断开远程ServiceAbility。操作远程设备上的ServiceAbility需要额外注意以下三点:
调用远程设备上的DataAbility无需借助Intent对象,只要将远程设备的UDID加入访问DataAbility的URI中即可。操作远程设备上的DataAbility需要额外注意以下两点:
无论流转还是协同,都需要目标设备(设备2)上装有能够处理特定数据的适当应用。如果该设备上没有该应用,则需要从应用商店下载。
因此运行在目标设备上的,参与流转或协同的应用,其字节数最好不要超过10M,以提供更加流畅的用户体验。
能够实现分布式组网的设备必须满足以下三个条件:
华为账号
_____________|_____________
/ | \
手表 <-蓝牙-> 手机 <-WIFI-> 智慧屏
\_____________|_____________/
|
基于分布式软总线多设备组网
List<DeviceInfo> devices = DeviceManager.getDeviceList( DeviceInfo.FLAG_GET_ONLINE_DEVICE); for (DeviceInfo device : devices) { HiLog.info(label, "Device ID : " + device.getDeviceID()); HiLog.info(label, "Device Name : " + device.getDeviceName()); HiLog.info(label, "Device Type : " + device.getDeviceType()); HiLog.info(label, "Device State : " + device.getDeviceState()); }
能够获取远程设备信息的应用程序,必须具备GET_DISTRIBUTED_DEVICE_INFO权限。该权限为非敏感权限,在config.json文件中配置即可。
运行在源设备中应用程序,通过Ability或AbilitySlice上下文对象的如下方法,即可实现应用迁移:
可被迁移FA的Ability和AbilitySlice需要实现IAbilityContinuation接口。
例程:ContinueAbility
...\ContinueAbility\entry\src\main\config.json
{ ... "module": { ... "reqPermissions": [ { "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO" }, { "name": "ohos.permission.DISTRIBUTED_DATASYNC" } ] } }
...\ContinueAbility\entry\src\main\resources\base\graphic\background_button_continue.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <corners ohos:radius="100"/> <solid ohos:color="#00a2e8"/> </shape>
...\ContinueAbility\entry\src\main\resources\base\layout\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:left_padding="80vp" ohos:right_padding="80vp" ohos:alignment="center" ohos:orientation="vertical"> <Text ohos:id="$+id:txt" ohos:height="match_content" ohos:width="match_content" ohos:text_size="60fp" ohos:text_color="#ff7f27" /> <Button ohos:id="$+id:btn" ohos:height="match_content" ohos:width="match_parent" ohos:padding="$ohos:float:button_radius" ohos:top_margin="20vp" ohos:background_element="$graphic:background_button_continue" ohos:text="迁移" ohos:text_size="28fp" ohos:text_color="#ffffff" /> </DirectionalLayout>
...\ContinueAbility\entry\src\main\java\com\minwei\continueability\MainAbility.java
public class MainAbility extends Ability implements IAbilityContinuation { ... @Override public boolean onStartContinuation() { return true; } @Override public boolean onSaveData(IntentParams intentParams) { return true; } @Override public boolean onRestoreData(IntentParams intentParams) { return true; } @Override public void onCompleteContinuation(int i) { } }
...\ContinueAbility\entry\src\main\java\com\minwei\continueability\slice\MainAbilitySlice.java
public class MainAbilitySlice extends AbilitySlice implements IAbilityContinuation { private int data = 1; @Override public void onStart(Intent intent) { ... ((Text)findComponentById(ResourceTable.Id_txt)) .setText(String.valueOf(data)); requestPermissions(); findComponentById(ResourceTable.Id_btn) .setClickedListener(component -> { List<String> deviceIds = getDeviceIds(); if (!deviceIds.isEmpty()) continueAbility(deviceIds.get(0)); }); } ... @Override public boolean onStartContinuation() { return true; } @Override public boolean onSaveData(IntentParams intentParams) { intentParams.setParam("data", data); return true; } @Override public boolean onRestoreData(IntentParams intentParams) { data = (int)intentParams.getParam("data") + 1; return true; } @Override public void onCompleteContinuation(int i) { terminateAbility(); } private void requestPermissions() { List<String> reqPermissions = new ArrayList<>(); String[] permissions = { "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO", "ohos.permission.DISTRIBUTED_DATASYNC"}; for (String permission : permissions) if (verifySelfPermission(permission) != 0 && canRequestPermission(permission)) reqPermissions.add(permission); requestPermissionsFromUser( reqPermissions.toArray(new String[0]), 0); } private List<String> getDeviceIds() { List<String> deviceIds = new ArrayList<>(); List<DeviceInfo> devices = DeviceManager.getDeviceList( DeviceInfo.FLAG_GET_ONLINE_DEVICE); for (DeviceInfo device : devices) deviceIds.add(device.getDeviceId()); return deviceIds; } }
运行效果如下图所示: