鸿蒙Ability学习(1),面试官面试流程话术

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img

img
img
htt

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
img

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

在这里插入图片描述

What? Ability 是一个框架,除了 Ability 本身,它还包含了 事件通知、线程处理、剪贴板?

好家伙,我学一个 Ability,等于直接学了一个鸿蒙应用开发。

本篇应该不会讲解那么多东西,主要是 FA 和 PA, 至于下面这些,我看着学 🙃

2. Page Ability

=================================================================================

Page Ability 是页面,它的实例就是一个 Page,一个Page可以包含一个或多个 AbilitySlice,就是 Ability碎片 - -, 如下图所示:

在这里插入图片描述

鸿蒙支持不同 Page 之间的跳转,也支持指定跳转到某个Page中的具体 AbilitySlice。 当然,Page也可以不用包含 AbilitySlice 就能展示 UI。跟 Activity 和 Fragment 的关系是一样的。

2.1 AbilitySlice路由配置


我们知道一个 Page 可以包含多个 AbilitySlice, 但进入前台时默认只展示一个 AbilitySlice,需要使用下面api来设置默认展示的碎片:

setMainRoute()

如果需要更改默认展示的 AbilitySlice, 则可以通过下面方法为其配置一条路由规则:

// 代码中添加一个隐式路由

addActionRoute()

不过这个方法需要添加一些参数,这些参数需要在 config.json 中进行注册:

// config.json

