HMS09.Ability

01.概述

  Ability是应用所具备能力的抽象,也是应用程序的重要组成部分;   

  一个应用可以具备多种能力(即可以包含多个Ability),HarmonyOS支持应用以Ability为单位进行部署。
  Ability可以分为FA(Feature Ability)和PA(Particle Ability)两种类型,每种类型为开发者提供了不同的模板,以便实现不同的业务功能。  

  • FA支持Page Ability

    Page模板是FA唯一支持的模板,用于提供与用户交互的能力。一个Page实例可以包含一组相关页面,每个页面用一个AbilitySlice实例表示。

  • PA支持Service AbilityData Ability
    • Service模板:用于提供后台运行任务的能力。
    • Data模板:用于对外部提供统一的数据访问抽象。

  在配置文件(config.json)中注册Ability时,可以通过配置Ability元素中的“type”属性来指定Ability模板类型

 1 {
 2     "module": {
 3         ...
 4         "abilities": [
 5             {
 6                 ...
 7                 "type": "page"
 8                 ...
 9             }
10         ]
11         ...
12     }
13     ...
14 }

  “type”的取值可以为“page”、“service”或“data”,分别代表Page模板、Service模板、Data模板。
  为了便于表述,后文中我们将基于Page模板、Service模板、Data模板实现的Ability分别简称为Page、Service、Data。

02. PageAbility

  >>> Page 模板是FA唯一支持的模板;用于提供与用户交互的能力;
       一个Page,可以由一个或多个 AbilitySlice 构成;AbilitySlice是指应用的单个页面及其控制逻辑的总和。
       HarmonyOS支持不同Page之间的跳转,并可以指定跳转到目标Page中某个具体的AbilitySlice。 

    

   >>> 关于页面路由的配置

    a. 主路由:  也就是Page页默认展示的 AbilitySlice
      配置:  super.setMainRoute(MainAbilitySlice.class.getName());

    b.其它路由: (用于配置Page页中其它 AbilitySlice )
      配置:  addActionRoute("action.pay", PaySlice.class.getName());
           addActionRoute("action.scan",ScanSlice.class.getName());

      c. 配置路由中的动作名称,需要在 config.json中进行配置

      

  >>> Page生命周期及回调
    ** INACTIVE状态是一种短暂存在的状态,可理解为“激活中”。

    

    a. onStart():  
      当系统首次创建Page实例时,触发该回调。对于一个Page实例,该回调在其生命周期过程中仅触发一次,Page在该逻辑后将进入INACTIVE状态。开发者必须重写该方法,并在此配置默认展示的AbilitySlice。
      可在此事件中配置主路由及其它Slice的路由;
    b. onStop()
      系统将要销毁Page时,将会触发此回调函数,通知用户进行系统资源的释放。销毁Page的可能原因包括以下几个方面:
      * 用户通过系统管理能力关闭指定Page,例如使用任务管理器关闭Page。
      * 
用户行为触发Page的terminateAbility()方法调用,例如使用应用的退出功能。
      * 
配置变更导致系统暂时销毁Page并重建。
      * 
系统出于资源管理目的,自动触发对处于BACKGROUND状态Page的销毁。
  >>>
