HarmonyOS之深入解析Ability的功能和使用

Posted Forever_wj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HarmonyOS之深入解析Ability的功能和使用相关的知识,希望对你有一定的参考价值。

一、Ability 概述

  • Ability 是应用所具备能力的抽象,也是应用程序的重要组成部分。一个应用可以具备多种能力(即可以包含多个 Ability),HarmonyOS 支持应用以 Ability 为单位进行部署。
  • Ability 可以分为 FA(Feature Ability)和 PA(Particle Ability)两种类型,每种类型为开发者提供了不同的模板,以便实现不同的业务功能。
  • FA 支持 Page Ability:Page 模板是 FA 唯一支持的模板,用于提供与用户交互的能力。一个 Page 实例可以包含一组相关页面,每个页面用一个 AbilitySlice 实例表示。
  • PA 支持 Service Ability 和 Data Ability:
    • Service 模板:用于提供后台运行任务的能力;
    • Data 模板:用于对外部提供统一的数据访问抽象。
  • 在配置文件(config.json)中注册 Ability 时,可以通过配置 Ability 元素中的“type”属性来指定 Ability 模板类型,其中,“type”的取值可以为“page”、“service”或“data”,分别代表 Page 模板、Service 模板、Data 模板,如下所示:
	{
	    "module": {
	        ...
	        "abilities": [
	            {
	                ...
	                "type": "page"
	                ...
	            }
	        ]
	        ...
	    }
	    ...
	}

二、Page Ability

① Page Ability 基本概念
  • Page 模板(以下简称“Page”)是 FA 唯一支持的模板,用于提供与用户交互的能力。
  • 一个 Page 可以由一个或多个 AbilitySlice 构成,AbilitySlice 是指应用的单个页面及其控制逻辑的总和。
  • 当一个 Page 由多个 AbilitySlice 共同构成时,这些 AbilitySlice 页面提供的业务能力应具有高度相关性。例如,新闻浏览功能可以通过一个 Page 来实现,其中包含了两个 AbilitySlice:一个 AbilitySlice 用于展示新闻列表,另一个 AbilitySlice 用于展示新闻详情。
  • Page 和 AbilitySlice 的关系下图所示:

在这里插入图片描述

  • 相比于桌面场景,移动场景下应用之间的交互更为频繁。通常,单个应用专注于某个方面的能力开发,当它需要其他能力辅助时,会调用其他应用提供的能力。例如,外卖应用提供了联系商家的业务功能入口,当用户在使用该功能时,会跳转到通话应用的拨号页面。
  • 与此类似,HarmonyOS 支持不同 Page 之间的跳转,并可以指定跳转到目标 Page 中某个具体的 AbilitySlice。
  • 虽然一个 Page 可以包含多个 AbilitySlice,但是 Page 进入前台时界面默认只展示一个 AbilitySlice,默认展示的 AbilitySlice 是通过 setMainRoute() 方法来指定的。
  • 如果需要更改默认展示的 AbilitySlice,可以通过 addActionRoute() 方法为此 AbilitySlice 配置一条路由规则,此时,当其它 Page 实例期望导航到此 AbilitySlice 时,可以在 Intent 中指定 Action。
  • setMainRoute() 方法与 addActionRoute() 方法的使用示例如下:
	public class MyAbility extends Ability {
	    @Override
	    public void onStart(Intent intent) {
	        super.onStart(intent);
	        // set the main route
	        setMainRoute(MainSlice.class.getName());
	
	        // set the action route
	        addActionRoute("action.pay", PaySlice.class.getName());
	        addActionRoute("action.scan", ScanSlice.class.getName());
	    }
	}
  • addActionRoute() 方法中使用的动作命名,需要在应用配置文件(config.json)中注册:
	{
	    "module": {
	        "abilities": [
	            {
	                "skills":[
	                    {
	                        "actions":[
	                            "action.pay",
	                            "action.scan"
	                        ]
	                    }
	                ]
	                ...
	            }
	        ]
	        ...
	    }
	    ...
	}
② Page Ability 生命周期
  • Page 生命周期的不同状态转换及其对应的回调,如下图所示:

