鸿蒙Ability(二):Particle Ability的Service Ability模板

导语

Ability可以分为FA(Feature Ability)和PA(Particle Ability)两种类型。
①Feature Ability
FA代表有界面的Ability,支持Page Ability模板(是FA唯一支持的模板),用于提供与用户交互的能力。
②Particle Ability
PA代表无界面的Ability,支持Service Ability和Data Ability模板。

Particle Ability的Service Ability模板

Service Ability概念

Service Ability是基于Service模板的Ability(下文简称Service),主要用于后台运行任务(如执行音乐播放、文件下载等),但不提供用户交互界面。Service可由其他应用或Ability启动,即使用户切换到其他应用,Service仍将在后台继续运行。
Service是单实例的。在一个设备上,相同的Service只会存在一个实例。如果多个Ability共用这个实例,只有当与Service绑定的所有Ability都退出后,Service才能够退出。
由于Service是在主线程里执行的,因此,如果在Service里面的操作时间过长,开发者必须在Service里创建新的线程来处理(可参考本人的鸿蒙线程间通信文章),防止造成主线程阻塞与应用程序无响应。

创建一个Service

在这里插入图片描述
输入名字,还可以选择是否要在后台运行,还可以选择要在后台做什么
在这里插入图片描述
这里我们先不勾选,就创建好了一个Service啦
可以看见创建Service不会生成AbilitySlice
在这里插入图片描述
在config.json中我们可以看见已经自动注册了Service,配置文件自动生成了Service的相关属性,Service的属性相较于Page要少一些,值得注意的是type为service!这就是service模板。
在这里插入图片描述

Service的生命周期

public class ServiceAbility extends Ability {
    private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "Demo");

    //该方法在创建Service的时候调用,用于Service的初始化。在Service的整个生命周期只会调用一次,调用时传入的Intent应为空。
    @Override
    public void onStart(Intent intent) {
        HiLog.info(LABEL_LOG, "ServiceAbility::onStart");
        super.onStart(intent);
    }

    @Override
    public void onBackground() {
        super.onBackground();
        HiLog.info(LABEL_LOG, "ServiceAbility::onBackground");
    }

    //在Service销毁时调用。Service应通过实现此方法来清理任何资源,如关闭线程、注册的侦听器等。
    @Override
    public void onStop() {
        super.onStop();
        HiLog.info(LABEL_LOG, "ServiceAbility::onStop");
    }

    //在Service创建完成之后调用,该方法在客户端每次启动该Service时都会调用,用户可以在该方法中做一些调用统计、初始化类的操作。
    @Override
    public void onCommand(Intent intent, boolean restart, int startId) {
        HiLog.info(LABEL_LOG, "ServiceAbility::onCommand");
    }

    //在Ability和Service连接时调用,该方法返回IRemoteObject对象
    // 用户可以在该回调函数中生成对应Service的IPC通信通道,以便Ability与Service交互。
    // Ability可以多次连接同一个Service,系统会缓存该Service的IPC通信对象,
    // 只有第一个客户端连接Service时,系统才会调用Service的onConnect方法来生成IRemoteObject对象,
    // 而后系统会将同一个RemoteObject对象传递至其他连接同一个Service的所有客户端,而无需再次调用onConnect方法。
    @Override
    public IRemoteObject onConnect(Intent intent) {
        HiLog.info(LABEL_LOG, "ServiceAbility::onConnect");
        return null;
    }

    //在Ability与绑定的Service断开连接时调用。
    @Override
    public void onDisconnect(Intent intent) {
        HiLog.info(LABEL_LOG, "ServiceAbility::onDisconnect");
    }
}

Service的生命周期根据调用方法的不同,其生命周期有以下两种路径:
启动Service
该Service在其他Ability调用startAbility()时创建,然后保持运行。其他Ability通过调用stopAbility()来停止Service,Service停止后,系统会将其销毁。
连接Service
该Service在其他Ability调用connectAbility()时创建,客户端可通过调用disconnectAbility​()断开连接。多个客户端可以绑定到相同Service,而且当所有绑定全部取消后,系统即会销毁该Service。
在这里插入图片描述