AbilitySlice 的生命周期
    AbilitySlice作为Page的组成单元,其生命周期是依托于其所属Page生命周期的。
    由于AbilitySlice承载具体的页面,开发者必须重写AbilitySlice的onStart()回调,并在此方法中通过setUIContent()方法设置页面
    

    ** 当同一Page页中不同的Slice进行切换时,各Slice的生命周期规则如下:

    当AbilitySlice处于前台且具有焦点时,其生命周期状态随着所属Page的生命周期状态的变化而变化。
    当一个Page拥有多个AbilitySlice时,例如:MyAbility下有FooAbilitySlice和BarAbilitySlice,当前FooAbilitySlice处于前台并获得焦点,并即将导航到BarAbilitySlice,在此期间的生命周期状态变化顺序为:

    1. FooAbilitySlice从ACTIVE状态变为INACTIVE状态。
    2. BarAbilitySlice则从INITIAL状态首先变为INACTIVE状态,然后变为ACTIVE状态(假定此前BarAbilitySlice未曾启动)。
    3. FooAbilitySlice从INACTIVE状态变为BACKGROUND状态。

    对应两个slice的生命周期方法回调顺序为:

      FooAbilitySlice.onInactive() --> BarAbilitySlice.onStart() --> BarAbilitySlice.onActive() --> FooAbilitySlice.onBackground()

    在整个流程中,MyAbility始终处于ACTIVE状态。但是,当Page被系统销毁时,其所有已实例化的AbilitySlice将联动销毁,而不仅是处于前台的AbilitySlice。

  >>> 同一Page内的Slice之间的导航

    1). 当发起导航的AbilitySlice和导航目标的AbilitySlice处于同一个Page时,您可以通过present()方法实现导航 

     

     2). 当发起导航的 AbilitySlice 在导航到目标AbilitySlice时,同时需要获取返回结果信息时,通过  presentForResult 实现导航;
      目标Slice : 通过 SetResult 进行设置;
      发起Slice : 在 onResult() 回调事件中获取结果; 

       

       系统为每个Page维护了一个AbilitySlice实例的栈每个进入前台的AbilitySlice实例均会入栈
      当开发者在调用present()presentForResult()指定的AbilitySlice实例已经在栈中存在时,则栈中位于此实例之上的AbilitySlice均会出栈并终止其生命周期
      前面的示例代码中,导航时指定的AbilitySlice实例均是新建的,即便重复执行此代码(此时作为导航目标的这些实例是同一个类),也不会导致任何AbilitySlice出栈

  >>> 不同Page之间的导航

     AbilitySlice作为Page的内部单元,以Action的形式对外暴露,因此可以通过配置Intent的Action导航到目标AbilitySlice。
    Page间的导航可以使用startAbility()或startAbilityForResult()方法,获得返回结果的回调为onAbilityResult()。在Ability中调用setResult()可以设置返回结果。    

  >>> 跨设备的迁移  
    跨设备迁移(下文简称“迁移”)支持将Page在同一用户的不同设备间迁移,以便支持用户无缝切换的诉求。以Page从设备A迁移到设备B为例,迁移动作主要步骤如下:

    1. 设备A上的Page请求迁移。
    2. HarmonyOS处理迁移任务,并回调设备A上Page的保存数据方法,用于保存迁移必须的数据。
    3. HarmonyOS在设备B上启动同一个Page,并回调其恢复数据方法。

    ** 实现IAbilityContinuation接口    

      一个应用可能包含多个Page,仅需要在支持迁移的Page中通过以下方法实现IAbilityContinuation接口。同时,此Page所包含的所有AbilitySlice也需要实现此接口

      

    

    

 03. Service Ability

  基于Service模板的Ability(以下简称“Service”)主要用于后台运行任务(如执行音乐播放、文件下载等),但不提供用户交互界面。
  Service可由其他应用或Ability启动,即使用户切换到其他应用,Service仍将在后台继续运行。

  Service是单实例的。在一个设备上,相同的Service只会存在一个实例。
  如果多个Ability共用这个实例,只有当与Service绑定的所有Ability都退出后,Service才能够退出。
  由于Service是在主线程里执行的,因此,如果在Service里面的操作时间过长,开发者必须在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 的启动与停止

  • 启动服务

    发者可以通过构造包含DeviceId、BundleName与AbilityName的Operation对象来设置目标Service信息。这三个参数的含义如下:

    • DeviceId:表示设备ID。如果是本地设备,则可以直接留空;如果是远程设备,可以通过ohos.distributedschedule.interwork.DeviceManager提供的getDeviceList获取设备列表,详见Java API参考
    • BundleName:表示包名称。
    • AbilityName:表示待启动的Ability名称。


  • 停止服务

      Service一旦创建就会一直保持在后台运行,除非必须回收内存资源,否则系统不会停止或销毁Service。
      开发者可以在Service中通过terminateAbility()停止本Service或在其他Ability调用stopAbility()来停止Service。

      停止Service同样支持停止本地设备Service和停止远程设备Service,使用方法与启动Service一样。一旦调用停止Service的方法,系统便会尽快销毁Service。

  • 连接服务

     a. 创建连接; 创建连接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) {
    }
};

    b. 连接服务; 连接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 的生命周期
    

 04. Data Ability 

  使用Data模板的Ability(以下简称“Data”)有助于应用管理其自身和其他应用存储数据的访问,并提供与其他应用共享数据的方法。Data既可用于同设备不同应用的数据共享,也支持跨设备不同应用的数据共享。

  数据的存放形式多样,可以是数据库,也可以是磁盘上的文件。Data对外提供对数据的增、删、改、查,以及打开文件等接口,这些接口的具体实现由开发者提供

 URI介绍

 Data的提供方和使用方都通过URI(Uniform Resource Identifier)来标识一个具体的数据,例如数据库中的某个表或磁盘上的某个文件。HarmonyOS的URI仍基于URI通用标准,格式如下:

  • scheme:协议方案名,固定为“dataability”,代表Data Ability所使用的协议类型。
  • authority:设备ID。如果为跨设备场景,则为目标设备的ID;如果为本地设备场景,则不需要填写。
  • path:资源的路径信息,代表特定资源的位置信息。
  • query:查询参数。
  • fragment:可以用于指示要访问的子资源。

  URI示例:

  • 跨设备场景:dataability://device_id/com.domainname.dataability.persondata/person/10
  • 本地设备:dataability:///com.domainname.dataability.persondata/person/10
    本地设备的“device_id”字段为空,因此在“dataability:”后面有三个“/”。

   >>> 数据存储类型
    a. 文件数据: 如 文本, 图片,音乐等;
    b. 结构化数据: 如 数据库等;

   >>> 实现 UserDataAbility
    UserDataAbility 用于接收其他应用发送的请求,提供外部程序访问的入口,从而实现应用间的数据访问;

     实现UserDataAbility,需要在“Project”窗口当前工程的主目录(entry > src > main > java > com.xxx.xxx”)选择File > New > Ability > Empty Data Ability”,设置Data Name后完成UserDataAbility的创建。        
    Data提供了文件存储和数据库存储两组接口供用户使用。

  >>> 文件存储

    开发者需要在Data中重写FileDescriptor openFile​(Uri uri, String mode)方法来操作文件:uri为客户端传入的请求目标路径;mode为开发者对文件的操作选项,可选方式包含“r”(读), “w”(写), “rw”(读写)等。

    开发者可通过MessageParcel静态方法dupFileDescriptor()复制待操作文件流的文件描述符,并将其返回,供远端应用访问文件。

     根据传入的uri打开对应的文件