在这里插入图片描述

  • onStart()
    • 当系统首次创建Page实例时,触发该回调。
    • 对于一个 Page 实例,该回调在其生命周期过程中仅触发一次,Page 在该逻辑后将进入 INACTIVE 状态。
    • 开发者必须重写该方法,并在此配置默认展示的 AbilitySlice。
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setMainRoute(FooSlice.class.getName());
    }
  • onActive()
    • Page 会在进入INACTIVE状态后来到前台,然后系统调用此回调。
    • Page 在此之后进入 ACTIVE 状态,该状态是应用与用户交互的状态。Page 将保持在此状态,除非某类事件发生导致 Page 失去焦点,比如用户点击返回键或导航到其他 Page。当此类事件发生时,会触发 Page 回到 INACTIVE 状态,系统将调用 onInactive() 回调。此后,Page 可能重新回到 ACTIVE 状态,系统将再次调用 onActive() 回调。
    • 因此,开发者通常需要成对实现 onActive() 和 onInactive(),并在 onActive() 中获取在 onInactive() 中被释放的资源。
  • onInactive()
    • 当 Page 失去焦点时,系统将调用此回调,此后 Page 进入 INACTIVE状态。
    • 开发者可以在此回调中实现 Page 失去焦点时应表现的恰当行为。
  • onBackground()
    • 如果 Page 不再对用户可见,系统将调用此回调通知开发者用户进行相应的资源释放,此后 Page 进入 BACKGROUND 状态。
    • 开发者应该在此回调中释放 Page 不可见时无用的资源,或在此回调中执行较为耗时的状态保存操作。
  • onForeground()
    • 处于 BACKGROUND 状态的 Page 仍然驻留在内存中,当重新回到前台时(比如用户重新导航到此 Page),系统将先调用 onForeground() 回调通知开发者,而后 Page 的生命周期状态回到 INACTIVE 状态。
    • 开发者应当在此回调中重新申请在 onBackground() 中释放的资源,最后 Page 的生命周期状态进一步回到 ACTIVE 状态,系统将通过 onActive() 回调通知开发者用户。
  • onStop()
    • 系统将要销毁 Page 时,将会触发此回调函数,通知用户进行系统资源的释放。
    • 销毁 Page 的可能原因包括以下几个方面:
      • 用户通过系统管理能力关闭指定 Page,例如使用任务管理器关闭 Page。
      • 用户行为触发 Page 的 terminateAbility() 方法调用,例如使用应用的退出功能。
      • 配置变更导致系统暂时销毁 Page 并重建。
      • 系统出于资源管理目的,自动触发对处于 BACKGROUND 状态 Page 的销毁。
  • AbilitySlice 作为 Page 的组成单元,其生命周期是依托于其所属 Page 生命周期的。AbilitySlice 和 Page 具有相同的生命周期状态和同名的回调,当 Page 生命周期发生变化时,它的 AbilitySlice 也会发生相同的生命周期变化。
  • 此外,AbilitySlice 还具有独立于 Page 的生命周期变化,这发生在同一 Page 中的 AbilitySlice 之间导航时,此时 Page 的生命周期状态不会改变。
  • AbilitySlice 生命周期回调与 Page 的相应回调类似,因此不再赘述。由于 AbilitySlice 承载具体的页面,开发者必须重写 AbilitySlice的onStart() 回调,并在此方法中通过 setUIContent() 方法设置页面,如下所示:
    @Override
    protected void onStart(Intent intent) {
        super.onStart(intent);

        setUIContent(ResourceTable.Layout_main_layout);
    }
  • AbilitySlice 实例创建和管理通常由应用负责,系统仅在特定情况下会创建 AbilitySlice 实例。例如,通过导航启动某个 AbilitySlice 时,是由系统负责实例化;但是在同一个 Page 中不同的 AbilitySlice 间导航时则由应用负责实例化。
  • 当 AbilitySlice 处于前台且具有焦点时,其生命周期状态随着所属 Page 的生命周期状态的变化而变化。当一个 Page 拥有多个 AbilitySlice 时,例如:MyAbility 下有 FooAbilitySlice 和 BarAbilitySlice,当前 FooAbilitySlice 处于前台并获得焦点,并即将导航到 BarAbilitySlice,在此期间的生命周期状态变化顺序为:
    • FooAbilitySlice 从 ACTIVE 状态变为 INACTIVE 状态;
    • BarAbilitySlice 则从 INITIAL 状态首先变为 INACTIVE 状态,然后变为 ACTIVE 状态(假定此前 BarAbilitySlice 未曾启动);
    • FooAbilitySlice 从 INACTIVE 状态变为 BACKGROUND 状态。
  • 对应两个slice的生命周期方法回调顺序为:
    FooAbilitySlice.onInactive() --> BarAbilitySlice.onStart() --> BarAbilitySlice.onActive() --> FooAbilitySlice.onBackground()
  • 在整个流程中,MyAbility 始终处于 ACTIVE 状态。但是,当 Page 被系统销毁时,其所有已实例化的 AbilitySlice 将联动销毁,而不仅是处于前台的 AbilitySlice。