启动一个Service

概念与启动Service的生命周期

Ability为开发者提供了startAbility()方法来启动另外一个Ability。因为Service也是Ability的一种,开发者同样可以通过将Intent传递给该方法来启动Service。不仅支持启动本地Service,还支持启动远程Service。
(启动Service主要用于启动一个服务执行后台任务或者远程启动一个功能,不进行通信,也就是不需要返回数据。)
Ability为启动Service提供了以下生命周期方法,用户可以重写这些方法来添加自己的处理。
Service将在其他Ability调用startAbility()时创建,然后保持运行。其他Ability通过调用stopAbility()来停止Service,Service停止后,系统会将其销毁。
在这里插入图片描述

启动本地Service示例

在MainAbilitySlice的布局里面添加一个按钮,并为其设置一个点击事件监听器用于启动本地Service。

		buttonStartService.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                //启动本地服务
                startLocalService();
            }
        });

	/**
     * 启动本地服务
     */
    private void startLocalService() {
        Intent intent = new Intent();
        //构建操作方式
        //开发者可以通过构造包含DeviceId、BundleName与AbilityName的Operation对象来设置目标Service信息。
        Operation operation = new Intent.OperationBuilder()
                // 表示设备ID
                // 如果是本地设备,则可以直接留空
                // 如果是远程设备,可以通过ohos.distributedschedule.interwork.DeviceManager提供的
                // getDeviceList获取设备列表,详见《API参考》
                .withDeviceId("")
                // 表示包名称
                .withBundleName("org.example.harmonyserviceabilitydemo")
                // 跳转目标的路径名,通常是包名+类名
                // 表示待启动的Ability名称
                .withAbilityName("org.example.harmonyserviceabilitydemo.ServiceAbility")
                .build();
        //设置操作
        intent.setOperation(operation);
        //Ability通过startAbility()方法来启动Service
        startAbility(intent);
    }

接下来就可以去运行项目,去点击按钮启动一下Service啦~
那怎么知道Service是否启动了呢?其实你会发现,在创建Service的时候,就已经写好了日志,对啦,通过打印的日志可以知道是否启动了Service。
在这里插入图片描述
如果Service尚未运行,则系统会先调用onStart()来初始化Service,再回调Service的onCommand()方法来启动Service。
如果Service正在运行,则系统会直接回调Service的onCommand()方法来启动Service。

再次点击启动本地设备Service,可以看见只执行了onCommand()方法。
在这里插入图片描述

启动远程Service示例

代码如下(官网抄的):

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);

在启动远程Service的时候,有一个需要注意的地方,就是你启动的这个服务是否允许其他应用程序发现,需要在配置文件中加上visible属性并设置为true(默认为false),如果是false,即使知道了包名和类名,也无法访问。
在这里插入图片描述

停止一个Service

Service一旦创建就会一直保持在后台运行,除非必须回收内存资源,否则系统不会停止或销毁Service。
开发者可以在Service中通过terminateAbility()停止本Service或在其他Ability调用stopAbility()来停止Service。
停止Service同样支持停止本地设备Service和停止远程设备Service,使用方法与启动Service一样。一旦调用停止Service的方法,系统便会尽快销毁Service。
有两种停止Service的方法,一种是在Page中停止,另一种是直接在本Service中停止

在Page中停止服务

在MainAbilitySlice中新增一个“在Page中停止本地设备Service”的按钮,并为其设置点击事件监听器。
关闭服务的方法很简单,和开启的过程几乎相同,只需要将startAbility改为stopAbility。

		buttonStopServicePage.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                //停止本地服务
                stopLocalService();
            }
        });

    /**
     * 停止本地服务
     * 在Page Ability中停止Service
     */
    private void stopLocalService() {
        Intent intent = new Intent();
        Operation operation = new Intent.OperationBuilder()
                .withDeviceId("")
                .withBundleName("org.example.harmonyserviceabilitydemo")
                .withAbilityName(".ServiceAbility")
                .build();
        intent.setOperation(operation);
        //停止服务
        stopAbility(intent);
    }