private static final HiLogLabel LABEL_LOG = new HiLogLabel(HiLog.LOG_APP, 0xD00201, "Data_Log");

@Override
public FileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
    File file = new File(uri.getDecodedPathList().get(0)); //get(0)是获取URI完整字段中查询参数字段。
    if (mode == null || !"rw".equals(mode)) {
        file.setReadOnly();
    }
    FileInputStream fileIs = new FileInputStream(file);
    FileDescriptor fd = null;
    try {
        fd = fileIs.getFD();
    } catch (IOException e) {
        HiLog.info(LABEL_LOG, "failed to getFD");
    }

    // 绑定文件描述符
    return MessageParcel.dupFileDescriptor(fd);
}

   >>> 数据库存储

     a. 初始化数据库连接。

      系统会在应用启动时调用onStart()方法创建Data实例。在此方法中,开发者应该创建数据库连接,并获取连接对象,以便后续和数据库进行操作。
      为了避免影响应用启动速度,开发者应当尽可能将非必要的耗时任务推迟到使用时执行,而不是在此方法中执行所有初始化。

      示例:初始化的时候连接数据库

private static final String DATABASE_NAME = "UserDataAbility.db";
private static final String DATABASE_NAME_ALIAS = "UserDataAbility";
private static final HiLogLabel LABEL_LOG = new HiLogLabel(HiLog.LOG_APP, 0xD00201, "Data_Log");
private OrmContext ormContext = null;

@Override
public void onStart(Intent intent) {
    super.onStart(intent);
    DatabaseHelper manager = new DatabaseHelper(this);
    ormContext = manager.getOrmContext(DATABASE_NAME_ALIAS, DATABASE_NAME, BookStore.class);
}

     b. 编写数据库操作方法。

      Ability定义了6个方法供用户处理对数据库表数据的增删改查。这6个方法在Ability中已默认实现,开发者可按需重写。

      

     

     

     

     

     ** UserDataAbility注册

      

原文地址:https://www.cnblogs.com/jieling/p/15434315.html