③ AbilitySlice 间导航
  • 同一 Page 内导航:当发起导航的 AbilitySlice 和导航目标的 AbilitySlice 处于同一个 Page 时,您可以通过 present() 方法实现导航。如下代码片段展示通过点击按钮导航到其他 AbilitySlice 的方法:
	@Override
	protected void onStart(Intent intent) {
	
	    ...
	    Button button = ...;
	    button.setClickedListener(listener -> present(new TargetSlice(), new Intent()));
	    ...
	
	}
  • 如果开发者希望在用户从导航目标 AbilitySlice 返回时,能够获得其返回结果,则应当使用 presentForResult() 实现导航。用户从导航目标 AbilitySlice 返回时,系统将回调 onResult() 来接收和处理返回结果,开发者需要重写该方法。返回结果由导航目标 AbilitySlice 在其生命周期内通过 setResult() 进行设置。
	@Override
	protected void onStart(Intent intent) {
	
	    ...
	    Button button = ...;
	    button.setClickedListener(listener -> presentForResult(new TargetSlice(), new Intent(), 0));
	    ...
	
	}
	
	@Override
	protected void onResult(int requestCode, Intent resultIntent) {
	    if (requestCode == 0) {
	        // Process resultIntent here.
	    }
	}
  • 系统为每个 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 为例,迁移动作主要步骤如下:
    • 设备 A 上的 Page 请求迁移;
    • HarmonyOS 处理迁移任务,并回调设备 A 上 Page 的保存数据方法,用于保存迁移必须的数据;
    • HarmonyOS 在设备 B 上启动同一个 Page,并回调其恢复数据方法。
  • 实现 IAbilityContinuation 接口:
    • onStartContinuation():Page 请求迁移后,系统首先回调此方法,开发者可以在此回调中决策当前是否可以执行迁移,比如,弹框让用户确认是否开始迁移;
    • onSaveData():如果 onStartContinuation() 返回 true,则系统回调此方法,开发者在此回调中保存必须传递到另外设备上以便恢复 Page 状态的数据。
    • onRestoreData():源侧设备上 Page 完成保存数据后,系统在目标侧设备上回调此方法,开发者在此回调中接受用于恢复 Page 状态的数据。注意,在目标侧设备上的Page会重新启动其生命周期,无论其启动模式如何配置。且系统回调此方法的时机在 onStart() 之前。
    • onCompleteContinuation():目标侧设备上恢复数据一旦完成,系统就会在源侧设备上回调 Page 的此方法,以便通知应用迁移流程已结束。开发者可以在此检查迁移结果是否成功,并在此处理迁移结束的动作,例如,应用可以在迁移完成后终止自身生命周期。
    • onRemoteTerminated():如果开发者使用 continueAbilityReversibly() 而不是 continueAbility(),则此后可以在源侧设备上使用 reverseContinueAbility() 进行回迁。这种场景下,相当于同一个 Page(的两个实例)同时在两个设备上运行,迁移完成后,如果目标侧设备上 Page 因任何原因终止,则源侧 Page 通过此回调接收终止通知。
  • 请求迁移:
    • 实现 IAbilityContinuation 的 Page 可以在其生命周期内,调用 continueAbility() 或 continueAbilityReversibly() 请求迁移。两者的区别是,通过后者发起的迁移此后可以进行回迁。
	try {
	    continueAbility();
	} catch (IllegalStateException e) {
	    // Maybe another continuation in progress.
	    ...
	}
    • 以 Page 从设备 A 迁移到设备 B 为例,详细的流程如下:
      • 设备 A 上的 Page 请求迁移;
      • 系统回调设备 A 上 Page 及其 AbilitySlice 栈中所有 AbilitySlice 实例的 IAbilityContinuation.onStartContinuation() 方法,以确认当前是否可以立即迁移。
      • 如果可以立即迁移,则系统回调设备 A 上 Page 及其 AbilitySlice 栈中所有 AbilitySlice 实例的 IAbilityContinuation.onSaveData() 方法,以便保存迁移后恢复状态必须的数据。
      • 如果保存数据成功,则系统在设备 B 上启动同一个 Page,并恢复 AbilitySlice 栈,然后回调 IAbilityContinuation.onRestoreData() 方法,传递此前保存的数据;此后设备 B 上此 Page从onStart() 开始其生命周期回调。
      • 系统回调设备 A 上 Page 及其 AbilitySlice 栈中所有 AbilitySlice 实例的 IAbilityContinuation.onCompleteContinuation() 方法,通知数据恢复成功与否。
  • 请求回迁
    • 使用 continueAbilityReversibly() 请求迁移并完成后,源侧设备上已迁移的 Page 可以发起回迁,以便使用户活动重新回到此设备。
	try {
	    reverseContinueAbility();
	} catch (IllegalStateException e) {
	    // Maybe another continuation in progress.
	    ...
	}
    • 以 Page 从设备 A 迁移到设备 B 后并请求回迁为例,详细的流程如下:
      • 设备 A 上的 Page 请求回迁;
      • 系统回调设备 B 上 Page 及其 AbilitySlice 栈中所有 AbilitySlice 实例的 IAbilityContinuation.onStartContinuation() 方法,以确认当前是否可以立即迁移;
      • 如果可以立即迁移,则系统回调设备 B 上 Page 及其 AbilitySlice 栈中所有 AbilitySlice 实例的 IAbilityContinuation.onSaveData() 方法,以便保存回迁后恢复状态必须的数据;
      • 如果保存数据成功,则系统在设备 A 上 Page 恢复 AbilitySlice 栈,然后回调 IAbilityContinuation.onRestoreData() 方法,传递此前保存的数据;
      • 如果数据恢复成功,则系统终止设备 B 上 Page 的生命周期。

