当前位置:   article > 正文

2024年鸿蒙Ability学习(1),2024年最新2024大厂HarmonyOS鸿蒙面试经历

2024年鸿蒙Ability学习(1),2024年最新2024大厂HarmonyOS鸿蒙面试经历

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!


img
img

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

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

需要这份系统化的资料的朋友,可以戳这里获取

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

} 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”

]

}

4.2 访问 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用来指定打开文件的方式,这个方法返回一个目标文件的描述符,可以把它封装成文件流,就可以对其进行自定义处理:

// 读取文件描述符

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

5. Intent

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

Intent 是对象之间传递信息的载体。

在这里插入图片描述

当 Intent 用于发起请求时,根据指定元素的不同,分为两种类型:

  • 如果同时指定了 BundleName 与 AbilityName, 则根据 AbilityName 来直接启动应用

  • 如果未同时指定 BundleName 和 AbilityName, 则根据 Operation 中的其他属性来启动应用

5.1 根据 Ability 全称启动应用


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

5.2 其他属性


和 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:

}

}

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!


img
img

既有适合小白学习的零基础资料,也有适合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%以上鸿蒙开发知识点,真正体系化!

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

需要这份系统化的资料的朋友,可以戳这里获取

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/572776
推荐阅读
相关标签
  

闽ICP备14008679号