赞
踩
基于Service模板的Ability(简称“Service”)主要用于后台运行任务(如执行音乐播放、文件下载等),但不提供用户交互界面。Service可由其他应用或Ability启动,即使用户切换到其他应用,Service仍将在后台继续运行。
1.Service是单实例的。在一个设备上,相同的Service只会存在一个实例。
2.如果多个Ability共用这个实例,只有当与Service绑定的所有Ability都退出后,Service才能够退出。
3.由于Service是在主线程里执行的,因此,如果在Service里面的操作时间过长,必须在Service里创建新的线程来处理,防止造成主线程阻塞,应用程序无响应。
创建Ability的子类,实现Service相关的生命周期方法。Service也是一种Ability,Ability为Service提供了以下生命周期方法,用户可以重写这些方法来添加自己的处理。
包上右键选择New => Ability => Empty Service Ability创建一个ServiceAbility1
public class ServiceAbility1 extends Ability { private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001101, "ServiceAbility1"); @Override public void onStart(Intent intent) { HiLog.error(LABEL_LOG, "--------ServiceAbility1::onStart"); super.onStart(intent); } @Override public void onBackground() { super.onBackground(); HiLog.info(LABEL_LOG, "--------ServiceAbility1::onBackground"); } @Override public void onStop() { super.onStop(); HiLog.info(LABEL_LOG, "--------ServiceAbility1::onStop"); } /** * 每次调用ServiceAbility1都会执行该方法 * @param intent 数据载体 * @param restart 启动状态 true:销毁后启动,false:正常启动 * @param startId 启动的计数器 */ @Override public void onCommand(Intent intent, boolean restart, int startId) { HiLog.info(LABEL_LOG, "--------ServiceAbility1::onCommand"); System.out.println("数据载体:"+intent.getOperation().getAbilityName()); System.out.println("启动状态:"+restart); System.out.println("启动的计数器:"+startId); } @Override public IRemoteObject onConnect(Intent intent) { return null; } @Override public void onDisconnect(Intent intent) { } }
Service也需要在应用配置文件中进行注册,注册类型type需要设置为service。
{
"module": {
"abilities": [
{
"name": "com.example.myapplication.ServiceAbility1",
"icon": "$media:icon",
"description": "$string:serviceability1_description",
"type": "service"
},
]
...
}
...
}
Ability为开发者提供了startAbility()方法来启动另外一个Ability。因为Service也是Ability的一种,开发者同样可以通过将Intent传递给该方法来启动Service。不仅支持启动本地Service,还支持启动远程Service。
启动Service主要用于启动一个服务执行后台任务或者远程启动一个功能,不进行通信,如设备A让设备B播放音乐。
可以通过构造包含DeviceId、BundleName与AbilityName的Operation对象来设置目标Service信息。
DeviceId:表示设备ID。如果是本地设备,则可以直接留空;如果是远程设备,可以通过ohos.distributedschedule.interwork.DeviceManager提供的getDeviceList获取设备列表。
BundleName:表示包名称。
AbilityName:表示待启动的Ability名称。
Layout_ability_main布局xml
<?xml version="1.0" encoding="utf-8"?> <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:width="match_parent" ohos:height="match_parent" ohos:orientation="vertical"> <Button ohos:id="$+id:button1" ohos:width="match_content" ohos:height="match_content" ohos:background_element="$graphic:background_button" ohos:text="启动本地设备的Service" ohos:layout_alignment="horizontal_center" ohos:text_size="100"/> <Button ohos:id="$+id:button2" ohos:width="match_content" ohos:top_margin="100px" ohos:height="match_content" ohos:background_element="$graphic:background_button" ohos:text="停止本地设备的Service" ohos:layout_alignment="horizontal_center" ohos:text_size="100"/> </DirectionalLayout>
背景xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:shape="rectangle">
<corners
ohos:radius="10"/>
<solid
ohos:color="#007CFD"/>
</shape>
config.js配置分布式权限
"reqPermissions": [
{"name": "ohos.permission.DISTRIBUTED_DATASYNC"},
{"name": "ohos.permission.servicebus.ACCESS_SERVICE"},
{"name": "com.huawei.hwddmp.servicebus.BIND_SERVICE"},
{"name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE"},
{"name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO" },
{"name": "ohos.permission.GET_BUNDLE_INFO"}
]
显示声明需要使用的权限
public class MainAbility extends Ability {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setMainRoute(MainAbilitySlice.class.getName());
requestPermissionsFromUser(new String[]{"ohos.permission.DISTRIBUTED_DATASYNC",
"ohos.permission.servicebus.ACCESS_SERVICE",
"com.huawei.hwddmp.servicebus.BIND_SERVICE"}, 0);
}
public class MainAbilitySlice extends AbilitySlice { static final HiLogLabel LOG_LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00002, "MainAbilitySlice"); /** * 启动本地设备的Service * @param intent */ @Override public void onStart(Intent intent) { super.onStart(intent); super.setUIContent(ResourceTable.Layout_ability_main); Button button1 = (Button) findComponentById(ResourceTable.Id_button1); button1.setClickedListener(component -> { //构造包含DeviceId、BundleName与AbilityName的Operation对象来设置目标Service信息 Intent intent1 = new Intent(); Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName("com.example.myapplication") .withAbilityName("com.example.myapplication.ServiceAbility1") .build(); intent1.setOperation(operation); startAbility(intent1); }); } }
/** * 启动本地设备的Service * @param intent */ @Override public void onStart(Intent intent) { super.onStart(intent); super.setUIContent(ResourceTable.Layout_ability_main); //获取设备列表 List<DeviceInfo> deviceList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE); if(deviceList.isEmpty()){ return ; } 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()); }); //使用deviceIds的第一个元素,做为启动远程设备的目标id String devcieIdStr = deviceIds.get(0); Button button1 = (Button) findComponentById(ResourceTable.Id_button1); button1.setClickedListener(component -> { //构造包含DeviceId、BundleName与AbilityName的Operation对象来设置目标Service信息 Intent intent1 = new Intent(); Operation operation = new Intent.OperationBuilder() .withDeviceId(devcieIdStr) .withBundleName("com.example.myapplication") .withAbilityName("com.example.myapplication.ServiceAbility1") .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) // 设置支持分布式调度系统多设备启动的标识 .build(); intent1.setOperation(operation); startAbility(intent1); }); }
Ability将通过startAbility() 方法来启动Service。
如果Service尚未运行,则系统会先调用onStart()来初始化Service,再回调Service的onCommand()方法来启动Service。
如果Service正在运行,则系统会直接回调Service的onCommand()方法来启动Service。
Service一旦创建就会一直保持在后台运行,除非必须回收内存资源,否则系统不会停止或销毁Service。开发者可以在Service中通过terminateAbility()停止本Service或在其他Ability调用stopAbility()来停止Service。
停止Service同样支持停止本地设备Service和停止远程设备Service,使用方法与启动Service一样。一旦调用停止Service的方法,系统便会尽快销毁Service。
public class MainAbilitySlice extends AbilitySlice { static final HiLogLabel LOG_LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00002, "MainAbilitySlice"); /** * 停止本地设备的Service * @param intent */ @Override public void onStart(Intent intent) { HiLog.info(LOG_LABEL, "--------onStart2"); super.onStart(intent); super.setUIContent(ResourceTable.Layout_ability_main); Button button2 = (Button) findComponentById(ResourceTable.Id_button2); button2.setClickedListener(component -> { //构造包含DeviceId、BundleName与AbilityName的Operation对象来设置目标Service信息 Intent intent1 = new Intent(); ElementName elementName = new ElementName("","com.example.myapplication","ServiceAbility1"); intent1.setElement(elementName); stopAbility(intent1); }); } }
/** * 停止本地设备的Service * @param intent */ @Override public void onStart(Intent intent) { HiLog.info(LOG_LABEL, "--------onStart2"); super.onStart(intent); super.setUIContent(ResourceTable.Layout_ability_main); //获取设备列表 List<DeviceInfo> deviceList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE); if(deviceList.isEmpty()){ return ; } 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()); }); //使用deviceIds的第一个元素,做为启动远程设备的目标id String devcieIdStr = deviceIds.get(0); Button button2 = (Button) findComponentById(ResourceTable.Id_button2); button2.setClickedListener(component -> { Intent intent1 = new Intent(); ElementName elementName = new ElementName(devcieIdStr,"com.example.myapplication","ServiceAbility1"); intent1.setElement(elementName); intent1.setFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE); // 设置支持分布式调度系统多设备启动的标识 stopAbility(intent1); }); }
如果Service需要与Page Ability或其他应用的Service Ability进行交互,则应创建用于连接的Connection。Service支持其他Ability通过connectAbility()方法与其进行连接。
在使用connectAbility()处理回调时,需要传入目标Service的Intent与IAbilityConnection的实例。
IAbilityConnection提供了两个方法供开发者实现:
onAbilityConnectDone()用来处理连接的回调
onAbilityDisconnectDone()用来处理断开连接的回调。
链接与启动的不同在于链接Service后,可以进行通信,也就是需要链接的Service执行任务后返回数据,此时,就要用链接而不能是启动。
客户端可通过调用disconnectAbility()断开连接。
// 与远程PA断开连接
private ClickedListener mDisconnectRemotePAListener = new ClickedListener() {
@Override
public void onClick(Component arg0) {
disconnectAbility(conn);
}
在onAbilityConnectDone()中获取管理链接的代理,进一步为了使用该代理跨设备调度PA,开发者需要在本地及对端分别实现对外接口一致的代理。
发起连接端
package com.example.myapplication.connections; import ohos.rpc.*; public class StartRemoteProxy implements IRemoteBroker { //发起端与接受端之间的通信代码,有效的任意值 private static final int MSGCODE = IRemoteObject.MIN_TRANSACTION_ID; //通信成功的code值: 0 private static final int OK = 0; //获取远程代理对象的持有者 private final IRemoteObject remote; /** * 初始远端代理对象,使用onAbilityDisconnectDone接口方法返回来获取 * * @param remote */ public StartRemoteProxy(IRemoteObject remote) { this.remote = remote; } /** * 获取远程代理对象 * * @return */ @Override public IRemoteObject asObject() { return remote; } /** * Servcie Ability具有的test功能 * * @param text * @return */ public String test(String text) { //传出的数据 MessageParcel data = MessageParcel.obtain(); //返回一个空的MessageParcel对象 data.writeString(text); //远端返回的响应数据 MessageParcel reply = MessageParcel.obtain(); //制定信息传输的模式的option 同步 or 异步 MessageOption option = new MessageOption(MessageOption.TF_SYNC); String result = null; try { //最核心方法,实现调用Servcie Ability的功能 remote.sendRequest(MSGCODE, data, reply, option); //通信结束,响应结果存放在reply容器中 //判断通信状态 int ec = reply.readInt(); if (ec != OK) { throw new RemoteException(); } result = reply.readString(); } catch (RemoteException e) { e.printStackTrace(); } return result; } }
接受端
package com.example.myapplication.connections; import ohos.rpc.*; public class AcceptRemoteProxy extends RemoteObject implements IRemoteBroker { //发起端与接受端之间的通信代码,有效的任意值 private static final int MSGCODE = IRemoteObject.MIN_TRANSACTION_ID; //通信成功的code值 0 private static final int OK = 0; //通信失败的Code值 private static final int ERROR = -1; public AcceptRemoteProxy() { super("this is acceptRemoteProxy"); } @Override public IRemoteObject asObject() { return this; } /** * 接受端接受发送端的请求数据,并处理这些数据,返回返回响应 * * @param code 接受端与发送端间的通信code * @param data 发送端发送的数据 * @param reply 接受端响应的数据 * @param option 数据传输的模式 * @return * @throws RemoteException */ @Override public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) throws RemoteException { //发起端的通信code需与接受端通信code值一致 if (code != MSGCODE) { //通信失败 reply.writeInt(ERROR); return false; } //取发起端传递的参数 String readString = data.readString(); //设置返回发起端的响应数据 reply.writeInt(OK); reply.writeString(readString); return true; } }
一个PA ServiceAbility1 ,实例化一个代理返回给发起端
public class ServiceAbility1 extends Ability { private static final HiLogLabel LABEL_LOG = new HiLogLabel(HiLog.LOG_APP, 0xD001101, "ServiceAbility1"); //实例化客户端,返回给发送方可调用的代理 private AcceptRemoteProxy remote = new AcceptRemoteProxy(); /** * 当该PA接收到连接请求时,将该客户端转化为代理返回给发起端 * @param intent * @return */ @Override public IRemoteObject onConnect(Intent intent) { return remote; } @Override public void onStart(Intent intent) { HiLog.error(LABEL_LOG, "--------ServiceAbility1::onStart"); super.onStart(intent); } /** * 每次调用ServiceAbility1都会执行该方法 * * @param intent 数据载体 * @param restart 启动状态 true:销毁后启动,false:正常启动 * @param startId 启动的计数器 */ @Override public void onCommand(Intent intent, boolean restart, int startId) { HiLog.info(LABEL_LOG, "--------ServiceAbility1::onCommand"); HiLog.info(LABEL_LOG, "--------ServiceAbility1::1数据载体:" + intent.getOperation().getAbilityName()); HiLog.info(LABEL_LOG, "--------ServiceAbility1::启动状态:" + restart); HiLog.info(LABEL_LOG, "--------ServiceAbility1::启动的计数器:" + startId); } @Override public void onDisconnect(Intent intent) { HiLog.info(LABEL_LOG, "--------连接Service断开::onDisconnect"); } }
public class MainAbilitySlice extends AbilitySlice { static final HiLogLabel LOG_LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00002, "MainAbilitySlice"); private StartRemoteProxy startRemoteProxy = null; // 创建连接回调实例 private IAbilityConnection conn = new IAbilityConnection() { //连接到Service的回调 @Override public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int i) { // Client侧(StartRemoteProxy)需要定义与Service侧(AcceptRemoteProxy)相同的IRemoteObject实现类。 // 开发者获取服务端传过来IRemoteObject对象,并从中解析出服务端传过来的信息。 startRemoteProxy = new StartRemoteProxy(iRemoteObject); HiLog.info(LOG_LABEL, "--------连接Service" + startRemoteProxy.toString()); } // 意外情况导致断开连接的回调 @Override public void onAbilityDisconnectDone(ElementName elementName, int i) { HiLog.info(LOG_LABEL, "--------意外情况导致断开连接"); } }; @Override public void onStart(Intent intent) { HiLog.info(LOG_LABEL, "--------onStart2"); super.onStart(intent); super.setUIContent(ResourceTable.Layout_ability_main); //链接service Button button1 = (Button) findComponentById(ResourceTable.Id_button1); button1.setClickedListener(component -> { Intent connectPAIntent = new Intent(); Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName("com.example.myapplication") .withAbilityName("com.example.myapplication.ServiceAbility1") .build(); connectPAIntent.setOperation(operation); connectAbility(connectPAIntent, conn); }); //连接后进行通信 Button button2 = (Button) findComponentById(ResourceTable.Id_button2); button2.setClickedListener(component -> { if (startRemoteProxy != null) { String rs = startRemoteProxy.test("hello world!"); HiLog.info(LOG_LABEL, "-------接受端响应数据:" + rs); } }); //断开链接service Button button3 = (Button) findComponentById(ResourceTable.Id_button3); button3.setClickedListener(component -> { disconnectAbility(conn); }); } }
<?xml version="1.0" encoding="utf-8"?> <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:width="match_parent" ohos:height="match_parent" ohos:orientation="vertical"> <Button ohos:id="$+id:button1" ohos:width="match_content" ohos:height="match_content" ohos:background_element="$graphic:background_button" ohos:text="连接Service" ohos:layout_alignment="horizontal_center" ohos:text_size="100"/> <Button ohos:id="$+id:button2" ohos:width="match_content" ohos:top_margin="100px" ohos:height="match_content" ohos:background_element="$graphic:background_button" ohos:text="连接后通信" ohos:layout_alignment="horizontal_center" ohos:text_size="100"/> <Button ohos:id="$+id:button3" ohos:width="match_content" ohos:top_margin="100px" ohos:height="match_content" ohos:background_element="$graphic:background_button" ohos:text="断开Service" ohos:layout_alignment="horizontal_center" ohos:text_size="100"/> </DirectionalLayout>
public class MainAbilitySlice extends AbilitySlice { static final HiLogLabel LOG_LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00002, "MainAbilitySlice"); private StartRemoteProxy startRemoteProxy = null; // 创建连接回调实例 private IAbilityConnection conn = new IAbilityConnection() { //连接到Service的回调 @Override public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int i) { // Client侧(StartRemoteProxy)需要定义与Service侧(AcceptRemoteProxy)相同的IRemoteObject实现类。 // 开发者获取服务端传过来IRemoteObject对象,并从中解析出服务端传过来的信息。 startRemoteProxy = new StartRemoteProxy(iRemoteObject); HiLog.info(LOG_LABEL, "--------连接Service" + startRemoteProxy.toString()); } // 意外情况导致断开连接的回调 @Override public void onAbilityDisconnectDone(ElementName elementName, int i) { HiLog.info(LOG_LABEL, "--------意外情况导致断开连接"); } }; @Override public void onStart(Intent intent) { HiLog.info(LOG_LABEL, "--------onStart2"); super.onStart(intent); super.setUIContent(ResourceTable.Layout_ability_main); //获取设备列表 List<DeviceInfo> deviceList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE); if(deviceList.isEmpty()){ return ; } 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()); }); //使用deviceIds的第一个元素,做为启动远程设备的目标id String devcieIdStr = deviceIds.get(0); //链接service Button button1 = (Button) findComponentById(ResourceTable.Id_button1); button1.setClickedListener(component -> { if(devcieIdStr!=null){ Intent connectPAIntent = new Intent(); Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName("com.example.myapplication") .withAbilityName("com.example.myapplication.ServiceAbility1") .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) .build(); connectPAIntent.setOperation(operation); connectAbility(connectPAIntent, conn); } }); //连接后进行通信 Button button2 = (Button) findComponentById(ResourceTable.Id_button2); button2.setClickedListener(component -> { if (startRemoteProxy != null) { String rs = startRemoteProxy.test("hello world!"); HiLog.info(LOG_LABEL, "-------接受端响应数据:" + rs); } }); //断开链接service Button button3 = (Button) findComponentById(ResourceTable.Id_button3); button3.setClickedListener(component -> { disconnectAbility(conn); }); } }
Ability为启动Service提供了以下生命周期方法,可以重写这些方法来添加自己的处理。Service将在其他Ability调用startAbility()时创建,然后保持运行。其他Ability通过调用stopAbility()来停止Service,Service停止后,系统会将其销毁。
根据调用方法的不同,其生命周期有以下两种路径:
启动Service
该Service在其他Ability调用startAbility()时创建,然后保持运行。其他Ability通过调用stopAbility()来停止Service,Service停止后,系统会将其销毁。
连接Service
该Service在其他Ability调用connectAbility()时创建,客户端可通过调用disconnectAbility()断开连接。多个客户端可以绑定到相同Service,而且当所有绑定全部取消后,系统即会销毁该Service。
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应通过实现此方法来清理任何资源,如关闭线程、注册的侦听器等。
第一次启动本地设备的Service
01-10 11:45:56.905 7290-7290/com.example.myapplication E 01101/ServiceAbility1: --------ServiceAbility1::onStart
01-10 11:45:56.906 7290-7290/com.example.myapplication I 01101/ServiceAbility1: --------ServiceAbility1::onCommand
01-10 11:45:56.906 7290-7290/com.example.myapplication I 01101/ServiceAbility1: --------ServiceAbility1::1数据载体:com.example.myapplication.ServiceAbility1
01-10 11:45:56.906 7290-7290/com.example.myapplication I 01101/ServiceAbility1: --------ServiceAbility1::启动状态:false
01-10 11:45:56.906 7290-7290/com.example.myapplication I 01101/ServiceAbility1: --------ServiceAbility1::启动的计数器:1
第二次启动本地设备的Service
01-10 11:48:18.613 7290-7290/com.example.myapplication I 01101/ServiceAbility1: --------ServiceAbility1::onCommand
01-10 11:48:18.613 7290-7290/com.example.myapplication I 01101/ServiceAbility1: --------ServiceAbility1::1数据载体:com.example.myapplication.ServiceAbility1
01-10 11:48:18.613 7290-7290/com.example.myapplication I 01101/ServiceAbility1: --------ServiceAbility1::启动状态:false
01-10 11:48:18.613 7290-7290/com.example.myapplication I 01101/ServiceAbility1: --------ServiceAbility1::启动的计数器:2
当点击停止本地设备的Service时
01-10 11:49:31.327 7290-7290/com.example.myapplication I 01101/ServiceAbility1: --------ServiceAbility1::onBackground
01-10 11:49:31.327 7290-7290/com.example.myapplication I 01101/ServiceAbility1: --------ServiceAbility1::onStop
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。