三、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 应通过实现此方法来清理任何资源,如关闭线程、注册的侦听器等。
  • 创建 Service 的代码示例如下:
	public class ServiceAbility extends Ability {
	    @Override
	    public void onStart(Intent intent) {
	        super.onStart(intent);
	    }
	
	    @Override
	    public void onCommand(Intent intent, boolean restart, int startId) {
	        super.onCommand(intent, restart, startId);
	    }
	
	    @Override
	    public IRemoteObject onConnect(Intent intent) {
	        return super.onConnect(intent);
	    }
	
	    @Override
	    public void onDisconnect(Intent intent) {
	        super.onDisconnect(intent);
	    }
	
	    @Override
	    public void onStop() {
	        super.onStop();
	    }
	}
  • 注册 Service:Service 也需要在应用配置文件中进行注册,注册类型 type 需要设置为 service:
	{
	    "module": {
	        "abilities": [         
	            {    
	                "name": ".ServiceAbility",
	                "type": "service",
	                "visible": true
	                ...
	            }
	        ]
	        ...
	    }
	    ...
	}
③ 启动 Service
  • Ability 为开发者提供了 startAbility() 方法来启动另外一个 Ability,因为 Service 也是 Ability 的一种,开发者同样可以通过将 Intent 传递给该方法来启动 Service。不仅支持启动本地 Service,还支持启动远程 Service。
  • 开发者可以通过构造包含 DeviceId、BundleName 与 AbilityName 的 Operation 对象来设置目标 Service 信息,这三个参数的含义如下:
    • DeviceId:表示设备 ID,如果是本地设备,则可以直接留空;如果是远程设备,可以通过 ohos.distributedschedule.interwork.DeviceManager 提供的 getDeviceList 获取设备列表;
    • BundleName:表示包名称;
    • AbilityName:表示待启动的 Ability 名称。
  • 启动本地设备 Service 的代码示例如下:
	Intent intent = new Intent();
	Operation operation = new Intent.OperationBuilder()
	        .withDeviceId("")
	        .withBundleName("com.domainname.hiworld.himusic")
	        .withAbilityName("com.domainname.hiworld.himusic.ServiceAbility")
	        .build();
	intent.setOperation(operation);
	startAbility(intent);
  • 启动远程设备 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);
  • 执行上述代码后,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。