在这里插入图片描述
可以看见,当我们在Page Ability中关闭Service的时候,系统会把Service放到后台,回调onBackground()方法,然后再通过onStop()来停止Service。

直接在本Service中停止服务

需要在ServiceAbility中创建一个线程池,并使用线程池开启一个线程延时关闭Service。

	//在本Service中停止服务
    /**
     * 创建一个线程池
     */
    final static ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(4);
    /**
     * 创建一个线程工厂
     */
//    private  ThreadFactory threadFactory=new ThreadFactory() {
//        @Override
//        public Thread newThread(Runnable runnable) {
//            return new Thread(){
//                @Override
//                public void run() {
//                    runnable.run();
//                }
//            };
//        }
//    };
    //private ThreadFactory threadFactory = runnable -> new Thread(() -> runnable.run());
    private final ThreadFactory threadFactory = Thread::new;


    private void stopService() {
        HiLog.info(LABEL_LOG, "ServiceAbility::开始倒计时3s");
        // 延时任务
        scheduledExecutorService.schedule(threadFactory.newThread(new Runnable() {
            @Override
            public void run() {
                //停止服务当前服务
                terminateAbility();
            }
        }), 3, TimeUnit.SECONDS);//延时三秒执行
    }

在这里插入图片描述
可以看出,在本Service中停止服务和在Page内停止服务的过程是一样的。

连接一个Service

连接Service的概念及其生命周期

如果Service需要与Page Ability或其他应用的Service Ability进行交互,则应创建用于连接的Connection。
前面的启动Service和连接Service的不同在于,连接Service后可以进行通信,也就是需要连接的Service执行任务后返回数据的时候,就要需要用连接,而不是启动。
连接Service同样有其生命周期,与启动Service的生命略有差异,Service将在其他Ability调用connectAbility()时创建,客户端可通过调用disconnectAbility​()断开连接。多个客户端可以绑定到相同Service,而且当所有绑定全部取消后,系统才会将其销毁。
在这里插入图片描述

运用

在MainAbilitySlice新增一个“连接Service”的按钮,并且为其设置点击事件监听器后回调下面的连接方法。

	buttonConnectService.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                //链接服务
                connectService();
            }
        });
        
	/**
     * 链接服务
     */
    private void connectService() {
        Intent intent = new Intent();
        //构建操作方式
        Operation operation = new Intent.OperationBuilder()
                // 设备id
                .withDeviceId("")
                // 应用的包名
                .withBundleName("org.example.harmonyserviceabilitydemo")
                // 跳转目标的路径名  通常是包名+类名
                .withAbilityName(".ServiceAbility")
                .build();
        //设置操作
        intent.setOperation(operation);
        //连接到服务
        //Service支持其他Ability通过connectAbility()方法与其进行连接
        //使用connectAbility()的时候,需要传入目标Service的Intent与IAbilityConnection接口的实例
        //(connection为IAbilityConnection的实现类)
        connectAbility(intent, connection);
    }

    /**
     * IAbilityConnection
     * 创建连接回调实例
     */
    // IAbilityConnection提供了两个方法
    // onAbilityConnectDone()用来处理连接的回调
    // onAbilityDisconnectDone()用来处理断开连接的回调
    private IAbilityConnection connection = new IAbilityConnection() {
        // 连接到Service的回调
        @Override
        public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int i) {
            // Client侧需要定义与Service侧相同的IRemoteObject实现类。
            // 在这里开发者可以拿到服务端传过来IRemoteObject对象,从中解析出服务端传过来的信息
            HiLog.info(LABEL_LOG, "MainAbilitySlice::" + iRemoteObject.getInterfaceDescriptor());
//            TestServiceAbility.MyLocalRemoteObject myLocalRemoteObject = (TestServiceAbility.MyLocalRemoteObject) iRemoteObject;
//            String backMessge = myLocalRemoteObject.getMessage();
//            HiLog.info(LABEL_LOG, "ServicePageAbilitySlice::" + backMessge);
        }

        // 断开与Service连接的回调
        @Override
        public void onAbilityDisconnectDone(ElementName elementName, int i) {

        }
    };

