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的功能和使用的主要内容,如果未能解决你的问题,请参考以下文章