HarmonyOS核心理念之一就是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模板:用于对外部提供统一的数据访问抽象。
Page Ability
-
生命周期

-
添加路由
添加启动导航到的路由使用setMainRoute()(注意要在ability中设置路由,而不是abilitySlice),使用addActionRounte()添加别的页面导航。如下:
public class MyAbility extends Ability {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
// 设置主页面
setMainRoute(MainSlice.class.getName());
// 设置其它页面导航
addActionRoute("action.pay", PaySlice.class.getName());
}
}
当然,这些action都要现在config.json中注册:
"abilities": [
{
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.pay"
]
}
],
"orientation": "unspecified",
"name": "com.example.demo.MainAbility",
"icon": "$media:icon",
"description": "$string:mainability_description",
"label": "$string:entry_MainAbility",
"type": "page",
"launchType": "standard"
}
]
- 导航
同一page内导航
就是处于同一Page Ability下。可以使用present()方法实现导航。如果希望从导航目标返回时,获得返回结果,可以使用presentForResult()实现导航,返回时使用onResult()回调来接收和处理返回结果。如下:
@Override
protected void onStart(Intent intent) {
...
Button button = ...;
button.setClickedListener(listener -> present(new TargetSlice(), new Intent()));
button.setClickedListener(listener -> presentForResult(new TargetSlice(), new Intent()));
...
}
不同page内导航
可以通过配置Intent的Action导航到目标的AbilirySlice,page间的导航可以使用startAbility()或startAbilityForResult()方法,获得返回结果的回调为onAbilityResult()。在Ability中可以调用setResult()设置结果,和Android类似。
Service Ability
主要用于运行后台任务。
- 创建Service
代码示例:
public class ServiceAbility extends Ability {
// 创建Service调用,初始化,整个生命周期只会调用一次,调用时传入的Intent应该为空
@Override
public void onStart(Intent intent) {
super.onStart(intent);
}
// Service初始化调用,Service每次启动都会调用
@Override
public void onCommand(Intent intent, boolean restart, int startId) {
super.onCommand(intent, restart, startId);
}
// Ability连接服务时调用,类似Android中的bind
@Override
public IRemoteObject onConnect(Intent intent) {
return super.onConnect(intent);
}
// 断开连接
@Override
public void onDisconnect(Intent intent) {
super.onDisconnect(intent);
}
// Service销毁时调用
@Override
public void onStop() {
super.onStop();
}
}
注册:
{
"module": {
"abilities": [
{
"name": ".ServiceAbility",
"type": "service",
"visible": true
...
}
]
...
}
...
}
- 启动Service
1.启动
使用startAbility()方法来启动另一个Ability。不仅支持启动本地Service,还支持启动远程Service。开发者可以通过构造包含DeviceId、BundleName与AbilityName的Operation对象来设置目标Service信息。
DeviceId:表示设备ID,如果是本地设备可以留空;如果是远程设备,可以通过
ohos.distributedschedule.interwork.DeviceManager提供的getDeviceList获取设备列表。
BundleName:表示包名称。
AbilityName:表示待启动的Ability名称。
启动本地:
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);
启动远程:
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);
2.停止
可在服务中通过terminateAbilty()停止本Service或在其他Ability调用stopAbility()来停止Service。
- 连接Service
和Andrid如出一辙,连接Service需要创建IAbilityConnection来建立连接。在使用connectAbility()处理回调时,需要传入目标Service的Intent与IAbilityConnection的实例,它拥有两个回调:onAbilityConnectionDone()连接成功,onAbilityDisconnectionDone()是处理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对象,如下:
// 创建自定义IRemoteObject实现类
private class MyRemoteObject extends LocalRemoteObject {
MyRemoteObject(){
}
}
// 把IRemoteObject返回给客户端
@Override
protected IRemoteObject onConnect(Intent intent) {
return new MyRemoteObject();
}
- 生命周期
两种Service: - 启动Service,调用startAbility创建Service,调用stopAbility()停止Service。
- 连接Service,其他Ability调用connectionAbility()时创建,可以使用disconnectionAbility()断开连接,多个可绑定到同一个Service上,全部断开连接,Service销毁,好熟啊哈哈。