在与Service进行交互的时候,是通过**connectAbility()**方法开启连接到服务的。
在使用connectAbility()的时候,需要传入目标Intent与IAbilityConnection接口的实例。IAbilityConnection接口的实例必须实现两个方法,一个是onAbilityConnectDone(),另一个是onAbilityDisconnectDone(),前者用来处理连接的回调,后者用来处理断开连接的回调。
可以运行项目点击一下,测试一下链接是否正确。
在这里插入图片描述
连接成功后,需要进行数据交互,那么Ability如何获取Service中的数据呢?
有两种方法
法一:
在ServiceAbility中:

	@Override
    public IRemoteObject onConnect(Intent intent) {
        HiLog.info(LABEL_LOG, "ServiceAbility::onConnect");
        // 把IRemoteObject返回给客户端
        return new MyRemoteObject();
    }

	// 创建自定义IRemoteObject实现类
    private class MyRemoteObject extends RemoteObject {
        MyRemoteObject() {
            super("消息");
        }
    }

在MainAbilitySlice中:

	    private IAbilityConnection connection = new IAbilityConnection() {
        // 连接到Service的回调
        @Override
        public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int i) {
            // Client侧需要定义与Service侧相同的IRemoteObject实现类。
            // 在这里开发者可以拿到服务端传过来IRemoteObject对象,从中解析出服务端传过来的信息
            HiLog.info(LABEL_LOG, "MainAbilitySlice::" + iRemoteObject.getInterfaceDescriptor());
        }

        // 断开与Service连接的回调
        @Override
        public void onAbilityDisconnectDone(ElementName elementName, int i) {

        }
    };

在这里插入图片描述
法二:
在ServiceAbility中:

    @Override
    public IRemoteObject onConnect(Intent intent) {
        HiLog.info(LABEL_LOG, "ServiceAbility::onConnect");
        // 把IRemoteObject返回给客户端
        return new MyLocalRemoteObject();
    }

    // 创建自定义IRemoteObject实现类
    public class MyLocalRemoteObject extends LocalRemoteObject {
        MyLocalRemoteObject() {
        }

        public String getMessage() {
            return "Service返回的消息";
        }
    }

在MainAbilitySlice中:

	private IAbilityConnection connection = new IAbilityConnection() {
        // 连接到Service的回调
        @Override
        public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int i) {
            // Client侧需要定义与Service侧相同的IRemoteObject实现类。
            // 在这里开发者可以拿到服务端传过来IRemoteObject对象,从中解析出服务端传过来的信息
            ServiceAbility.MyLocalRemoteObject myLocalRemoteObject = (ServiceAbility.MyLocalRemoteObject) iRemoteObject;
            String backMessge = myLocalRemoteObject.getMessage();
            HiLog.info(LABEL_LOG, "MainAbilitySlice::" + backMessge);
        }

        // 断开与Service连接的回调
        @Override
        public void onAbilityDisconnectDone(ElementName elementName, int i) {

        }
    };

在这里插入图片描述

断开一个Service

断开服务只需要调用disconnectAbility()方法即可
在MainAbilitySlice中新增一个按钮并为其设置点击事件

	buttonDisconnectService.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                //断开服务
                //断开Service不用传Intent,但是要传IAbilityConnection接口的实例(connection为IAbilityConnection的实现类)
                disconnectAbility(connection);
            }
        });

