OpenHarmony_Ability框架详解
开发环境 IDE:DevEco Studio 3.0 Release(Build Version: 3.0.0.993) SDK:Api Version 8和9 Ability框架 概述 Ability是应用所具备能力的抽象,也是应用程序的重要组成部分。Ability是系统调度应用的最小单元,是能够完成一个独立功能的组件。一个应用可以包含一个或多个Ability。 类型 Abi
开发环境
IDE
SDK:Api Version 8和9
Ability框架
概述
Ability是应用所具备能力的抽象,也是应用程序的重要组成部分。Ability是系统调度应用的最小单元,是能够完成一个独立功能的组件。一个应用可以包含一个或多个Ability。
类型
Ability的类型存在差异:
Ability框架模型具有两种形态:
-
FA模型
API 8及其更早版本的应用程序只能使用FA模型进行开发。FA模型将Ability分为FA(Feature Ability)和PA(Particle Ability)两种类型,其中其中FA支持PageAbility,PA支持ServiceAbility、DataAbility、以及FormAbility。
-
Stage模型
从API 9开始,Ability框架引入了Stage模型作为第二种应用框架形态,Stage模型将Ability分为PageAbility和ExtensionAbility两大类,其中ExtensionAbility又被扩展为ServiceExtensionAbility、FormExtensionAbility、DataShareExtensionAbility等一系列ExtensionAbility,以便满足更多的使用场景。
基础用法
FA模型
PageAbility
PageAbility是具备ArkUI实现的Ability,是开发者具体可见并可以与之交互的Ability实例。开发者通过DevEco Studio创建Ability时,DevEco Studio会自动创建相关模板代码。 PageAbility相关能力通过单独的featureAbility实现,生命周期相关回调则通过app.js/app.ets中各个回调函数实现。
创建PageAbility
-
首次创建工程,打开DevEco Studio选择Create Project(如果在已有的工程界面创建新工程,则选择File->New->Create Project),然后选择OpenHarmony下的Empty Ability模板,点击Next进行下一步配置。
-
进入工程配置界面,按默认参数设置即可,点击Finish,工具会自动生成示例代码和相关资源,等待工程创建成功。
-
工程创建成功后生成一个默认的PageAbility,名为MainAbility。
-
如需在工程中创建多个PageAbility,点击File->New->Ability->Page Ability即可。
featureAbility接口说明
接口名 | 描述 |
---|---|
void: startAbility(parameter: StartAbilityParameter) | 启动Ability。 |
Context: getContext(): | 获取应用Context。 |
void: terminateSelf() | 结束Ability。 |
bool: hasWindowFocus() | 是否获取焦点。 |
启动PageAbility
使用featureAbility.startAbility方法可以启动本地设备的PageAbility。
-
导入模块
import featureAbility from '@ohos.ability.featureAbility'
-
调用featureAbility.startAbility方法,由该方法的Want传递bundleName和abilityName属性来启动PageAbility。
-
bundleName:表示包名称。可在config.json中的app字段查看bundleName,或者通过File->Project Structure查看。
-
abilityName:表示待启动的Ability名称,由config.json中的package字段与abilities字段下的name字段组成。
featureAbility.startAbility({
want: {
bundleName: "com.samples.myapplication",
// FA模型abilityName由config.json中的package + Ability name组成
abilityName: "com.samples.myapplication.MainAbility"
}
});
ServiceAbility
基于Service模板的Ability(以下简称“Service”),主要用于后台运行任务(如执行音乐播放、文件下载等),但不提供用户UI交互界面。Service可由其他应用或Ability启动。即使用户切换到其他应用,Service仍将在后台继续运行。
接口说明
Service中相关生命周期API功能介绍
接口名 | 描述 |
---|---|
onStart?(): void | 该方法在创建Service的时候调用,用于Service的初始化。在Service的整个生命周期只会调用一次,调用时传入的Want应为空。 |
onCommand?(want: Want, startId: number): void | 在Service创建完成之后调用,该方法在客户端每次启动该Service时都会调用,开发者可以在该方法中做一些调用统计、初始化类的操作。 |
onConnect?(want: Want): rpc.RemoteObject | 在Ability和Service连接时调用。 |
onDisconnect?(want: Want): void | 在Ability与绑定的Service断开连接时调用。 |
onStop?(): void | 在Service销毁时调用。开发者应通过实现此方法来清理资源,如关闭线程、注册的侦听器等。 |
创建与注册Service
-
在工程中,选择File->New->Ability->Service Ability,点击Finish,成功创建ServiceAbility,默认生成service.ts文件。
-
重写Service的生命周期方法,来添加其他Ability请求与ServiceAbility交互时的处理方法。
// service.ts
import hilog from '@ohos.hilog';
export default {
onStart() {
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', '%{public}s', 'ServiceAbility onStart');
},
onStop() {
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', '%{public}s', 'ServiceAbility onStop');
},
onCommand(want, startId) {
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', '%{public}s', 'ServiceAbility onCommand');
},
onConnect(want) {
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', '%{public}s', 'ServiceAbility onConnect');
return null;
},
onDisconnect(want) {
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', '%{public}s', 'ServiceAbility OnDisConnect');
}
};
-
注册Service。FA模型在创建ServiceAbility时已在config.json中自动注册,注册类型type为service,只需手动添加visible字段并设置为true。
config.json配置样例
// config.json
"module": {
...
"abilities": [
...
{
"name": ".ServiceAbility",
"srcLanguage": "ets",
"srcPath": "ServiceAbility",
"icon": "$media:icon",
"description": "$string:ServiceAbility_desc",
"type": "service",
"visible": true
}
...
]
...
}
Json重要字段
字段名 | 备注说明 | |
---|---|---|
"name" | Ability名称,对应Ability派生的Service类名。 | |
"type" | Ability类型,Service对应的Ability类型为”service“。 | |
"visible" | 指定能力是否可被其他应用调用,默认值为false,设置为true时,Service才能与其他应用进行调用。 |
启动Service
Ability为开发者提供了startAbility()方法来启动另外一个Ability,因为Service也是Ability的一种,开发者同样可以通过将want传递给该方法来启动Service,通过构造包含bundleName与abilityName的Want对象来设置目标Service信息。
启动本地设备Service示例如下:
import featureAbility from '@ohos.ability.featureAbility';
featureAbility.startAbility({
want:
{
bundleName: "com.samples.myapplication",
// FA模型abilityName由config.json中的package + Ability name组成
abilityName: "com.samples.myapplication.ServiceAbility",
}
}
);
执行上述代码后,Ability将通过startAbility()方法来启动Service。
-
如果Service尚未运行,则系统会先调用onStart()来初始化Service,再回调Service的onCommand()方法来启动Service。
-
如果Service正在运行,则系统会直接回调Service的onCommand()方法来启动Service。
连接Service
如果Service需要与Page Ability或其他应用的Service Ability进行交互,则须创建用于连接的Connection。Service支持其他Ability通过connectAbility()方法将当前Ability连接到指定ServiceAbility。在使用connectAbility()处理回调时,需要传入目标Service的Want与ConnectOptions远程对象实例。
ConnectOptions远程对象实例提供了以下方法:
-
onConnect():处理连接Service成功的回调。
-
onDisconnect():处理Service异常死亡的回调。
-
onFailed():处理连接Service失败的回调。
创建连接本地Service回调实例以及连接本地Service的示例如下:
import prompt from '@ohos.prompt';
import featureAbility from '@ohos.ability.featureAbility';
// 导入rpc进程通信模块
import rpc from "@ohos.rpc";
connectService() {
// ConnectOptions远程对象实例
let connectOptions = {
// 连接成功时回调
onConnect: function onConnectCallback(element, proxy) {
console.log(`onConnectLocalService onConnectDone element:${element}`)
if (proxy === null) {
prompt.showToast({
message: 'Connect service failed'
})
return
}
// 携带客户端调用参数的MessageParcel对象
let data = rpc.MessageParcel.create()
// 接收应答数据的MessageParcel对象
let reply = rpc.MessageParcel.create()
// 本次请求的同异步模式,默认为TF_SYNC同步调用
let option = new rpc.MessageOption()
// 将字符串值写入MessageParcel实例
data.writeString("connect")
// 以同步模式向服务端发送MessageParcel消息,发送的消息代码为1,同步模式下该接口将等待服务端的响应,直到请求超时,响应的结果在reply中
proxy.sendRequest(1, data, reply, option).then((result) => {
console.log(`sendRequest: ${result}`);
// 客户端以reply从MessageParcel实例中读取服务端处理后的字符串值
let msg = reply.readString();
console.log(`sendRequest msg: ${msg}`);
}).catch((err) => {
console.log(`sendRequest error: ${err}`);
});
prompt.showToast({
message: "Connect service success"
})
},
// 连接断开时回调
onDisconnect: function onDisconnectCallback(element) {
console.log(`onConnectLocalService onDisconnectDone element:${element}`)
prompt.showToast({
message: "Disconnect service success"
})
},
// 连接失败时回调
onFailed: function onFailedCallback(code) {
console.log(`onConnectLocalService onFailed errCode:${code}`)
prompt.showToast({
message: "Connect local service onFailed"
})
}
}
// want参数
let want = {
bundleName: 'com.samples.myapplication',
// FA模型abilityName由config.json中的package + Ability name组成
abilityName: 'com.samples.myapplication.ServiceAbility'
};
// 连接本地Service
featureAbility.connectAbility(want, connectOptions);
}
同时,Service服务端也需要在onConnect()时返回IRemoteObject,从而定义与Service进行通信的接口。OpenHarmony提供了IRemoteObject的默认实现,可以通过继承rpc.RemoteObject来创建自定义的实现类。
Service服务端把自身的实例返回给调用侧的代码示例如下:
// service.ts
import rpc from '@ohos.rpc';
import hilog from '@ohos.hilog';
// 实现远程对象,服务提供者必须继承RemoteObject类
class FirstServiceAbilityStub extends rpc.RemoteObject {
// 用于创建RemoteObject实例的构造函数,参数des为指定接口描述符
constructor(des: any) {
super(des)
}
// sendRequest请求的响应处理函数,服务端在该函数里处理请求,回复结果
onRemoteRequest(code, data, reply, option) {
console.log(`onRemoteRequest called`)
// 判断客户端发送过来的消息代码是否为1
if (code === 1) {
// 从MessageParcel实例中读取由客户端写入的字符串值
let string = data.readString()
console.log(`string=${string}`)
// 将读取的字符串作拆分和排序处理
let result = Array.from(string).sort().join('')
console.log(`result=${result}`)
// 将处理后的字符串值写入MessageParcel实例
reply.writeString(result)
} else {
console.log(`unknown request code`)
}
return true;
}
};
export default {
onStart() {
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', '%{public}s', 'ServiceAbility onStart');
},
onStop() {
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', '%{public}s', 'ServiceAbility onStop');
},
onCommand(want, startId) {
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', '%{public}s', 'ServiceAbility onCommand');
},
onConnect(want) {
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', '%{public}s', 'ServiceAbility onConnect');
// 返回RemoteObject对象与描述符
return new FirstServiceAbilityStub('first ts service stub');
},
onDisconnect(want) {
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', '%{public}s', 'ServiceAbility OnDisConnect');
}
};
DataAbility
基于Data模板的Ability(以下简称“Data”),有助于应用管理其自身和其他应用存储数据的访问,并提供与其他应用共享数据的方法,但不提供用户UI交互界面。Data既可用于同设备不同应用的数据共享,也支持跨设备不同应用的数据共享。
Data提供方可以自定义数据的增、删、改、查,以及文件打开等功能,并对外提供这些接口。
接口说明
Data中相关生命周期API功能介绍
接口名 | 描述 |
---|---|
onInitialized?(info: AbilityInfo): void | 在Ability初始化调用,通过此回调方法执行RDB等初始化操作。 |
update?(uri: string, valueBucket: rdb.ValuesBucket, predicates: dataAbility.DataAbilityPredicates, callback: AsyncCallback<number>): void | 更新数据库中的数据。 |
query?(uri: string, columns: Array<string>, predicates: dataAbility.DataAbilityPredicates, callback: AsyncCallback<ResultSet>): void | 查询数据库中的数据。 |
delete?(uri: string, predicates: dataAbility.DataAbilityPredicates, callback: AsyncCallback<number>): void | 删除一条或多条数据。 |
normalizeUri?(uri: string, callback: AsyncCallback<string>): void | 对uri进行规范化。一个规范化的uri可以支持跨设备使用、持久化、备份和还原等,当上下文改变时仍然可以引用到相同的数据项。 |
batchInsert?(uri: string, valueBuckets: Array<rdb.ValuesBucket>, callback: AsyncCallback<number>): void | 向数据库中插入多条数据。 |
denormalizeUri?(uri: string, callback: AsyncCallback<string>): void | 将一个由normalizeUri生产的规范化URI转换成非规范化的uri。 |
insert?(uri: string, valueBucket: rdb.ValuesBucket, callback: AsyncCallback<number>): void | 向数据中插入一条数据。 |
openFile?(uri: string, mode: string, callback: AsyncCallback<number>): void | 打开一个文件。 |
getFileTypes?(uri: string, mimeTypeFilter: string, callback: AsyncCallback<Array<string>>): void | 获取文件的MIME类型。 |
getType?(uri: string, callback: AsyncCallback<string>): void | 获取uri指定数据相匹配的MIME类型。 |
executeBatch?(ops: Array<DataAbilityOperation>, callback: AsyncCallback<Array<DataAbilityResult>>): void | 批量操作数据库中的数据。 |
call?(method: string, arg: string, extras: PacMap, callback: AsyncCallback<PacMap>): void | 自定义方法。 |
创建与注册Data
-
在工程中,选择File->New->Ability->Data Ability,点击Finish,成功创建DataAbility,默认生成data.ts文件。
-
在data.ts文件中,实现Data的insert、query、update、delete等相关接口的业务内容,保证能够满足数据库存储业务的基本需求。batchInsert与executeBatch接口在系统中实现遍历逻辑,依赖insert、query、update、delete接口逻辑,来实现数据的批量处理。
创建Data示例如下:
// data.ts
import hilog from '@ohos.hilog';
import featureAbility from '@ohos.ability.featureAbility';
// 导入DataAbility谓词相关模块
import dataAbility from '@ohos.data.dataAbility';
// 导入rdb关系数据库模块
import dataRdb from '@ohos.data.rdb';
// 表名
const TABLE_NAME = 'book';
// 库的配置信息:库名称
const STORE_CONFIG = { name: 'book.db' };
// 表结构
const SQL_CREATE_TABLE = 'CREATE TABLE IF NOT EXISTS book(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, introduction TEXT NOT NULL)';
// rdb库对象
let rdbStore: dataRdb.RdbStore = undefined;
export default {
// 初始化库和表
onInitialized(abilityInfo) {
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', '%{public}s', 'DataAbility onInitialized');
// 获取应用上下文
let context = featureAbility.getContext();
// 获取一个相关的RdbStore,操作关系型数据库
dataRdb.getRdbStore(context, STORE_CONFIG, 1).then((store) => {
console.info('DataAbility getRdbStore callback');
// 执行包含指定参数但不返回值的SQL语句
store.executeSql(SQL_CREATE_TABLE, null);
// 把获得的数据库存入本地变量,防止后面多次操作读取创建
rdbStore = store;
});
},
// 向数据库中插入一条数据
insert(uri, valueBucket, callback) {
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', '%{public}s', 'DataAbility insert');
rdbStore.insert(TABLE_NAME, valueBucket, callback);
},
// 向数据库中插入多条数据
batchInsert(uri, valueBuckets, callback) {
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', '%{public}s', 'DataAbility batchInsert');
for (let i = 0; i < valueBuckets.length; i++) {
console.info('DataAbility batch insert i=' + i)
if (i < valueBuckets.length - 1) {
rdbStore.insert(TABLE_NAME, valueBuckets[i], (err: any, num: number) => {
console.info('DataAbility batch insert ret=' + num)
})
} else {
rdbStore.insert(TABLE_NAME, valueBuckets[i], callback)
}
}
},
// 查询数据库中的数据
query(uri, columns, predicates, callback) {
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', '%{public}s', 'DataAbility query');
let rdbPredicates = dataAbility.createRdbPredicates(TABLE_NAME, predicates);
rdbStore.query(rdbPredicates, columns, callback);
},
// 更新数据库中的数据
update(uri, valueBucket, predicates, callback) {
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', '%{public}s', 'DataAbility update');
let rdbPredicates = dataAbility.createRdbPredicates(TABLE_NAME, predicates);
rdbStore.update(valueBucket, rdbPredicates, callback);
},
// 删除一条或多条数据
delete(uri, predicates, callback) {
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', '%{public}s', 'DataAbility delete');
let rdbPredicates = dataAbility.createRdbPredicates(TABLE_NAME, predicates);
rdbStore.delete(rdbPredicates, callback);
}
};
-
注册Data。FA模型在创建DataAbility时已在config.json中自动注册,注册类型type为data,visible字段需手动添加并设置为true。注册后会生成默认权限defPermissions字段,表明调用Data之前须配置这些权限,添加reqPermissions字段设置相应权限。
config.json配置样例
"module": {
"package": "com.samples.myapplication",
...
"abilities": [
...
{
"name": ".DataAbility",
"srcLanguage": "ets",
"srcPath": "DataAbility",
"icon": "$media:icon",
"description": "$string:DataAbility_desc",
"type": "data",
"uri": "dataability://com.samples.myapplication.DataAbility",
"visible": true
}
],
...
"defPermissions": [
{
"name": "com.example.entry.DataAbilityShellProvider.PROVIDER"
}
],
"reqPermissions": [
{
"name": "com.example.entry.DataAbilityShellProvider.PROVIDER"
}
]
...
}
Json重要字段
字段名 | 备注说明 |
---|---|
"name" | Ability名称,对应Ability派生的Data类名。 |
"type" | Ability类型,Data对应的Ability类型为”data“。 |
"uri" | 通信使用的uri。URL后缀由"package"字段和"name"字段组成。 |
"visible" | 指定能力是否可被其他应用调用,默认值为false,设置为true时,Data才能与其他应用进行通信传输数据。 |
"defPermissions" | 表示应用程序默认需要的权限,调用者在调用Data之前必须获取相应权限。 |
访问Data
需导入基础模块,以及获取与Data子模块通信的uri字符串。基础模块包括:
-
@ohos.ability.featureAbility
-
@ohos.data.dataability
-
@ohos.data.rdb
-
创建工具接口类对象。
import featureAbility from '@ohos.ability.featureAbility';
import dataAbility from '@ohos.data.dataAbility';
import dataRdb from '@ohos.data.rdb';
// 定义数据访问uri常量
// 注意:作为参数传递的uri,比config.json中定义的uri多了一个"/",因为在第二个与第三个"/"中间,存在一个DeviceID的参数
let BASE_URI = "dataability:///com.example.entry.DataAbility";
// 获取dataAbilityHelper,用来协助其他Ability访问DataAbility的工具类。
let DA_HELPER = featureAbility.acquireDataAbilityHelper(BASE_URI);
// 定义表的列名
const COLUMNS = ['id', 'name', 'introduction'];
// 获取谓词
let predicates = new dataAbility.DataAbilityPredicates();
-
构建数据库相关的RDB数据。
// 创建一个BookModel数据模型
export class BookModel {
id: number
name: string
introduction: string
constructor(id: number, name: string, introduction: string) {
this.id = id;
this.name = name;
this.introduction = introduction;
}
}
//初始化测试数据
export function initValuesBuckets() {
let valuesBuckets = [
{ 'name': 'Book name1', 'introduction': 'Book introduction1' },
{ 'name': 'Book name2', 'introduction': 'Book introduction2' },
{ 'name': 'Book name3', 'introduction': 'Book introduction3' }];
return valuesBuckets;
}
// 将数据转换为BookModel数组,用于处理DA_HELPER返回的数据。
// 获取一个BookModel的数据列表
export function getListFromResultSet(resultSet) {
let bookList = [];
for (let i = 0;i < resultSet.rowCount; i++) {
//按照构造函数进行封装
let book = new BookModel(
resultSet.getDouble(resultSet.getColumnIndex('id'))
, resultSet.getString(resultSet.getColumnIndex('name'))
, resultSet.getString(resultSet.getColumnIndex('introduction')));
//添加到列表中
bookList.push(book);
}
return bookList;
}
let valuesBucket = { 'name': 'Book name', 'introduction': 'Book introduction' };
let valuesBuckets = [
{ 'name': 'Book name1', 'introduction': 'Book introduction1' },
{ 'name': 'Book name2', 'introduction': 'Book introduction2' },
{ 'name': 'Book name3', 'introduction': 'Book introduction3' }
];
-
调用insert方法向指定的Data子模块插入数据。
let bookList: Array<BookModel> = [];
let valuesBucket = { name: 'Book name', introduction: 'Book introduction' };
DA_HELPER.insert(BASE_URI, valuesBucket, predicates).then((data) => {
console.log("DAHelper insert result: " + data);
bookList.push({ id: data, name: 'Book name', introduction: 'Book introduction' });
}).catch((err) => {
console.log('DAHelper insert fail: ' + err);
});
-
调用delete方法删除Data子模块中指定的数据。
let book: BookModel = null;
predicates.equalTo('id', book.id);
DA_HELPER.delete(BASE_URI, predicates).then((data) => {
console.log("DAHelper delete result: " + data);
}).catch((err) => {
console.log('DAHelper delete fail: ' + err);
});
-
调用update方法更新指定Data子模块中的数据。
let book: BookModel = null;
predicates.equalTo('id', book.id);
let valuesBucket = {
'name': book.name,
'introduction': book.introduction
};
DA_HELPER.update(BASE_URI, valuesBucket, predicates).then((data) => {
console.log("DAHelper update result: " + data);
}).catch((err) => {
console.log('DAHelper update fail: ' + err);
});
-
调用query方法在指定的Data子模块中查找数据。
let bookList: Array<BookModel> = [];
//根据名字或介绍模糊匹配
predicates.contains('name', 'Book name1')
.or()
.contains('introduction', 'Book introduction1');
DA_HELPER.query(BASE_URI, COLUMNS, predicates).then((data) => {
console.log("DAHelper query result: " + data);
bookList = getListFromResultSet(resultSet);
}).catch((err) => {
console.log('DAHelper query fail: ' + err);
});
-
调用batchInsert方法向指定的数据子模块批量插入数据。
let valuesBuckets = initValuesBuckets();
DA_HELPER.batchInsert(BASE_URI, valuesBuckets, predicates).then((data) => {
console.log("DAHelper batchInsert result: " + data);
}).catch((err) => {
console.log('DAHelper batchInsert fail: ' + err);
});
Stage模型
Ability开发指导
开发Stage模型应用时,需要在module.json5和app.json配置文件中对应用的包结构进行声明。基于Stage模型的Ability应用开发,主要涉及如下功能逻辑:
-
创建支持使用屏幕浏览及人机交互的Ability应用,包括实现Ability的生命周期、获取Ability配置信息、向用户申请授权及环境变化通知等场景。
-
启动Ability应用,包括相同设备启动Ability、跨设备启动Ability以及指定页面启动Ability等场景。
-
通用组件Call功能。
-
连接、断开ServiceExtensionAbility。
-
应用迁移。
创建Ability
-
点击Create Project,选择OpenHarmony下的Empty Ability模板,点击Next进行下一步配置。在工程配置项中, "Compile API"选择"9","Model"默认为"Stage",其他参数可保持默认设置。点击Finish,工具会自动生成示例代码和相关资源,等待工程创建成功。
-
工程创建成功后生成默认的Ability,名为MainAbility。
启动Ability
应用可以通过this.context获取Ability实例的上下文,进而使用AbilityContext中的StartAbility相关接口启动Ability。启动Ability由Wan传递bundleName和abilityName属性,通过callback形式或promise形式实现。
-
bundleName:表示包名称。可在AppScope->app.json5中查看bundleName,或者通过File->Project Structure查看。
-
abilityName:表示待启动的Ability名称。
启动本地设备Ability示例如下:
// MainAbility.ts
import hilog from '@ohos.hilog';
import Ability from '@ohos.application.Ability';
import Window from '@ohos.window';
export default class MainAbility extends Ability {
...
onWindowStageCreate(windowStage: Window.WindowStage) {
// Main window is created, set main page for this ability
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
// want参数配置
let want = {
bundleName: "com.samples.myapplication",
abilityName: "MainAbility"
};
// 拉起指定的Ability
this.context.startAbility(want).then((data) => {
console.log("Succeed to start ability with data: " + JSON.stringify(data))
}).catch((error) => {
console.error("Failed to start ability with error: " + JSON.stringify(error))
});
windowStage.loadContent('pages/index', (err, data) => {
if (err.code) {
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.ERROR);
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
});
}
...
};
ExtensionAbility机制
ExtensionAbility是Stage模型中新增的扩展组件的基类,一般用于处理无界面的任务,生命周期较简单,没有前后台生命周期。
不同于用于页面展示的Ability,ExtensionAbility提供的是一种受限的服务运行环境。ExtensionAbility具有如下特点:
-
独立于主进程的单独进程运行,与主进程无IPC,共享一个存储沙箱。
-
独立的Context提供基于业务场景的api能力。
-
由系统触发创建,应用不能直接创建。
-
ExtensionAbility和进程的生命周期受系统管理。
ServiceExtensionAbility开发指导
ServiceExtensionAbility是ExtensionAbility的扩展类。开发者可以自定义类继承ServiceExtensionAbility,通过重写基类中相关生命周期方法,来做初始化、连接中、断开连接时相关业务逻辑操作。
接口说明
ServiceExtensionAbility中相关生命周期API功能介绍
接口名 | 描述 |
---|---|
onCreate(want: Want): void | 首次调用startAbility、connectAbility时触发,执行初始化业务逻辑操作。 |
onRequest(want: Want, startId: number): void | 每次调用startAbility拉起服务都会触发,首次调用时startId为1,重复调用时startId会递增。 |
onConnect(want: Want): rpc.RemoteObject | 调用connectAbility拉起服务时触发,重复调用不会再次触发,除非调用disconnectAbility解除绑定后再调用;onConnect返回一个进程通信类RemoteObject,用于和客户端进行通信。 |
onDestroy(): void | 调用停止当前Ability的接口terminateSelf时触发,执行资源清理等操作。 |
onDisconnect(want: Want): void | 调用disconnectAbility断开服务连接时触发,Extension如果是用connectAbility拉起的,并且已经没有其他应用绑定这个Extension,则会触发onDestroy生命周期销毁组件。 |
创建与注册ServiceExtensionAbility
-
在ets目录下选择New->Directory创建ServiceExtAbility目录,然后在ServiceExtAbility目录选择New->ts File创建ServiceExtAbility.ts文件。
-
在ServiceExtAbility.ts文件中自定义类继承ServiceExtensionAbility,重写基类回调函数。
说明:'@ohos.application.ServiceExtensionAbility'模块从SDK 3.2.5.5版本起,被提取到了full-sdk当中,使用时需手动下载并安装full-sdk。
// ServiceExtAbility.ts
import ServiceExtensionAbility from '@ohos.application.ServiceExtensionAbility';
export default class ServiceExtAbility extends ServiceExtensionAbility {
onCreate(want) {
console.log(`onCreate, want: ${want.abilityName}`);
}
onRequest(want, startId) {
console.log(`onRequest, want: ${want.abilityName}`);
}
onConnect(want) {
console.log(`onConnect , want: ${want.abilityName}`);
return null;
}
onDisconnect(want) {
console.log(`onDisconnect, want: ${want.abilityName}`);
}
onDestroy() {
console.log(`onDestroy`);
}
}
-
注册ServiceExtensionAbility。在配置文件module.json5中进行注册,手动添加"extensionAbilities"配置项,将注册类型type设置为service。
module.json5配置样例
"module": {
...
"extensionAbilities": [
{
"name": "ServiceExtAbility",
"srcEntrance": "./ets/ServiceExtAbility/ServiceExtAbility.ts",
"icon": "$media:icon",
"description": "service",
"type": "service",
"visible": true
}
]
...
}
启动ServiceExtensionAbility
应用通过this.context获取Ability实例的上下文,调用startAbility()方法来启动本地ServiceExtensionAbility,该方法的Want传递bundleName和abilityName属性。将this.context挂载到全局对象globalThis上,用于外部自定义方法调用AbilityContext中的startAbility接口。
启动本地设备ServiceExtensionAbility示例如下:
// index.ets
@Entry
@Component
struct Index {
private context = getContext(this) as any;
startService() {
// want参数配置
let want = {
bundleName: "com.samples.myapplication",
abilityName: "ServiceExtAbility"
};
// 拉起指定的ServiceExtensionAbility
this.context.startAbility(want).then((data) => {
console.log(`startAbility success: ${JSON.stringify(data)}`);
}).catch((error) => {
console.error(`startAbility failed: ${JSON.stringify(error)}`);
})
}
build() {
...
}
}
连接ServiceExtensionAbility
由this.context获取Ability实例的上下文,调用connectAbility方法连接本地ServiceExtensionAbility,由该方法的Want传递连接目标的bundleName和abilityName属性,ConnectOptions远程对象实例提供onConnect()、onDisconnect()和onFailed()方法,用于监听不同连接状态并回调。
创建连接本地ServiceExtensionAbility回调实例以及连接本地ServiceExtensionAbility的示例如下:
import prompt from '@ohos.prompt';
// 导入rpc进程通信模块
import rpc from '@ohos.rpc';
connectService() {
// ConnectOptions远程对象实例
let connectOptions = {
// 连接成功时回调
onConnect: function onConnectCallback(element, proxy) {
console.log(`onConnectLocalService onConnectDone element:${element} proxy:${proxy}`);
if (proxy === null) {
prompt.showToast({
message: 'Connect service failed'
})
return;
}
// 携带客户端调用参数的MessageParcel对象
let data = rpc.MessageParcel.create();
// 接收应答数据的MessageParcel对象
let reply = rpc.MessageParcel.create();
// 本次请求的同异步模式,默认为TF_SYNC同步调用
let option = new rpc.MessageOption();
// 将两个整数值写入MessageParcel实例
data.writeInt(100);
data.writeInt(200);
// 以同步模式向服务端发送MessageParcel消息,发送的消息代码为1,同步模式下该接口将等待服务端的响应,直到请求超时,响应的结果在reply中
proxy.sendRequest(1, data, reply, option).then((result) => {
console.log(`sendRequest: ${result}`);
// 客户端以reply从MessageParcel实例中读取服务端处理后的整数值
let msg = reply.readInt();
console.log(`sendRequest msg: ${msg}`);
}).catch((err) => {
console.log(`sendRequest error: ${err}`);
});
prompt.showToast({
message: "Connect service success"
})
},
// 连接断开时回调
onDisconnect: function onDisconnectCallback(element) {
console.log(`onConnectLocalService onDisconnectDone element:${element}`);
prompt.showToast({
message: "Disconnect service success"
})
},
// 连接失败时回调
onFailed: function onFailedCallback(code) {
console.log(`onConnectLocalService onFailed errCode:${code}`);
prompt.showToast({
message: "Connect local service onFailed"
})
}
}
// want参数配置
let want = {
bundleName: 'com.samples.myapplication',
abilityName: 'ServiceExtAbility'
};
// 连接指定的ServiceExtensionAbility
this.context.connectAbility(want, connectOptions);
}
ServiceExtensionAbility服务端通过继承rpc.RemoteObject来创建自定义的实现类StubTest,在onConnect()中返回RemoteObject对象与描述符参数。在StubTest类中调用onRemoteRequest()方法响应客户端发送的请求,并对数据作处理。
Service服务端把自身的实例返回给调用侧的代码示例如下:
// ServiceExtAbility.ts
import ServiceExtensionAbility from '@ohos.application.ServiceExtensionAbility';
import rpc from '@ohos.rpc';
// 实现远程对象,服务提供者必须继承RemoteObject类
class StubTest extends rpc.RemoteObject {
// 用于创建RemoteObject实例的构造函数,参数des为指定接口描述符
constructor(des) {
super(des);
}
// sendRequest请求的响应处理函数,服务端在该函数里处理请求,回复结果
onRemoteRequest(code, data, reply, option) {
console.log(`onRemoteRequest`);
// 判断客户端发送过来的消息代码是否为1
if (code === REQUEST_VALUE) {
// 从MessageParcel实例中读取由客户端写入的整数值
let dataFirst = data.readInt();
let dataSecond = data.readInt();
// 将读取的整数值相加并写入MessageParcel实例
reply.writeInt(dataFirst + dataSecond);
console.log(`onRemoteRequest: dataFirst: ${dataFirst}, dataSecond: ${dataSecond}`);
}
return true;
}
}
export default class ServiceExtAbility extends ServiceExtensionAbility {
onCreate(want) {
console.log(`onCreate, want: ${want.abilityName}`);
}
onRequest(want, startId) {
console.log(`onRequest, want: ${want.abilityName}`);
}
onConnect(want) {
console.log(`onConnect , want: ${want.abilityName}`);
// 返回RemoteObject对象与描述符
return new StubTest("test");
}
onDisconnect(want) {
console.log(`onDisconnect, want: ${want.abilityName}`);
}
onDestroy() {
console.log(`onDestroy`);
}
}
Ability间的跳转
应用场景描述
实现Ability之间的跳转将调用startAbility()方法,此方法根据want参数传递Abillity名称,拉起指定的Ability,达到跳转效果。
说明:Ability跳转效果需在设备上验证。
FA模型Ability跳转
-
在工程中,选择File->New->Ability->Page Ability,新建一个PageAbility命名为MainAbilitySecond,并在其pages目录选择New->Page新建second.ets页面。目录结构如下:
-
在MainAbility/pages/index.ets中导入featureAbility模块。
import featureAbility from '@ohos.ability.featureAbility';
-
在MainAbility/pages/index.ets页面添加Button组件,给Butom添加文本、样式和点击事件,在点击事件中调用featureAbility.startAbility拉起指定的Ability实现跳转。startAbility中的want参数主要传递bundleName和abilityName,在want中添加parameters参数,设置路由url为指定页面,点击Button按钮即可跳转到MainAbilitySecond的指定页面。
说明:want中如果不添加parameters参数,则默认指向Ability的index页面。
// MainAbility/pages/index.ets
import featureAbility from '@ohos.ability.featureAbility';
@Entry
@Component
struct Index {
build() {
Row() {
Column() {
Button() {
Text('to MainAbilitySecond')
.fontSize(25)
.fontWeight(FontWeight.Bolder)
}
.height(60)
.onClick(() => {
let parameter = {
want: {
bundleName: "com.samples.myapplication",
// FA模型abilityName由config.json中的package + Ability name组成
abilityName: "com.samples.myapplication.MainAbilitySecond",
parameters: {
url: 'pages/second'
}
}
}
featureAbility.startAbility(parameter).then((data) => {
console.info('startAbility successful. Data: ' + JSON.stringify(data))
}).catch((error) => {
console.error('startAbility failed. Cause: ' + JSON.stringify(error))
})
})
}
.width('100%')
}
.height('100%')
}
}
Stage模型Ability跳转
-
在工程中,选择File->New->Ability,新建一个Ability命名为MainAbilitySecond,在pages目录选择New->Page新建second.ets。目录结构如下:
-
将MainAbilitySecond.ts页面中loadContent方法加载的主页面改为pages/second,与MainAbility中加载的index主页面区分开。
// MainAbilitySecond.ts
import hilog from '@ohos.hilog';
import Ability from '@ohos.application.Ability'
import Window from '@ohos.window'
export default class MainAbilitySecond extends Ability {
...
onWindowStageCreate(windowStage: Window.WindowStage) {
// Main window is created, set main page for this ability
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
// 默认指向pages/index,这里修改为pages/second
windowStage.loadContent('pages/second', (err, data) => {
if (err.code) {
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.ERROR);
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
});
}
...
}
-
在MainAbility/pages/index.ets页面添加Button组件,给Butom添加文本、样式和点击事件,创建context对象获取应用上下文, 并调用startAbility拉起指定的Ability实现跳转。其中startAbility的want参数传递bundleName和abilityName。
// MainAbility/pages/index.ets
@Entry
@Component
struct Index {
private context = getContext(this) as any;
build() {
Row() {
Column() {
Button() {
Text('to MainAbilitySecond')
.fontSize(25)
.fontWeight(FontWeight.Bolder)
}
.height(60)
.onClick(() => {
let want = {
bundleName: "com.samples.myapplication",
abilityName: "MainAbilitySecond"
}
this.context.startAbility(want).then((data) => {
console.info('startAbility successful. Data: ' + JSON.stringify(data))
}).catch((error) => {
console.error('startAbility failed. Cause: ' + JSON.stringify(error))
})
})
}
.width('100%')
}
.height('100%')
}
}
Ability生命周期
生命周期差异对比
FA模型与Stage模型的Ability生命周期存在差异:
生命周期接口说明
FA模型生命周期回调函数
接口名 | 描述 |
---|---|
onCreate() | Ability第一次启动创建Ability时调用onCreate方法,开发者可以在该方法里做一些应用初始化工作。 |
onActive() | Ability切换到前台,并且已经获取焦点时调用onActive方法。 |
onInactive() | Ability失去焦点时调用onInactive方法,Ability在进入后台状态时会先失去焦点,再进入后台。 |
onHide() | Ability由前台切换到后台不可见状态时调用onHide方法,此时用户在屏幕看不到该Ability。 |
onShow() | Ability由后台不可见状态切换到前台可见状态调用onShow方法,此时用户在屏幕可以看到该Ability。 |
onDestroy() | 应用退出,销毁Ability对象前调用onDestroy方法,开发者可以在该方法里做一些回收资源、清空缓存等应用退出前的准备工作。 |
Stage模型生命周期回调函数
接口名 | 描述 |
---|---|
onCreate(want: Want, param: AbilityConstant.LaunchParam): void | Ability生命周期回调,Ability启动时被调用。 |
onWindowStageCreate(windowStage: window.WindowStage): void | Ability生命周期回调,创建window stage时被调用,应用开发者可通过window.WindowStage的接口执行页面加载等操作。 |
onForeground(): void | Ability生命周期回调,Ability切换至前台时被调用。 |
onBackground(): void | Ability生命周期回调,Ability切换至后台时被调用。 |
onWindowStageDestroy(): void | Ability生命周期回调,销毁window stage时被调用。 |
onDestroy(): void | Ability生命周期回调,Ability销毁时被调用。 |
Ability启动模式
FA模型启动模式
FA模型Ability支持单实例和多实例两种启动模式,在config.json中通过launchType配置项,配置具体的启动模式;如果不配置,则默认为standard模式。
启动模式 | 描述 | 说明 |
---|---|---|
singleton | 单实例 | 系统中只存在唯一一个实例,startAbility时,如果已存在,则复用系统中的唯一一个实例。 |
standard | 多实例 | 每次startAbility调用都会启动一个新的实例。 |
Stage模型启动模式
Stage模型Ability支持单实例、多实例和指定实例3种启动模式,在module.json5中默认没有launchType配置项,需手动添加;如果不添加,则默认为singleton模式。
启动模式 | 描述 | 说明 |
---|---|---|
singleton | 单实例 | 系统中只存在唯一一个实例,startAbility时,如果已存在,则复用系统中的唯一一个实例。 |
standard | 多实例 | 每次startAbility调用都会启动一个新的实例。 |
specified | 指定实例 | 运行时由ability内部业务决定是否创建多实例。 |
module.json5示例:
{
"module": {
"abilities": [
{
...
"launchType": "standard"
...
}
]
}
}
参考文献
[1] OpenHarmony Ability开发.
[2] OpenHarmony接口开发.
更多推荐
所有评论(0)