HarmonyOS实战—Service Ability简介以及各种操作:启动 连接

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

    1. 需要获取远程设备的信息,我们需要给予权限ohos.permission.GET_DISTRIBUTED_DEVICE_INFO

      {
        ...,
        "module": {
      	...,
          "reqPermissions": [
            {
              "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO"
            }
          ]
        }
      }
      
    2. 我们通过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个选择

      1. DeviceInfo.FLAG_GET_ALL_DEVICE 获取全部设备
      2. DeviceInfo.FLAG_GET_ONLINE_DEVICE 获取在线设备
      3. DeviceInfo.FLAG_GET_OFFLINE_DEVICE 获取离线设备
    3. 我们测试一下单设备模式下运行这个app

      在这里插入图片描述

    4. 接下来我们试着在多设备模式下运行这个app

      在这里插入图片描述

    5. 接下来我们要开始启动远程的服务

      这里我们沿用官网文档中的代码

      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。

    img

前台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的这些功能都包含在其中

  1. 编写前端页面

    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>
    
  2. 新建若干个新的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异常死亡的回调方法。

  3. 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"
          }
        ]
      }
    }
    
  4. 写好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();
        }
    }
    
  5. 值得注意的是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();
                }
            }
        }
    }
    
  6. 测试

    在这里插入图片描述

    这个测试确实不方便截图出来,但是在我的deveco studio 中是可以正常运行的

【本文正在参与“有奖征文 | HarmonyOS征文大赛”活动】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值