Service Ability
文章目录
Service Ability基本概念
基于Service模板的Ability(以下简称“Service”)主要用于后台运行任务(如执行音乐播放、文件下载等),但不提供用户交互界面。Service可由其他应用或Ability启动,即使用户切换到其他应用,Service仍将在后台继续运行。
Service是单实例的。在一个设备上,相同的Service只会存在一个实例。如果多个Ability共用这个实例,只有当与Service绑定的所有Ability都退出后,Service才能够退出。由于Service是在主线程里执行的,因此,如果在Service里面的操作时间过长,开发者必须在Service里创建新的线程来处理(详见线程间通信),和后台程序使用异步是一个道理,防止造成主线程阻塞,应用程序无响应。
创建Service
创建Ability的子类
实现Service相关的生命周期方法。Service也是一种Ability,Ability为Service提供了以下生命周期方法,开发者可以重写这些方法,来添加其他Ability请求与Service Ability交互时的处理方法。
-
onStart()
该方法在创建Service的时候调用,用于Service的初始化。在Service的整个生命周期只会调用一次,调用时传入的Intent应为空。
-
onCommand()
在Service创建完成之后调用,该方法在客户端每次启动该Service时都会调用,开发者可以在该方法中做一些调用统计、初始化类的操作。
-
onConnect()
在Ability和Service连接时调用,该方法返回IRemoteObject对象,开发者可以在该回调函数中生成对应Service的IPC通信通道,以便Ability与Service交互。Ability可以多次连接同一个Service,系统会缓存该Service的IPC通信对象,只有第一个客户端连接Service时,系统才会调用Service的onConnect方法来生成IRemoteObject对象,而后系统会将同一个RemoteObject对象传递至其他连接同一个Service的所有客户端,而无需再次调用onConnect方法。
-
onDIsconnect()
在Ability与绑定的Service断开连接时调用。
-
onStop()
在Service销毁时调用。Service应通过实现此方法来清理任何资源,如关闭线程、注册的侦听器等
package com.example.harmonyos02createservice;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.rpc.IRemoteObject;
public class ServiceAbility extends Ability {
/**
* 该方法在创建Service的时候调用,用于Service的初始化。在Service的整个生命周期只会调用一次,调用时传入的Intent应为空
* @param intent
*/
@Override
protected void onStart(Intent intent) {
super.onStart(intent);
}
/**
* 在Service销毁时调用。Service应通过实现此方法来清理任何资源,如关闭线程、注册的侦听器等
*/
@Override
protected void onStop() {
super.onStop();
}
/**
* 在**Ability和Service连接时**调用,该方法**返回IRemoteObject对象**,
* 开发者可以在该回调函数中**生成对应Service的IPC通信通道**,以便Ability与Service交互。
* Ability可以多次连接同一个Service,系统会缓存该Service的IPC通信对象,只有第一个客户端连接Service时,
* 系统才会调用Service的onConnect方法来生成IRemoteObject对象,
* 而后系统会将同一个RemoteObject对象传递至其他连接同一个Service的所有客户端,而无需再次调用onConnect方法
* @param intent
* @return
*/
@Override
protected IRemoteObject onConnect(Intent intent) {
return super.onConnect(intent);
}
/**
* 在Ability与绑定的Service断开连接时调用。
* @param intent
*/
@Override
protected void onDisconnect(Intent intent) {
super.onDisconnect(intent);
}
/**
* 在Service**创建完成之后**调用,该方法在客户端**每次启动该Service时都会调用**,
* 开发者可以在该方法中做一些调用统计、初始化类的操作。
* @param intent
* @param restart
* @param startId
*/
@Override
protected void onCommand(Intent intent, boolean restart, int startId) {
super.onCommand(intent, restart, startId);
}
}
注册Service
我们每写一个Ability都需要到对应的配置文件注册对应的Ability信息,我们在写service的ability信息的时候,type记得要设置为service
{
...,
"module": {
...,
"abilities": [
...,
{
"icon": "$media:icon",
"name": ".ServiceAbility",
"visible": true,
"description": "创建Service",
"type": "service"
}
]
}
}
其中值得一提的是这个visible属性,它代表着这个Ability是否可以被别的应用调用
启动Service
介绍通过startAbility()启动Service以及对应的停止方法
启动Service
Ability为开发者提供了startAbility()方法来启动另外一个Ability。因为Service也是Ability的一种,开发者同样可以通过将Intent传递给该方法来启动Service。不仅支持启动本地Service,还支持启动远程Service。
-
DeviceId
表示设备ID。如果是本地设备,则可以直接留空;如果是远程设备,可以通过ohos.distributedschedule.interwork.DeviceManager提供的getDeviceList(int flag)获取设备列表
参考API:https://developer.harmonyos.com/cn/docs/documentation/doc-references/devicemanager-0000001054358820#ZH-CN_TOPIC_0000001054358820__getDeviceList-int-
首先本应用需要有
-
BundleName
表示包名称
-
AbilityName
表示待启动的Ability名称
示例:
-
启动本地设备Service
Intent intent = new Intent(); Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName("com.example.harmonyos02createservice") .withAbilityName("com.example.harmonyos02createservice.ServiceAbility") .build(); intent.setOperation(operation); startAbility(intent);
本地设备的Service不需要deviceId,所以我这里我们设置为"",剩下的两个属性就是需要启动的Service的两个属性
-
启动远程设备Service
-
需要获取远程设备的信息,我们需要给予权限ohos.permission.GET_DISTRIBUTED_DEVICE_INFO
{ ..., "module": { ..., "reqPermissions": [ { "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO" } ] } }
-
我们通过ohos.distributedschedule.interwork.DeviceManager提供的getDeviceList(int flag)来获取设备信息
package com.example.harmonyos02startremoteservice; import com.example.harmonyos02startremoteservice.slice.MainAbilitySlice; import ohos.aafwk.ability.Ability; import ohos.aafwk.content.Intent; import ohos.distributedschedule.interwork.DeviceInfo; import java.util.List; public class MainAbility extends Ability { @Override public void onStart(Intent intent) { super.onStart(intent); super.setMainRoute(MainAbilitySlice.class.getName()); getDeviceInfo(); } public void getDeviceInfo(){ System.out.println("start get deviceInfos..."); List<DeviceInfo> deviceInfos=ohos.distributedschedule.interwork.DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE); System.out.println("finish"); System.out.println("deviceInfos:"); for(DeviceInfo deviceInfo:deviceInfos ) { System.out.println("device id \t: "+deviceInfo.getDeviceId()); System.out.println("device name \t: "+deviceInfo.getDeviceName()); System.out.println("device state \t: "+deviceInfo.getDeviceState()); System.out.println("device type \t: "+deviceInfo.getDeviceType()); } if(deviceInfos.isEmpty()){ System.out.println("empty"); } } }
我们不难发现getDeviceList方法中得flag参数的含义是获取在线的设备,这里我们顺带提一下这里这个flag有3个选择
- DeviceInfo.FLAG_GET_ALL_DEVICE 获取全部设备
- DeviceInfo.FLAG_GET_ONLINE_DEVICE 获取在线设备
- DeviceInfo.FLAG_GET_OFFLINE_DEVICE 获取离线设备
-
我们测试一下单设备模式下运行这个app
-
接下来我们试着在多设备模式下运行这个app
-
接下来我们要开始启动远程的服务
这里我们沿用官网文档中的代码
Intent intent = new Intent(); Operation operation = new Intent.OperationBuilder() .withDeviceId("deviceId") .withBundleName("com.domainname.hiworld.himusic") .withAbilityName("com.domainname.hiworld.himusic.ServiceAbility") .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) // 设置支持分布式调度系统多设备启动的标识 .build(); intent.setOperation(operation); startAbility(intent);
这里的deviceId就填写deviceInfos里头的device id
-
停止Service
Service一旦创建就会一直保持在后台运行,除非必须回收内存资源,否则系统不会停止或销毁Service。开发者可以在Service中通过terminateAbility()停止本Service或在其他Ability调用stopAbility()来停止Service。停止Service同样支持停止本地设备Service和停止远程设备Service,使用方法与启动Service一样。一旦调用停止Service的方法,系统便会尽快销毁Service。
连接Service
如果Service需要与Page Ability或其他应用的Service Ability进行交互,则须创建用于连接的Connection。Service支持其他Ability通过connectAbility()方法与其进行连接。
在使用connectAbility()处理回调时,需要传入目标Service的Intent与IAbilityConnection的实例。IAbilityConnection提供了两个方法供开发者实现:onAbilityConnectDone()是用来处理连接Service成功的回调,onAbilityDisconnectDone()是用来处理Service异常死亡的回调。
// 创建连接Service回调实例
private IAbilityConnection connection = new IAbilityConnection() {
// 连接到Service的回调
@Override
public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int resultCode) {
// Client侧需要定义与Service侧相同的IRemoteObject实现类。开发者获取服务端传过来IRemoteObject对象,并从中解析出服务端传过来的信息。
}
// Service异常死亡的回调
@Override
public void onAbilityDisconnectDone(ElementName elementName, int resultCode) {
}
};
连接Service的代码示例如下
// 连接Service
Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
.withDeviceId("deviceId")
.withBundleName("com.domainname.hiworld.himusic")
.withAbilityName("com.domainname.hiworld.himusic.ServiceAbility")
.build();
intent.setOperation(operation);
connectAbility(intent, connection);
同时,Service侧也需要在onConnect()时返回IRemoteObject,从而定义与Service进行通信的接口。onConnect()需要返回一个IRemoteObject对象,HarmonyOS提供了IRemoteObject的默认实现,用户可以通过继承LocalRemoteObject来创建自定义的实现类。Service侧把自身的实例返回给调用侧的代码示例如下:
// 创建自定义IRemoteObject实现类
private class MyRemoteObject extends LocalRemoteObject {
MyRemoteObject(){
}
}
// 把IRemoteObject返回给客户端
@Override
protected IRemoteObject onConnect(Intent intent) {
return new MyRemoteObject();
}
Service Ability生命周期
与Page类似,Service也拥有生命周期。根据调用方法的不同,其生命周期有以下两种路径:
-
启动Service
该Service在其他Ability调用startAbility()时创建,然后保持运行。其他Ability通过调用stopAbility()来停止Service,Service停止后,系统会将其销毁。
-
连接Service
该Service在其他Ability调用connectAbility()时创建,客户端可通过调用disconnectAbility()断开连接。多个客户端可以绑定到相同Service,而且当所有绑定全部取消后,系统即会销毁该Service。
connectAbility()也可以连接通过startAbility()创建的Service。
前台Service
一般情况下,Service都是在后台运行的,后台Service的优先级都是比较低的,当资源不足时,系统有可能回收正在运行的后台Service。
在一些场景下(如播放音乐),用户希望应用能够一直保持运行,此时就需要使用前台Service。前台Service会始终保持正在运行的图标在系统状态栏显示。
使用前台Service并不复杂,开发者只需在Service创建的方法里,调用keepBackgroundRunning()将Service与通知绑定。调用keepBackgroundRunning()方法前需要在配置文件中声明ohos.permission.KEEP_BACKGROUND_RUNNING权限,同时还需要在配置文件中添加对应的backgroundModes参数。在onStop()方法中调用cancelBackgroundRunning()方法可停止前台Service。
然后需要用到该服务就用到刚刚提到的启动或者连接都可以啦,关闭的话就stop或者disconnect
package com.example.harmonyos02serviceability;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.event.notification.NotificationRequest;
import ohos.rpc.IRemoteObject;
import ohos.rpc.RemoteObject;
public class ForegroundServiceAbility extends Ability {
private static final int NOTIFICATION_ID = 0XD0000002;
private static class ServiceRemoteObject extends RemoteObject {
private ServiceRemoteObject() {
super("RemoteObject");
}
}
@Override
protected void onStart(Intent intent) {
startForeground();
super.onStart(intent);
}
@Override
protected void onStop() {
super.onStop();
cancelBackgroundRunning();
}
@Override
protected IRemoteObject onConnect(Intent intent) {
return new ServiceRemoteObject();
}
private void startForeground() {
NotificationRequest request = new NotificationRequest(NOTIFICATION_ID).setTapDismissed(true);
NotificationRequest.NotificationNormalContent content = new NotificationRequest.NotificationNormalContent();
content.setTitle("Foreground Service").setText("I'm a Foreground Service");
NotificationRequest.NotificationContent notificationContent = new NotificationRequest.NotificationContent(
content);
request.setContent(notificationContent);
keepBackgroundRunning(NOTIFICATION_ID, request);
}
}
值得注意的是,出了作为一个service ability需要注册到config.json里面以外,还需要在配置文件中声明ohos.permission.KEEP_BACKGROUND_RUNNING权限,以及给该ability添加一个backgroundModes属性
{
...,
"module": {
...,
"abilities": [
...,
{
"name": "com.example.harmonyos02serviceability.ForegroundServiceAbility",
"type": "service",
"backgroundModes": ["dataTransfer", "location"]
}
],
"reqPermissions": [
...,
{
"name": "ohos.permission.KEEP_BACKGROUND_RUNNING"
}
]
}
}
测试一下
确实完成了
汇总测试
我们做一个demo可以让整个Service Ability的这些功能都包含在其中
-
编写前端页面
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:alignment="center" ohos:orientation="vertical"> <Button ohos:id="$+id:buttonToStartLS" ohos:height="match_content" ohos:width="match_content" ohos:background_element="$graphic:blue_button" ohos:padding="8vp" ohos:left_padding="20vp" ohos:right_padding="20vp" ohos:text_color="#fff" ohos:text="start local service" ohos:text_size="32vp" ohos:bottom_margin="10vp"/> <Button ohos:id="$+id:buttonToStopLS" ohos:height="match_content" ohos:width="match_content" ohos:background_element="$graphic:blue_button" ohos:padding="8vp" ohos:left_padding="20vp" ohos:right_padding="20vp" ohos:text_color="#fff" ohos:text="stop local service" ohos:text_size="32vp" ohos:bottom_margin="10vp"/> <Button ohos:id="$+id:buttonToCLS" ohos:height="match_content" ohos:width="match_content" ohos:background_element="$graphic:green_button" ohos:padding="8vp" ohos:left_padding="20vp" ohos:right_padding="20vp" ohos:text_color="#fff" ohos:text="connect local service" ohos:text_size="26vp" ohos:bottom_margin="10vp"/> <Button ohos:id="$+id:buttonToDLS" ohos:height="match_content" ohos:width="match_content" ohos:background_element="$graphic:green_button" ohos:padding="8vp" ohos:left_padding="20vp" ohos:right_padding="20vp" ohos:text_color="#fff" ohos:text="disconnect local service" ohos:text_size="26vp" ohos:bottom_margin="10vp"/> <Button ohos:id="$+id:buttonToStartRS" ohos:height="match_content" ohos:width="match_content" ohos:background_element="$graphic:yellow_button" ohos:padding="8vp" ohos:left_padding="20vp" ohos:right_padding="20vp" ohos:text_color="#fff" ohos:text="start remote service" ohos:text_size="32vp" ohos:bottom_margin="10vp"/> <Button ohos:id="$+id:buttonToStopRS" ohos:height="match_content" ohos:width="match_content" ohos:background_element="$graphic:yellow_button" ohos:padding="8vp" ohos:left_padding="20vp" ohos:right_padding="20vp" ohos:text_color="#fff" ohos:text="stop remote service" ohos:text_size="32vp" ohos:bottom_margin="10vp"/> <Button ohos:id="$+id:buttonToCRS" ohos:height="match_content" ohos:width="match_content" ohos:background_element="$graphic:red_button" ohos:padding="8vp" ohos:left_padding="20vp" ohos:right_padding="20vp" ohos:text_color="#fff" ohos:text="connect remote service" ohos:text_size="26vp" ohos:bottom_margin="10vp"/> <Button ohos:id="$+id:buttonToDRS" ohos:height="match_content" ohos:width="match_content" ohos:background_element="$graphic:red_button" ohos:padding="8vp" ohos:left_padding="20vp" ohos:right_padding="20vp" ohos:text_color="#fff" ohos:text="disconnect remote service" ohos:text_size="26vp" ohos:bottom_margin="10vp"/> </DirectionalLayout>
graphic/blue_button.xml
<?xml version="1.0" encoding="UTF-8" ?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <solid ohos:color="#409eff"/> <corners ohos:radius="75"/> </shape>
graphic/yellow_button.xml
<?xml version="1.0" encoding="UTF-8" ?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <solid ohos:color="#FFF8D24F"/> <corners ohos:radius="75"/> </shape>
graphic/red_button.xml
<?xml version="1.0" encoding="UTF-8" ?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <solid ohos:color="#FFF36C91"/> <corners ohos:radius="75"/> </shape>
graphic/green_button.xml
<?xml version="1.0" encoding="UTF-8" ?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <solid ohos:color="#FF35F642"/> <corners ohos:radius="75"/> </shape>
-
新建若干个新的Ability,方便表示不同情况下启动的Service Ability
-
LocalServiceAbility
package com.example.harmonyos02serviceability; import ohos.aafwk.ability.Ability; import ohos.aafwk.ability.LocalRemoteObject; import ohos.aafwk.content.Intent; import ohos.aafwk.content.Operation; import ohos.agp.components.Button; import ohos.agp.window.dialog.ToastDialog; import ohos.hiviewdfx.HiLog; import ohos.rpc.IRemoteObject; import ohos.rpc.MessageOption; import ohos.rpc.MessageParcel; import ohos.rpc.RemoteObject; public class LocalServiceAbility extends Ability { // 创建自定义IRemoteObject实现类 private class CurrentRemoteObject extends RemoteObject { private CurrentRemoteObject() { super("CurrentRemoteObject"); } @Override public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) { return true; } } @Override protected void onStart(Intent intent) { super.onStart(intent); showTip("LocalService onStart"); } @Override protected void onStop() { super.onStop(); showTip("LocalService onStop"); } @Override protected IRemoteObject onConnect(Intent intent) { showTip("LocalService onConnect"); return new CurrentRemoteObject(); } @Override protected void onDisconnect(Intent intent) { super.onDisconnect(intent); showTip("LocalService onDisconnect"); } @Override protected void onCommand(Intent intent, boolean restart, int startId) { super.onCommand(intent, restart, startId); showTip("LocalService onCommand"); } private void showTip(String message){ new ToastDialog(this).setText(message).show(); } }
-
RemoteServiceAbility
package com.example.harmonyos02serviceability; import ohos.aafwk.ability.Ability; import ohos.aafwk.content.Intent; import ohos.agp.window.dialog.ToastDialog; import ohos.rpc.*; public class RemoteServiceAbility extends Ability { private class CurrentRemoteObject extends RemoteObject { private CurrentRemoteObject() { super("CurrentRemoteObject"); } @Override public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) { return true; } } @Override protected void onStart(Intent intent) { super.onStart(intent); showTip("RemoteService onStart"); } @Override protected void onStop() { super.onStop(); showTip("RemoteService onStop"); } @Override protected IRemoteObject onConnect(Intent intent) { showTip("RemoteService onConnect"); return new CurrentRemoteObject(); } @Override protected void onDisconnect(Intent intent) { super.onDisconnect(intent); showTip("RemoteService onDisconnect"); } @Override protected void onCommand(Intent intent, boolean restart, int startId) { super.onCommand(intent, restart, startId); showTip("RemoteService onCommand"); } private void showTip(String message){ new ToastDialog(this).setText(message).show(); } }
两个几乎都没有什么区别,但是这个CurrentRemoteObject是必不可少的,不然connectAbility会回调的是onAbilityDisconnectDone方法,也就是Service异常死亡的回调方法。
-
-
config.json也要做好相应的配置
{ "app": { "bundleName": "com.example.harmonyos02serviceability", ... }, ..., "module": { ..., "abilities": [ ..., { "name": "com.example.harmonyos02serviceability.LocalServiceAbility", "description": "LocalServiceAbility", "type": "service" }, { "name": "com.example.harmonyos02serviceability.RemoteServiceAbility", "description": "RemoteServiceAbility", "visible": true, "type": "service" } ] } }
-
写好AbilitySlice对应的Local Service 部分和Remote Service 部分
package com.example.harmonyos02serviceability.slice; import com.example.harmonyos02serviceability.ResourceTable; import ohos.aafwk.ability.AbilitySlice; import ohos.aafwk.ability.IAbilityConnection; import ohos.aafwk.content.Intent; import ohos.aafwk.content.Operation; import ohos.agp.components.Button; import ohos.agp.window.dialog.ToastDialog; import ohos.bundle.ElementName; import ohos.bundle.IBundleManager; import ohos.distributedschedule.interwork.DeviceInfo; import ohos.rpc.IRemoteObject; import ohos.security.SystemPermission; import java.util.List; public class MainAbilitySlice extends AbilitySlice { //constant //Copying in config.json is more accurate final private String BUNDLE_NAME = "com.example.harmonyos02serviceability"; final private String LOCAL_SERVICE_ABILITY_NAME = "com.example.harmonyos02serviceability.LocalServiceAbility"; final private String REMOTE_SERVICE_ABILITY_NAME = "RemoteServiceAbility"; @Override public void onStart(Intent intent) { super.onStart(intent); super.setUIContent(ResourceTable.Layout_ability_main); initComponent(); } @Override public void onActive() { super.onActive(); } @Override public void onForeground(Intent intent) { super.onForeground(intent); } public void initComponent(){ //initialization component Button buttonToStartLS = (Button)findComponentById(ResourceTable.Id_buttonToStartLS); Button buttonToStopLS = (Button)findComponentById(ResourceTable.Id_buttonToStopLS); Button buttonToStartRS = (Button)findComponentById(ResourceTable.Id_buttonToStartRS); Button buttonToStopRS = (Button)findComponentById(ResourceTable.Id_buttonToStopRS); Button buttonToCLS = (Button)findComponentById(ResourceTable.Id_buttonToCLS); Button buttonToDLS = (Button)findComponentById(ResourceTable.Id_buttonToDLS); Button buttonToCRS = (Button)findComponentById(ResourceTable.Id_buttonToCRS); Button buttonToDRS = (Button)findComponentById(ResourceTable.Id_buttonToDRS); //initialization event buttonToStartLS.setClickedListener(component -> startLocalService()); buttonToStopLS.setClickedListener(component -> stopLocalService()); buttonToStartRS.setClickedListener(component -> startRemoteService()); buttonToStopRS.setClickedListener(component -> stopRemoteService()); buttonToCLS.setClickedListener(component -> connectLocalService()); buttonToDLS.setClickedListener(component -> disconnectLocalService()); buttonToCRS.setClickedListener(component -> connectRemoteService()); buttonToDRS.setClickedListener(component -> disconnectRemoteService()); } // 创建连接Service回调实例 private final IAbilityConnection connection = new IAbilityConnection() { // 连接到Service的回调 @Override public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int resultCode) { // Client侧需要定义与Service侧相同的IRemoteObject实现类。开发者获取服务端传过来IRemoteObject对象,并从中解析出服务端传过来的信息。 showTip("service connect success"); } // Service异常死亡的回调 @Override public void onAbilityDisconnectDone(ElementName elementName, int resultCode) { showTip("service connect fail"); } }; private Intent getLocalServiceIntent(){ Intent intent = new Intent(); Operation operation = new Intent.OperationBuilder(). withBundleName(BUNDLE_NAME). withAbilityName(LOCAL_SERVICE_ABILITY_NAME). build(); intent.setOperation(operation); return intent; } public void startLocalService(){ startAbility(getLocalServiceIntent()); } public void stopLocalService(){ stopAbility(getLocalServiceIntent()); } public void connectLocalService(){ connectAbility(getLocalServiceIntent(),connection); } public void disconnectLocalService(){ disconnectAbility(connection); } public void connectRemoteService(){ connectAbility(getRemoteServiceIntent(),connection); } public void disconnectRemoteService(){ disconnectAbility(connection); } private String getRemoteDeviceId(){ List<DeviceInfo> deviceInfos=ohos.distributedschedule.interwork.DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE); if(deviceInfos.size()>0){ return deviceInfos.get(0).getDeviceId(); } return ""; } private Intent getRemoteServiceIntent(){ Intent intent = new Intent(); Operation operation = new Intent.OperationBuilder() .withDeviceId(getRemoteDeviceId()) .withBundleName(BUNDLE_NAME) .withAbilityName(REMOTE_SERVICE_ABILITY_NAME) .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) .build(); intent.setOperation(operation); return intent; } public void startRemoteService(){ startAbility(getRemoteServiceIntent()); } public void stopRemoteService(){ stopAbility(getRemoteServiceIntent()); } private void showTip(String message){ new ToastDialog(this).setText(message).show(); } }
-
值得注意的是Remote Service的一些操作是需要权限的,比如获取远程设备信息的GET_DISTRIBUTED_DEVICE_INFO,以及获得多设备协同权限的DISTRIBUTED_DATASYNC
{ ... "module": { ..., "reqPermissions": [ { "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO" }, { "name": "ohos.permission.DISTRIBUTED_DATASYNC" } ] } }
值得注意的是,DISTRIBUTED_DATASYNC权限需要用户手动授予,所以我们在应用打开时添加了权限授予的步骤
package com.example.harmonyos02serviceability; import com.example.harmonyos02serviceability.slice.MainAbilitySlice; import ohos.aafwk.ability.Ability; import ohos.aafwk.content.Intent; import ohos.bundle.IBundleManager; import ohos.security.SystemPermission; public class MainAbility extends Ability { @Override public void onStart(Intent intent) { super.onStart(intent); super.setMainRoute(MainAbilitySlice.class.getName()); requestPermission(); } private void requestPermission() { if (verifySelfPermission(SystemPermission.DISTRIBUTED_DATASYNC) != IBundleManager.PERMISSION_GRANTED) { requestPermissionsFromUser(new String[] {SystemPermission.DISTRIBUTED_DATASYNC}, 0); } } @Override public void onRequestPermissionsFromUserResult(int requestCode, String[] permissions, int[] grantResults) { if (permissions == null || permissions.length == 0 || grantResults == null || grantResults.length == 0) { return; } if (requestCode == 0) { if (grantResults[0] == IBundleManager.PERMISSION_DENIED) { terminateAbility(); } } } }
-
测试
这个测试确实不方便截图出来,但是在我的deveco studio 中是可以正常运行的