从 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设备,这就涉及到获取分布式设备类了,需要通过监听迁移按钮的点击事件,然后获取分布式设备列表,选择设备后进行传递,具体代码可以看:获取分布式设备
2.6.3 请求回迁
如果前面调用 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 提供自定义数据的增删改查等功能,并对外提供这些接口
4.1.1 确定数据存储方式
确定数据的存储方式, Data 支持一下两种数据形式:
-
文本数据:文本、图片、音乐等
-
结构化数据:如数据库、数据Bean等。
4.1.2 实现 DataAbility
Data Ability
用于接收其他应用发送的请求,所以它提供访问接口。
通过在工程目录下,点击 Empty Data Ability 并且输入 Data 的名称,既可创建一个默认实现的 DataAbility
Data 提供了两组接口,分别是:
-
文件存储
-
数据库存储
4.1.3 文件存储
通过 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);
}
4.1.4 数据库存储
使用数据库,需要先连接到数据库。
系统会在应用启动的时候调用 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 能力可以看这里: 对象关系映射数据库开发指导
4.1.5 注册 UserDataAbility
和 Service 类似,开发者必须在配置文件中注册 Data
需要关注几个属性:
- type
类型设置为 data
- uri
对外提供的访问路径,全局唯一
- permission
访问该 data ability 时需要申请的权限
如下所示:
{
“name”: “.UserDataAbility”,
“type”: “data”,
“visible”: true,
“uri”: “dataability://com.example.myapplication5.DataAbilityTest”,
“permissions”: [
“com.example.myapplication5.DataAbility.DATA”
]
}
开发者通过 DataAbilityHelper
来访问当前应用或者其他应用提供的共享数据。
4.2.1 声明访问权限
如果需要访问的 Data 声明了权限,那么访问此 Data 需要也在配置文件中声明权限
4.2.2 创建 DataAbilityHelper
通过传入一个 context 来创建 DataAbilityHelper
对象
DataAbilityHelper helper = DataAbilityHelper.creator(this);
4.2.3 使用 DataAbilityHelper
- 访问文件
通过 DataAbilityHelper 的 FileDescriptor openFile(Uri uri, String mode)
来操作文件,需要传入两个参数,其中uri用来确定目标资源路径,mode用来指定打开文件的方式,这个方法返回一个目标文件的描述符,可以把它封装成文件流,就可以对其进行自定义处理:
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
最后
想要了解更多关于大厂面试的同学可以点赞支持一下,除此之外,我也分享一些优质资源,包括:Android学习PDF+架构视频+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 这几块的内容。非常适合近期有面试和想在技术道路上继续精进的朋友。
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
担。**
[外链图片转存中…(img-3ulKLezK-1712781788381)]
[外链图片转存中…(img-CZh7IYwS-1712781788382)]
[外链图片转存中…(img-WWlb3YBN-1712781788383)]
[外链图片转存中…(img-L8W6uUfK-1712781788383)]
[外链图片转存中…(img-qBAd1PDI-1712781788383)]
[外链图片转存中…(img-HSFoSuQq-1712781788384)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-mZGbCheo-1712781788384)]
最后
想要了解更多关于大厂面试的同学可以点赞支持一下,除此之外,我也分享一些优质资源,包括:Android学习PDF+架构视频+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 这几块的内容。非常适合近期有面试和想在技术道路上继续精进的朋友。
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-30rVI3QE-1712781788384)]