④ 链接 Service
  • 如果 Service 需要与 Page Ability 或其他应用的 Service Ability 进行交互,则须创建用于连接的 Connection。Service 支持其他 Ability 通过 connectAbility() 方法与其进行连接。
  • 在使用 connectAbility() 处理回调时,需要传入目标 Service 的 Intent 与 IAbilityConnection 的实例。IAbilityConnection 提供了两个方法供开发者实现:onAbilityConnectDone() 是用来处理连接 Service 成功的回调, onAbilityDisconnectDone() 是用来处理 Service 异常死亡的回调。
  • 创建连接 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。
⑥ 前台 Service
  • 一般情况下,Service都是在后台运行的,后台 Service 的优先级都是比较低的,当资源不足时,系统有可能回收正在运行的后台 Service。
  • 在一些场景下(如播放音乐),用户希望应用能够一直保持运行,此时就需要使用前台 Service,前台 Service 会始终保持正在运行的图标在系统状态栏显示。
  • 使用前台 Service 并不复杂,开发者只需在 Service 创建的方法里,调用 keepBackgroundRunning() 将 Service 与通知绑定。调用 keepBackgroundRunning() 方法前需要在配置文件中声明 ohos.permission.KEEP_BACKGROUND_RUNNING 权限,同时还需要在配置文件中添加对应的 backgroundModes 参数。在 onStop() 方法中调用 cancelBackgroundRunning​() 方法可停止前台 Service。
  • 使用前台 Service的onStart() 代码示例如下:
	// 创建通知,其中1005为notificationId
	NotificationRequest request = new NotificationRequest(1005);
	NotificationRequest.NotificationNormalContent content = new NotificationRequest.NotificationNormalContent();
	content.setTitle("title").setText("text");
	NotificationRequest.NotificationContent notificationContent = new NotificationRequest.NotificationContent(content);
	request.setContent(notificationContent);
	
	// 绑定通知,1005为创建通知时传入的notificationId
	keepBackgroundRunning(1005, request);
  • 在配置文件中,“module > abilities”字段下对当前 Service 做如下配置:
	{    
	    "name": ".ServiceAbility",
	    "type": "service",
	    "visible": true,
	    "backgroundModes": ["dataTransfer", "location"]
	}

四、Data Ability

① Data Ability 基本概念
  • 使用 Data 模板的 Ability(以下简称“Data”)有助于应用管理其自身和其他应用存储数据的访问,并提供与其他应用共享数据的方法。Data 既可用于同设备不同应用的数据共享,也支持跨设备不同应用的数据共享。
  • 数据的存放形式多样,可以是数据库,也可以是磁盘上的文件。Data 对外提供对数据的增、删、改、查,以及打开文件等接口,这些接口的具体实现由开发者提供。
  • Data 的提供方和使用方都通过 URI(Uniform Resource Identifier)来标识一个具体的数据,例如数据库中的某个表或磁盘上的某个文件。 HarmonyOS 的 URI 仍基于 URI 通用标准,格式如下:

在这里插入图片描述

  • 参数说明:
    • scheme:协议方案名,固定为“dataability”,代表 Data Ability 所使用的协议类型。
    • authority:设备 ID,如果为跨设备场景,则为目标设备的 ID;如果为本地设备场景,则不需要填写。
    • path:资源的路径信息,代表特定资源的位置信息。
    • query:查询参数。
    • fragment:可以用于指示要访问的子资源。
② 创建 Data
  • 确定数据的存储方式,Data 支持以下两种数据形式:
    • 文件数据:如文本、图片、音乐等;
    • 结构化数据:如数据库等。
  • 实现 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”(读写)等。
      • ohos.rpc.MessageParcel 类提供了一个静态方法,用于获取 MessageParcel 实例。开发者可通过获取到的 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 {
	    // 创建messageParcel
	    MessageParcel messageParcel = MessageParcel.obtain();
	    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);
	}
    • 数据库存储
      • 初始化数据库连接,系统会在应用启动时调用 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(以上是关于HarmonyOS之深入解析Ability的功能和使用的主要内容,如果未能解决你的问题,请参考以下文章

HarmonyOS之深入解析视频的功能和使用

HarmonyOS之深入解析音频的功能和使用

HarmonyOS之深入解析相机的功能和使用

HarmonyOS之深入解析WLAN的功能和使用

HarmonyOS之深入解析蓝牙Bluetooth的功能和使用

HarmonyOS之深入解析设备标识符的功能和使用