(connection为上节新建的private IAbilityConnection connection)
在这里插入图片描述

前台Service

什么是前台Service呢,音乐播放是最常见的一个前台Service,使用音乐播放的时候,会在通知栏创建一个音乐播放,然后播放音乐。
前台Service就是指那些被认为用户知道(用户所认可的)且用户希望能够一直保持运行的、在系统内存不足的时候不允许系统杀死的服务(一般情况下,Service都是在后台运行的,后台Service的优先级都是比较低的,当资源不足时,系统有可能回收正在运行的后台Service)。
前台Service会始终保持正在运行的图标在系统状态栏显示,这就意味着通知只有在这个服务被终止或从前台主动移除通知后才能被解除。

开启前台Service

使用前台Service并不复杂,开发者只需在Service创建的方法onStart()里,调用keepBackgroundRunning()将Service与通知绑定。调用keepBackgroundRunning()方法前需要在配置文件中声明ohos.permission.KEEP_BACKGROUND_RUNNING权限(该权限是允许Service Ability在后台继续运行,属于非敏感权限,仅需在config.json中声明,应用安装后即被授权),同时还需要在配置文件中添加对应的backgroundModes参数(backgroundModes参数表示后台服务的类型,可以为一个服务配置多个后台服务类型。该标签仅适用于service类型的Ability)。
backgroundModes参数取值范围如下:

取值意义
dataTransfer通过网络/对端设备进行数据下载、备份、分享、传输等业务
audioPlayback音频输出业务
audioRecording音频输入业务
pictureInPicture画中画、小窗口播放视频业务
voip音视频电话、VOIP业务
location定位、导航业务
bluetoothInteraction蓝牙扫描、连接、传输业务
wifiInteractionWLAN扫描、连接、传输业务
screenFetch录屏、截屏业务

在TestServiceAbility中:

 	@Override
    public void onStart(Intent intent) {
        HiLog.info(LABEL_LOG, "ServiceAbility::onStart");
        super.onStart(intent);
        //启动前台服务
        startForegroundService();
    }

	//启动前台服务
    private void startForegroundService() {
        // 创建通知请求,设置notificationId为1008
        NotificationRequest request = new NotificationRequest(1008);
        //创建普通通知
        NotificationRequest.NotificationNormalContent content = new NotificationRequest.NotificationNormalContent();
        //设置通知的标题和内容
        content.setTitle("网易云音乐").setText("羽落");
        //创建通知内容
        NotificationRequest.NotificationContent notificationContent = new NotificationRequest.NotificationContent(content);
        //设置通知
        request.setContent(notificationContent);
        // 绑定通知,1008为创建通知时传入的notificationId
        keepBackgroundRunning(1008, request);

        HiLog.info(LABEL_LOG, "ServiceAbility::startForegroundService");
    }

在配置文件中:
声明ohos.permission.KEEP_BACKGROUND_RUNNING权限,同时还需要添加对应的backgroundModes参数。

		"backgroundModes": [
          "dataTransfer"
        ],
        "permissions": [
          "ohos.permission.KEEP_BACKGROUND_RUNNING"
        ]

在这里插入图片描述
接下来就可以运行应用啦~
在这里插入图片描述
可以看见已经启动了一个前台服务(虽然我也不知道这应用在不在前台哈)。

停止前台Service

在onStop()方法中调用cancelBackgroundRunning​()方法可停止前台Service。

	@Override
    public void onCommand(Intent intent, boolean restart, int startId) {
        HiLog.info(LABEL_LOG, "ServiceAbility::onCommand");
        //取消前台服务
        cancelBackgroundRunning();
        HiLog.error(LABEL_LOG, "TestServiceAbility::cancelBackgroundRunning");
    }

在这里插入图片描述
这里为了测试,把cancelBackgroundRunning​()方法放在了onCommand()中,可以看见,已经取消了前台服务。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值