赞
踩
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
从 A 设备迁移到 B 设备,流程如下:
设备 A 上的 Page 请求迁移
系统回调设备 A 上 Page 以及 AbilitySlice 栈里面所有 AbilitySlice 实例的 IAbilityContinuation.onStartContinuation
方法,已确认当前是否可以立即迁移
如果可以立即迁移,则系统回调设备 A 上 Page 及其 AbilitySlice 实例的 IAbilityContinuation.onSaveData
如果数据保存成功,则系统在 B上启动一个 Page,并恢复其 AbilitySlice 栈,然后回调 IAbilityContinuation.onRestore
方法,传递此前保存的数据,然后 B 设备的 Page 从 onStart()
生命周期开始进行
调用 A 设备 Page 以及其所有 AbilitySlice 的 IAbilityContinuation.onCompleteContinuation
方法
如果迁移发生异常, 系统回调 A 的 Page及其所有 AbilitySlice 栈中所有 AbilitySlice 实例的 IAbilityContinuation.onFialedContinuation
方法,并不是所有异常都会回调此FA方法,仅局限于该接口枚举的异常。
这里有一个问题,A设备如何找到B设备,这就涉及到获取分布式设备类了,需要通过监听迁移按钮的点击事件,然后获取分布式设备列表,选择设备后进行传递,具体代码可以看:获取分布式设备
如果前面调用 continueAbilityReversibly
请求迁移完成后, 源设备可以调用 reverseContinueAbility
发起回迁:
try {
reverseContinueAbility();
} catch (IllegalStateException e) {
…
}
这里就不具体展示具体流程了,和迁移流程大同小异。
====================================================================================
基于 Service 的 Ability ,主要提供的能力是后台运行任务(比如音乐播放、文件下载),不能提供页面UI服务。
Service 是单例的,一个设备上,一个Service 只存在一个实例, 一个 Service 可以绑定多个 Ability, 只有在绑定的 Ability 全部退出后, 这个Service 才能退出。
理论上,它和 Android 的 Service 组件作用一样, 这样看来, Ability 更像是一个 context。
它有两种启动方法
启动Service, 其他 Ability 通过调用 startAbility()
时创建,然后保持运行,其他 Ability 通过调用 stopAbility()
来停止 Service,停止后,它将会被销毁
绑定Service,其他 Ability 通过调用 connectAbility
来绑定 Service, 通过 disconnectAbility()
来解绑。多个 Ability 可以连接一个 Service,当一个 Service 不在被任何 Ability 绑定时,其将会被销毁
Service Ability 生命周期的方法:
onStart
创建 Ability 的时候调用,整个生命周期只调用一次,传入的 Intent 应该是空的
onCommand
在Service创建完成之后调用,该方法在客户端每次启动该Service时都会调用,开发者可以在该方法中做一些调用统计、初始化类的操作
onConnect
在 Ability 绑定 Service Ability 的时候回调,会让 Service 创建并返回一个 IRemoteObject
对象,可以通过这个对象生成一个 IPC 通道,便于 Service 和 Ability 通信,所以可以看出来,这个 Service 和 通信的Ability 可以不在一个进程中。
其次,多个 Ability 可以绑定同一个 Service, 系统会缓存这个 IPC 通道,只有在第一个客户端绑定的时候,会调用 onConnect
方法,之后别的 Ability 再次绑定时,会将这个 IPC 通道发送过去,而无需再次调用 onConnect
方法
onDisConnected
在 Ability 和 Service 解除绑定的时候调用
onStop
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();
}
}
同时需要在 config.json 中注册:
{
“module”: {
“abilities”: [
{
“name”: “.ServiceAbility”,
“type”: “service”,
“visible”: true
…
}
]
…
}
…
}
通过 startAbility()
来启动一个 Service Ability,可以通过传入 Intent
来启动,可以支持本地或者远程的 Service
可以通过Intent传入三个信息:
DeviceId
设备ID,如果是本地设备,可以为空,如果是远程设备,可以通过 ohos.distributedschedule.interwork.DeviceManager 获取设备列表
BundleName
表示包名
AbilityName
表示待启动的Ability名称
启动本地设备 Service 的代码如下:
Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
.withDeviceId(“”)
.withBundleName(“com.xxx”)
.withAbilityName(“com.xxx.ServiceAbility”)
.withFlag(Intent.FLAG_ABILITYSLCE_MULTI_DEVICE) // 启动远端设备的Service时需要带上,表示分布式调度系统多设备启动
.build();
intent.setOperation(operation);
startAbility(intent);
如果 Service 需要与 Page 或者其他应用的 Service Ability 交互,就必须要创建用于连接的 Connection
, 这样别的 Ability 就可以通过 connectAbility()
方法和它进行连接。
在使用 connectAbility
时,需要传入目标 Service 的 Intent 和 IAblityConnection
的示例,它有两个方法,分别是连接成功的回调 和 异常死亡的回调,如下所示:
// 创建连接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的代码,需要带上 Connection:
// 连接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,从而传递这个 IPC通道。鸿蒙提供了默认实现,可以通过继承 LocalRemoteObject
来创建这个通道,然后回传,如下所示:
private class MyRemoteObject extends LocalRemoteObject {
MyRemoteObject(){
}
}
// 把IRemoteObject返回给客户端
@Override
protected IRemoteObject onConnect(Intent intent) {
return new MyRemoteObject();
}
Service 一般是在后台运行,所以优先级比较低,容易被回收。
但是在一些场景下(比如文件下载),用户希望能够一直保持运行,这个时候就需要使用前台Service。 前台Service会始终保持正在运行的图标在状态栏显示,即和通知绑定
使用 前台Service,只需以下步骤:
Serivce 内部调用 keepBackgroundRunning()
绑定 Service 和 通知
在 Service 的配置文件中声明 ohos.permission.KEEP_BACKGROUND_RUNNING
权限
在配置文件中添加对应的 backgroundModes
参数
在 Serivce 的 onStop()
方法中调用 cancelBackgroundRunning()
示例如下:
// 创建通知,其中121为notificationId
NotificationRequest request = new NotificationRequest(121);
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);
同时修改 config.json 里面的配置:
{
“name”: “.ServiceAbility”,
“type”: “service”,
“visible”: true,
“backgroundModes”: [“dataTransfer”, “location”]
}
backgroundModes 表示后台的服务类型,该标签仅适用于 Service Ability,取值如下:
这个是前台的音乐播放器Demo: 音乐播放器
=================================================================================
Data Ability 有助于应用管理自身和其他应用存储数据的访问,并提供与其他应用共享数据的方法。 Data既可以用于同设备下不同应用的数据共享,也支持跨设备不同应用数据共享
Data 提供的 api 都是 URI
来标识一个具体的数据,HarmonyOS的URI是基于URI通用标准,格式如下:
scheme: 固定为 “dataability”
authority: 设备id,如果为跨设备场景,则为目标设备的id,如果为本地设备场景,则不需要填写
path:资源路径信息,代表特定资源的位置信息
query:查询参数
fragment:可以用于指示要访问的子资源
例如:
跨设备场景:dataability://device_id/com.domainname.dataability.persondata/person/10
本地设备:dataability:///com.domainname.dataability.persondata/person/10
查询 persondata 路径下 person 参数的第10条数据
Data 提供自定义数据的增删改查等功能,并对外提供这些接口
确定数据的存储方式, Data 支持一下两种数据形式:
文本数据:文本、图片、音乐等
结构化数据:如数据库、数据Bean等。
Data Ability
用于接收其他应用发送的请求,所以它提供访问接口。
通过在工程目录下,点击 Empty Data Ability 并且输入 Data 的名称,既可创建一个默认实现的 DataAbility
Data 提供了两组接口,分别是:
文件存储
数据库存储
通过 FileDescriptor openFile(Uri uri, String mode)
来操作文件, uri 为调用方传入的请求目标路径, mode为开发者对文件的操作选项,可选方式包括 “r”(只读), “w”(只写),“rw”(读写) 等
ohos.rpc.MessageParcel 提供了一个静态方法,用于获取 MessageParcel 实例。 开发者可以通过获取到的 MessageParcel 实例,使用 dupFileDescriptor()
复制带操作文件流的文件描述符,并将其返回,供远端应用访问文件。
代码如下所示:
@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 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);
}
提供下面的 api 来增删改查:
这里需要注意的点是, 数据库是支持 ORM
的, ValueBucket 就像 Bundle 一样,可以传递参数,例如下面这个在数据库中插入一条数据:
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;
}
具体 orm 能力可以看这里: 对象关系映射数据库开发指导
和 Service 类似,开发者必须在配置文件中注册 Data
需要关注几个属性:
类型设置为 data
对外提供的访问路径,全局唯一
访问该 data ability 时需要申请的权限
如下所示:
{
“name”: “.UserDataAbility”,
“type”: “data”,
“visible”: true,
“uri”: “dataability://com.example.myapplication5.DataAbilityTest”,
“permissions”: [
“com.example.myapplication5.DataAbility.DATA”
]
}
开发者通过 DataAbilityHelper
来访问当前应用或者其他应用提供的共享数据。
如果需要访问的 Data 声明了权限,那么访问此 Data 需要也在配置文件中声明权限
通过传入一个 context 来创建 DataAbilityHelper
对象
DataAbilityHelper helper = DataAbilityHelper.creator(this);
通过 DataAbilityHelper 的 FileDescriptor openFile(Uri uri, String mode)
来操作文件,需要传入两个参数,其中uri用来确定目标资源路径,mode用来指定打开文件的方式,这个方法返回一个目标文件的描述符,可以把它封装成文件流,就可以对其进行自定义处理:
// 读取文件描述符
FileDescriptor fd = helper.openFile(uri, “r”);
// 使用文件描述符封装成的文件流,进行文件操作
FileInputStream fis = new FileInputStream(fd);
上面的图片又数据库操作的api,可以用来对数据库增删改查,下面是一段查询操作代码
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());
===========================================================================
Intent 是对象之间传递信息的载体。
当 Intent 用于发起请求时,根据指定元素的不同,分为两种类型:
如果同时指定了 BundleName 与 AbilityName, 则根据 AbilityName 来直接启动应用
如果未同时指定 BundleName 和 AbilityName, 则根据 Operation 中的其他属性来启动应用
Intent intent = new Intent();
// 通过Intent中的OperationBuilder类构造operation对象,指定设备标识(空串表示当前设备)、应用包名、Ability名称
Operation operation = new Intent.OperationBuilder()
.withDeviceId(“”)
.withBundleName(“com.demoapp”)
.withAbilityName(“com.demoapp.FooAbility”)
.build();
// 把operation设置到intent中
intent.setOperation(operation);
startAbility(intent);
和 Android 的 Intent 一样,可以通过设置 Action,来使用其他应用提供的能力,而不用关心具体是哪一个应用。
例如打开一个 天气查询的功能,请求只需要设置一个 ACTION_QUERY_WEATHER
就行:
private void queryWeather() {
Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
.withAction(Intent.ACTION_QUERY_WEATHER)
.build();
intent.setOperation(operation);
startAbilityForResult(intent, REQ_CODE_QUERY_WEATHER);
}
@Override
protected void onAbilityResult(int requestCode, int resultCode, Intent resultData) {
switch (requestCode) {
case REQ_CODE_QUERY_WEATHER:
// Do something with result.
…
return;
default:
…
}
}
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
开一个 天气查询的功能,请求只需要设置一个 ACTION_QUERY_WEATHER
就行:
private void queryWeather() {
Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
.withAction(Intent.ACTION_QUERY_WEATHER)
.build();
intent.setOperation(operation);
startAbilityForResult(intent, REQ_CODE_QUERY_WEATHER);
}
@Override
protected void onAbilityResult(int requestCode, int resultCode, Intent resultData) {
switch (requestCode) {
case REQ_CODE_QUERY_WEATHER:
// Do something with result.
…
return;
default:
…
}
}
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
[外链图片转存中…(img-djD67Pwg-1715167219735)]
[外链图片转存中…(img-YxV5v9aP-1715167219736)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。