- 前台服务
使用前台Service并不复杂,开发者只需在Service创建的方法里,调用keepBackgroundRunning()将Service与通知绑定。调用keepBackgroundRunning()方法前需要在配置文件中声明ohos.permission.KEEP_BACKGROUND_RUNNING权限,同时还需要在配置文件中添加对应的backgroundModes参数。在onStop()方法中调用cancelBackgroundRunning()方法可停止前台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);
配置文件:
{
"name": ".ServiceAbility",
"type": "service",
"visible": true,
"backgroundModes": ["dataTransfer", "location"]
}
Data Ability(就是Android中的ContentProvider)
- 概念
使用Data模板的Ability有助于应用管理自身和其它应用存储数据的访问,并提供与其他应用共享数据的方法。
URI介绍
Scheme://[authority]/[path][?query][#fragement]
Scheme:协议方案名,固定为“dataability”,代表Data Ability所使用的协议类型。
authority:设备ID。本地不用填写。
path:资源的路径信息。
query:查询参数。
fragment:可以用于指示要访问的子资源。
示例:dataability://device_id/com.example.app/user/10 - 创建Data
- 确定数据存储方式
一般来说有两种:
- 文件数据:如文本、图片、音乐。
- 结构化数据:如数据库等。
- 实现UserrDataAbility
UserDataAbility用来接收其他应用发送的请求,提供外部程序访问的入口,从而实现应用间的数据访问。File > New > Ability > Empty Data Ability,设置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);
}
数据库存储
1.初始化数据连接
在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);
}
2.编写数据库操作方法。
Ability默认定义了6个方法供用户使用,默认已经实现,可以按需重写。