“abilities”: [

{

“skills”:[

{

// 在这里为 ability 那些可以被拉起展示的 abilityslice

“actions”:[

“action.pay”,

“action.scan”

]

看下面代码, 其他页面可以通过 Intent 打开该 Page 时默认展示具体哪个 AbilitySlice:

public class MyAbility extends Ability {

@Override

public void onStart(Intent intent) {

super.onStart(intent);

// 设置主 AbilitySlice

setMainRoute(MainSlice.class.getName());

// 注册默认的页面, 其他页面可以通过 Intent 打开该 Page 时默认展示具体哪个 AbilitySlice

addActionRoute(“action.pay”, PaySlice.class.getName());

addActionRoute(“action.scan”, ScanSlice.class.getName());

}

}

2.2 Page Ability的生命周期


状态机如下所示:

在这里插入图片描述

  • onStart()

系统 首次创建 Page实例的时机。 该回调在生命周期中仅触发一次, Page在该逻辑后会进入 INACTIVE 状态,该方法必须重写,且需要在该方法里设置 AbilitySlice,模板代码如下:

@Override

public void onStart(Intent intent) {

super.onStart(intent);

super.setMainRoute(FooSlice.class.getName());

}

  • onActive()

Page 在进入了 INACTIVE 状态后来到前台,就会调用该方法。 在此进入 ACTIVE 状态, 该状态是应用于用户交互的状态。

Page一直保持在该状态, 直至 Page 失去焦点,发生时,Page 会回到 INACTIVE 状态,系统调用 onInactive() 方法,此后如果回到 ACTIVE 状态,就会再次调用 onActive()

  • onInactive()

当 Page 失去焦点时,就会调用此方法。

  • onBackground()

当 Page 不再可见时,会调用此方法,此后 Page 进入 BACKGROUND 状态,该方法可以用来释放无用资源

  • onForeground()

Page 在处于 BACKGROUND 的状态时若仍然驻留在内存中,当重新回到前台时, 会首先调用 onForeground() 通知开发者,

然后进入 INACTIVE 状态

  • onStop()

系统将要销毁 Page 时,将会触发此回调函数,这个时候需要释放系统资源。销毁Page的原因:

①:用户使用系统能力关闭Page,比如任务管理器关闭

②:触发 Page 的 terminateAbility() 方法

③:配置变更导致系统暂时销毁Page并重建 (竖屏转横屏?)

④:系统处于资源管理的目的, 回收处于 BACKGROUND 状态的Page

和 Android Activity/Fragment 生命周期的一些区别:

  1. Android中使用 onStart() 表示进入前台, 第一次也会调, 鸿蒙用 onForeground() 表示,但是第一次进入时不会调用

  2. 无。

2.3 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);

}

在同一个 Page 中,从 A AbilitySlice 导航到 B AbilitySlice 所经历的方法回调是:

A.onInactive() -> B.onStart() -> B.onActive() -> A.onBackground()

2.4 同一 Page 内导航


2.4.1 presenter()

当发起导航的 AbilitySlice 和导航目标的 AbilitySlice 处于同一个 Page 时, 可以通过 presenter() 实现导航,代码如下,导航到 Target AbilitySlice:

private void initUi() {

Text text = (Text) findComponentById(ResourceTable.Id_jump_button);

text.setClickedListener(listener -> present(new TargetSlice(), new Intent()));

}

2.4.2 presentForResult()

如果希望从导航目标的 AbilitySlice 返回时,带上结果,则需要使用 presenterForResult(),带上 requestCode ,并重写 onResult() 回调:

private void initUi() {

Text text = (Text) findComponentById(ResourceTable.Id_jump_button);

text.setClickedListener(listener -> presentForResult(new TargetSlice(), new Intent(), 0));

}

@Override

protected void onResult(int requestCode, Intent resultIntent) {

// 这里返回页面结果

super.onResult(requestCode, resultIntent);

}

2.4.3 AbilitySlice 实例栈

每个 Page 都会维护一个 AbilitySlice 实例的栈,每个进入前台的 AbilitySlice 实例都会入栈。

当调用 presenter() 时,指定的 AbilitySlice 实例已经入栈了,则栈中位于此实例之上的 AbilitySlice 均会出栈,并终止其生命周期。

2.5 不同 Page 间导航


AbilitySlice 是作为 Page 的内部单元,以 Action 的形式对外暴露

Page 间的导航使用 startAbility() / startAbilityForResult() 方法导航, 获得返回结果的回调为 onAbilityResult()。在 Ability 中的 setResult() 可以设置返回结果。

2.6 跨设备迁移


跨设备迁移,支持将 Page 在同一用户的不同设备间的迁移,就是将一个页面从 A 设备转移到 B 设备,这是万物互联的一个体现。其步骤大概三步走:

  1. 设备A上的Page请求迁移

  2. HarmonyOS 处理迁移任务,并回调设备A上Page的保存数据方法,用于保存迁移必须的数据

  3. HarmonyOS 在设备B上启动同一个Page,并回调其恢复数据方法

2.6.1 实现 IAbilityContinuation 接口

一个应用可能包含多个Page,仅需要在支持迁移的Page中通过以下方法实现IAbilityContinuation接口。同时,此Page所包含的所有AbilitySlice也需要实现此接口。

  • onStartContinuation

Page 请求迁移后,系统首先回调这个方法,开发者可以在此回调中决策当前是否可以执行迁移。比如弹一个弹窗让用户确认是否迁移

  • onSaveData

如果 onStartContinuation 返回true,那么就会调用这个方法,开发者在此方法中保存数据,这些数据后续将传到另一台设备上

  • onRestoreData

源侧设备上Page完成保存数据后,系统在目标侧设备上回调这个方法,开发者在此回调中接收用于恢复 Page 状态的数据。

这个方法调用实际在 onStart 之前,会触发目标设备上 Page 的重新启动生命周期

  • onCompleteContinuation

如果数据传递成功,源设备会回调这个方法,告知迁移结束,源设备可以在这里结束Page

  • onFailedContinuation

迁移过程中发生异常,会在源设备回调FA的此方法。

  • onRemoteTerminated (可以不必实现)

如果开发者使用 continueAbilityReversibly() 而不是 continueAbility(),则此后可以在源设备上使用 reverseContinueAbility 进行回迁。这种场景下,相当于同一个 Page 的两个实例运行在两台设备上。迁移完成后,如果目标设备上的 Page 生命周期销毁,那么源设备会调用此方法

2.6.2 请求迁移

实现了 IAbilityContinuation 的接口后,可以在生命周期内, 调用 continueAbility() / continueAbilityReversibly 请求迁移,后者可以回迁:

try {

continueAbility();

} catch (IllegalStateException e) {

}

从 A 设备迁移到 B 设备,流程如下:

  1. 设备 A 上的 Page 请求迁移

  2. 系统回调设备 A 上 Page 以及 AbilitySlice 栈里面所有 AbilitySlice 实例的 IAbilityContinuation.onStartContinuation 方法,已确认当前是否可以立即迁移

  3. 如果可以立即迁移,则系统回调设备 A 上 Page 及其 AbilitySlice 实例的 IAbilityContinuation.onSaveData

  4. 如果数据保存成功,则系统在 B上启动一个 Page,并恢复其 AbilitySlice 栈,然后回调 IAbilityContinuation.onRestore 方法,传递此前保存的数据,然后 B 设备的 Page 从 onStart() 生命周期开始进行

  5. 调用 A 设备 Page 以及其所有 AbilitySlice 的 IAbilityContinuation.onCompleteContinuation 方法

  6. 如果迁移发生异常, 系统回调 A 的 Page及其所有 AbilitySlice 栈中所有 AbilitySlice 实例的 IAbilityContinuation.onFialedContinuation 方法,并不是所有异常都会回调此FA方法,仅局限于该接口枚举的异常。

这里有一个问题,A设备如何找到B设备,这就涉及到获取分布式设备类了,需要通过监听迁移按钮的点击事件,然后获取分布式设备列表,选择设备后进行传递,具体代码可以看:获取分布式设备

2.6.3 请求回迁

如果前面调用 continueAbilityReversibly 请求迁移完成后, 源设备可以调用 reverseContinueAbility 发起回迁:

try {

reverseContinueAbility();

} catch (IllegalStateException e) {

}

这里就不具体展示具体流程了,和迁移流程大同小异。

3. Service Ability

====================================================================================

基于 Service 的 Ability ,主要提供的能力是后台运行任务(比如音乐播放、文件下载),不能提供页面UI服务。

Service 是单例的,一个设备上,一个Service 只存在一个实例, 一个 Service 可以绑定多个 Ability, 只有在绑定的 Ability 全部退出后, 这个Service 才能退出。

理论上,它和 Android 的 Service 组件作用一样, 这样看来, Ability 更像是一个 context。

3.1 Service Ability 的生命周期


在这里插入图片描述

它有两种启动方法

  • 启动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

}

]

}

}

3.2 启动 Service


通过 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);

3.3 连接 Service


如果 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();

}

3.4 前台 Service


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: 音乐播放器

4. Data Ability

=================================================================================

Data Ability 有助于应用管理自身和其他应用存储数据的访问,并提供与其他应用共享数据的方法。 Data既可以用于同设备下不同应用的数据共享,也支持跨设备不同应用数据共享

4.1 URI概述


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条数据

4.1 创建 Data


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();

//img-blog.csdnimg.cn/56ba346bdecf4e2180e0376f72f5017d.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3Jpa2thdGhld29ybGQ=,size_16,color_FFFFFF,t_70)

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();

  • 15
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值