query():接收三个参数分别是查询的目标路径,查询的列名。以及查询条件,查询条件由DataAbilityPredicates构成。
public ResultSet query(Uri uri, String[] columns, DataAbilityPredicates predicates) {
if (ormContext == null) {
HiLog.error(LABEL_LOG, "failed to query, ormContext is null");
return null;
}
// 查询数据库
OrmPredicates ormPredicates = DataAbilityUtils.createOrmPredicates(predicates,User.class);
ResultSet resultSet = ormContext.query(ormPredicates, columns);
if (resultSet == null) {
HiLog.info(LABEL_LOG, "resultSet is null");
}
// 返回结果
return resultSet;
}
insert():两个参数,分别是插入的路径和插入的数据值。
public int insert(Uri uri, ValuesBucket value) {
// 参数校验
if (ormContext == null) {
HiLog.error(LABEL_LOG, "failed to insert, ormContext is null");
return -1;
}
// 构造插入数据
User user = new User();
user.setUserId(value.getInteger("userId"));
user.setFirstName(value.getString("firstName"));
user.setLastName(value.getString("lastName"));
user.setAge(value.getInteger("age"));
user.setBalance(value.getDouble("balance"));
// 插入数据库
boolean isSuccessful = ormContext.insert(user);
if (!isSuccessful) {
HiLog.error(LABEL_LOG, "failed to insert");
return -1;
}
isSuccessful = ormContext.flush();
if (!isSuccessful) {
HiLog.error(LABEL_LOG, "failed to insert flush");
return -1;
}
DataAbilityHelper.creator(this, uri).notifyChange(uri);
int id = Math.toIntExact(user.getRowId());
return id;
}
batchInsert():该方法为批量插入方法,接收一个ValuesBucket数组用于单次插入一组对象。
delete():删除条件由类DataAbilityPredicates构建。
public int delete(Uri uri, DataAbilityPredicates predicates) {
if (ormContext == null) {
HiLog.error(LABEL_LOG, "failed to delete, ormContext is null");
return -1;
}
OrmPredicates ormPredicates = DataAbilityUtils.createOrmPredicates(predicates,User.class);
int value = ormContext.delete(ormPredicates);
DataAbilityHelper.creator(this, uri).notifyChange(uri);
return value;
}
update():用户可以在ValuesBucket参数中指定要更新的数据,在DataAbilityPredicates中构建更新的条件等。
public int update(Uri uri, ValuesBucket value, DataAbilityPredicates predicates) {
if (ormContext == null) {
HiLog.error(LABEL_LOG, "failed to update, ormContext is null");
return -1;
}
OrmPredicates ormPredicates = DataAbilityUtils.createOrmPredicates(predicates,User.class);
int index = ormContext.update(ormPredicates, value);
HiLog.info(LABEL_LOG, "UserDataAbility update value:" + index);
DataAbilityHelper.creator(this, uri).notifyChange(uri);
return index;
}
executeBatch():此方法用来批量执行操作。DataAbilityOperation中提供了设置操作类型、数据和操作条件的方法,用户可自行设置自己要执行的数据库操作。
- 注册UserDataAbility
type:类型设置为data
uri:对外提供的访问路径,全局唯一
permissions:访问该data ability时需要申请的访问权限
{
"name": ".UserDataAbility",
"type": "data",
"visible": true,
"uri": "dataability://com.example.myapplication5.DataAbilityTest",
"permissions": [
"com.example.myapplication5.DataAbility.DATA"
]
}
- 访问Data
可以通过DataAbilityHelper类来访问当前应用或其他应用提供的共享数据。DataAbilityHelper作为客户端,与提供方的Data进行通信。
1.声明权限
如果访问的Data声明了访问需要权限,则访问此Data需要在配置文件需要此权限。
"reqPermissions": [
{
"name": "com.example.myapplication5.DataAbility.DATA"
},
// 访问文件还需要添加访问存储读写权限
{
"name": "ohos.permission.READ_USER_STORAGE"
},
{
"name": "ohos.permission.WRITE_USER_STORAGE"
}
]
2.创建DataAbilityHelper
DataAbilityHelper为开发者提供了creator()方法来创建DataAbilityHelper实例。静态方法。
DataAbilityHelper helper = DataAbilityHelper.creator(this);
访问文件:
// 读取文件描述符
FileDescriptor fd = helper.openFile(uri, "r");
FileInputStream fis = new FileInputStream(fd);
// 使用文件描述符封装成的文件流,进行文件操作
访问数据库:
上面说过各个方法创建时的应用。
query():
DataAbilityHelper helper = DataAbilityHelper.creator(this);
// 构造查询条件
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.between("userId", 101, 103);
// 进行查询
ResultSet resultSet = helper.query(uri, columns, predicates);
// 处理结果
resultSet.goToFirstRow();
do {
// 在此处理ResultSet中的记录;
} while(resultSet.goToNextRow());
insert():
DataAbilityHelper helper = DataAbilityHelper.creator(this);
// 构造插入数据
ValuesBucket valuesBucket = new ValuesBucket();
valuesBucket.putString("name", "Tom");
valuesBucket.putInteger("age", 12);
helper.insert(uri, valuesBucket);
batchInsert():
DataAbilityHelper helper = DataAbilityHelper.creator(this);
// 构造插入数据
ValuesBucket[] values = new ValuesBucket[2];
values[0] = new ValuesBucket();
values[0].putString("name", "Tom");
values[0].putInteger("age", 12);
values[1] = new ValuesBucket();
values[1].putString("name", "Tom1");
values[1].putInteger("age", 16);
helper.batchInsert(uri, values);
delete():
DataAbilityHelper helper = DataAbilityHelper.creator(this);
// 构造删除条件
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.between("userId", 101, 103);
helper.delete(uri, predicates);
update():
DataAbilityHelper helper = DataAbilityHelper.creator(this);
// 构造更新条件
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.equalTo("userId", 102);
// 构造更新数据
ValuesBucket valuesBucket = new ValuesBucket();
valuesBucket.putString("name", "Tom");
valuesBucket.putInteger("age", 12);
helper.update(uri, valuesBucket, predicates);
executeBatch():
DataAbilityHelper helper = DataAbilityHelper.creator(abilityObj, insertUri);
// 构造批量操作
ValuesBucket value1 = initSingleValue();
DataAbilityOperation opt1 = DataAbilityOperation.newInsertBuilder(insertUri).withValuesBucket(value1).build();
ValuesBucket value2 = initSingleValue2();
DataAbilityOperation opt2 = DataAbilityOperation.newInsertBuilder(insertUri).withValuesBucket(value2).build();
ArrayList<DataAbilityOperation> operations = new ArrayList<DataAbilityOperation>();
operations.add(opt1);
operations.add(opt2);
DataAbilityResult[] result = helper.executeBatch(insertUri, operations);
887

被折叠的 条评论
为什么被折叠?



