体验一下使用 ArkUI 进行 HarmonyOS 开发并与 Compose 简单对比

devtools/2024/11/22 17:33:26/

作者:equationl

前言

最近几年各个技术公众号和技术群都在唱衰原生安卓开发,疯狂贩卖焦虑。

搞得我也焦虑的不行,在谷歌的 Compose 推出后就赶紧去学,但是又觉得好像 Compose 的热度也不算太高,又去学 Flutter 。

转头两个都还没学明白呢,大佬们又在说鸿蒙下次更新不兼容安卓了,再不学鸿蒙开发就等着失业吧。

啊?这?这能忍?这必须学啊!

于是抽出时间来简单了解了一下使用 ArkUI 的鸿蒙应用开发

编写第一个鸿蒙应用

搭建环境

鸿蒙应用开发有它自己的一个 IDE ,叫做 DevEco Studio

可以在 官网 下载到,按照你自己的系统选择对应的安装包后直接下载安装即可。

安装过程不用我多介绍吧,毕竟都是老程序员了,这点基础知识还是得有吧。

然后,等你安装完打开你就会发现,这个 IDE 怎么会这么眼熟呢?

眼熟就对了啊,因为:

它也是基于 Intellij PLatform 开发的。

安装好后,首次打开 DevEco Studio 会要求你进行环境下载和配置,因为鸿蒙开发使用的 ArkUI 框架是基于 ArkTS 语言的,而 ArkTS 语言是 TS 语言的超集,所以我们需要配置对应的环境。

按照 IDE 提示首先下载并安装 Node.js 和 Ohpm ,因为上面说过 ArkTS 是基于 TS 的超集,所以需要 Node.js 的支持;而 Ohpm 是华为自己的依赖管理工具。

安装好 Node.js 和 Ohpm 后需要安装鸿蒙的 SDK ,依旧是按照 IDE 提示直接下载安装即可。

安装完成后即可正常使用 IDE 了。

Hello, World!

打开刚安装配置好的 DevEco Studio,在欢迎页面选择 Create Project - Application - Empty Ability 然后点击 Next

在这个页面会要求填写一些应用的基本信息,和安卓开发类似:

  • Project name 是项目名称
  • Bundle name 是包名,也是应用的唯一 ID
  • Save location 就是项目保存位置
  • Compile SDK 要使用的编译 SDK 版本
  • Model 应用程序开发模型,Stage 是新版的模型,FA 是老版本使用的模型,这里选择 Stage 即可,他俩的区别看 这里(吐槽一下,华为的文档链接居然是错的,还得我自己去搜索正确的文档)

修改完后点击 Finsh 即可创建我们的第一个 Hello World 项目。

打开这个项目的主页UI文件:

./entry/src/main/ets/pages/index.ets

然后在右边侧栏点击 Previewer 按钮即可打开 UI 预览:

如果想要运行这个程序的话,我们需要创建一个模拟器:

在顶栏菜单中的运行按钮旁边点击 No devies 下拉框,选择 Device Manager,然后按照提示创建一个模拟器并启动。

(吐槽一下华为魔改的这个 IDE 明明我已经创建过模拟器了,却不能像 Android Studio 一样直接在运行菜单中选择这个模拟器并直接一键启动运行,非得手动进入 Device Manager 里面启动了模拟器才能运行程序)

启动好模拟器后,运行菜单应该已经默认选中了这个模拟器,点击运行图标运行即可:

这样我们就能看到它的运行效果了:

项目结构

接下来,我们来了解一下 ArkUI 的项目的结构,不然我们连需要改哪儿个文件都不知道了,哈哈。

项目整体结构如图:

其中,

AppScope

这个目录存放应用全局所需要的资源文件。

在其下有一个 app.json5 配置文件,该文件是应用的全局配置文件,一个典型的全局配置文件包含以下内容:

{"app": {"bundleName": "com.example.myapplication","vendor": "example","versionCode": 1000000,"versionName": "1.0.0","icon": "$media:app_icon","label": "$string:app_name"}
}

其中的配置分别表示:

  • bundleName 包名。
  • vendor 应用程序供应商。
  • versionCode 数字版本号。
  • versionName 版本名称。
  • icon 应用图标。
  • label 应用名。

另外,在该目录下还有一个 resources 文件夹,用于存放公共资源文件,它包括两个文件夹:

  • ./base/element 主要存放公共的字符串、布局文件等资源。
  • ./base/media 存放全局公共的多媒体资源文件。
entry

这个是 entry 模块的目录,在这个项目中 entry 即为主模块。

在这个模块中可以存放代码、资源等。

其中,./entry/src 包含两个文件夹:

  • main/ 主目录
  • ohosTest/ 单测目录

在 ./entry/src/main 文件夹中包括以下目录和文件:

  • ets/ 用于存放ets代码
  • resources/ 存放模块内的多媒体及布局文件等
  • module.json5 模块的配置文件

而 ./entry/src/main/ets 下又有以下目录:

  • entryability/ 存放 ability 文件,用于当前ability应用逻辑和生命周期管理。
  • pages/ 存放UI界面相关代码文件,初始会生成一个Index页面。

而 ./entry/src/main/resources 目录用于存放模块公共的多媒体、字符串及布局文件等资源,分别存放在element、media文件夹中。

在 ./entry 目录下还有几个文件:

  • build-profile.json5 模块级配置信息,包括编译构建配置项。
  • hvigorfile.ts 模块级构建脚本。
  • oh-package.json5 模块级依赖配置信息文件。
oh_modules

该目录是项目的依赖包,存放工程依赖的源文件。

一般不需要我们去手动更改。

build-profile.json5

该文件是项目级配置信息,包括签名、产品配置等。

hvigorfile.ts

该文件是工程级编译构建任务脚本,hvigor是基于任务管理机制实现的一款全新的自动化构建工具,主要提供任务注册编排,工程模型管理、配置管理等核心能力。

oh-package.json5

该文件是项目级依赖配置文件,用于记录引入包的配置信息。

简单的代码理解

上面简单梳理了一下项目的目录结构,相信大家心里也大概有个底了,其实和安卓项目也大差不差,都是这么一回事儿。

只是依赖管理系统变了(安卓是 gradle ),配置文件变了(安卓是 groovy 或 kts 鸿蒙是 json)。

其他结构对于安卓开发者来说基本属于一看就懂。

接下来我们来看下代码结构。

首先,我们得先找到代码的入口点。

通过上面的介绍,我们已经知道了这个项目的主模块是 entry,所以入口点需要在这个模块目录中寻找。

在该模块的配置文件 module.json5 中,配置了入口 ability

也就是说,这个 ./ets/entryability/EntryAbility.ts 文件就是我们要找的入口点。

打开这个文件,安卓开发者理解起来应该不难,似乎,这个 ability 的概念有点像 Activity ?

可以看到这个 EntryAbility 继承自 UIAbility 并实现了其中的 onCreateonDestroyonWindowStageCreateonWindowStageDestroyonForegroundonBackground 等方法,显然,这些方法就是这个 ability 的生命周期嘛。

然后在 onWindowStageCreate 生命周期中通过 windowStage.loadContent 加载了 pages/Index 的内容。

我们再来看看这个 pages/Index 的内容:

这个就是 ArtUI 的内容了,看起来还是非常的熟悉是吧?也是一看就懂。

Row 嵌套 Column 再添加一个 Text 组件,构成了这个 Hello, World 的 UI 界面。

接下来,我们简要介绍一下上述代码出现的几个要点。

Ability

在上文中,我们说感觉 Ability 有点类似于安卓的 Aciticity ,这其实有一丝道理,但是又不完全对。

在上述代码中,我们使用的是继承自 Ability 的 UIAbility,根据官方介绍:

UIAbility是一种包含用户界面的应用组件,主要用于和用户进行交互。UIAbility也是系统调度的单元,为应用提供窗口在其中绘制界面。

每一个UIAbility实例,都对应于一个最近任务列表中的任务。

一个应用可以有一个UIAbility,也可以有多个UIAbility。

一个UIAbility可以对应于多个页面。

从这段介绍中可以看出,虽然 Activity 和 UIAbility 都是程序的 UI 界面的承载组件,且每个程序都可以有一个或多个 UIAbility(Activity),每个 UIAbility(Activity) 也可以对应一个或多个页面。但是,多个 Activity 并不会出现多个页面在最近任务栏中(一般情况下),而一个 UIAbility 就对应了最近任务栏中的一个页面。

另外,和 Activity 一样,UIAbility 也是系统调度的单元,具有自己的生命周期,UIAbility 的生命周期如下:

它的生命周期流程和 Acticity 类似。

最后,UIAbility 也具有不同的启动模式,但是相比 Activity 的启动模式概念略有不同。

它只有三种启动模式:singleton(单实例模式)、multiton(多实例模式)和specified(指定实例模式)

singleton 模式会确保当前应用进程中的同一个 UIAbility 仅有一个,当以该模式启动某个 UIAbility 时,如果进程中已存在相同的 UIAbility 实例,则会直接复用该实例。

multiton 模式会在每次以该模式启动 UIAbility 时都创建一个新的 UIAbility 实例,而无论这个 UIAbility 是否已经在当前进程中存在。

specified 模式则可以在创建 UIAbility 时指定一个 key 与之绑定,之后打开新的 UIAbility 时都需要提供一个 key,如果该 key 存在已经绑定了的 UIAbility 则会直接复用这个 UIAbility,否则用这个 key 创建一个新的 UIAbility。

ArkUI

接着,我们再来看看具体的 UI 部分(pages/Index),显然这里的 UI 使用的是 ArkUI 实现的。

在 ArkUI 中通过 struct 关键字来声明一个 UI 组件:

struct Index { }

然后通过 @Component 装饰表明 Index 是一个自定义的 ArkUI 组件:

@Component
struct Index { }

同样的,还有一个叫做 @Entry 的装饰用于指明该组件是该页面的入口组件:

@Entry
@Component
struct Index { }

经过上述操作,我们已经声明好了 Index 页面的入口自定义组件。

接下来只需要在 Index 添加描述这个组件的内容即可,为此,我们需要在其中添加一个 build() { }

@Entry
@Component
struct Index {// ……build() {// …… 在此编写布局}
}

我们可以在 build 中添加内置 UI 组件或自定义组件用于描述我们的页面组成,例如样例中的居中显示一个 “Hello, World” 文本:

@Entry
@Component
struct Index {build() {Row() {Column() {Text("hello, world")}.width('100%')}.height('100%')}
}

我们还可以通过链式调用的方式,修改组件的参数:

Text(”hello, world“).fontSize(50).fontWeight(FontWeight.Bold)

这样,就把文本修改为字号 50,字重 加粗。

当然,上面的界面是没有状态的,作为声明式 UI 当然要给它定义一个状态了。

首先,我们声明一个 message 变量表示需要显示的文字:

message: string = 'Hello World'

然后,为该变量加上 @State 装饰即表示将该变量与 UI 绑定,当该变量值发生改变时,使用到该变量的 UI 也会自动重新渲染:

@State message: string = 'Hello World'

为了能够看到状态改变的效果,我们为这个文本添加一个点击事件,并在点击后更改 message 值:

Text(this.message).fontSize(50).fontWeight(FontWeight.Bold).onClick(() => {this.message = `我被点击了 ${this.count} 次`;this.count++;})

现在,只要我们点击这个文本,它就会自动变更为 “我被点击了 xx 次”,其中 xx 是点击次数:

该部分完整代码如下:

@Entry
@Component
struct Index {count: number = 1@State message: string = 'Hello World'build() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold).onClick(() => {this.message = `我被点击了 ${this.count} 次`;this.count++;})}.width('100%')}.height('100%')}
}

对比 Compose

在上文中,我们已经完成了一个简单的使用 ArkUI 的鸿蒙应用,接下来,我们用 Compose 实现一个同样布局同样功能的应用做一个简单的对比。

首先是 UI 布局的承载者,

在 Compose 中使用的是 Activity:

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {Greeting()}}
}

在 Activity 的 onCreate 生命周期通过 setContent 设置具体的 UI 实现。

而在鸿蒙中则是使用 UIAbility:

export default class EntryAbility extends UIAbility {onWindowStageCreate(windowStage: window.WindowStage) {windowStage.loadContent('pages/Index', (err, data) => {});}
}

在 UiAbility 的 onWindowStageCreate 生命周期,通过 windowStage.loadContent 设置具体的 UI 实现。

而在具体的 UI 实现部分,Compose 代码如下:

@Composable
fun Greeting() {var count by remember { mutableStateOf(1) }var message by remember { mutableStateOf("Hello, World") }Row(modifier = Modifier.fillMaxHeight(),verticalAlignment = Alignment.CenterVertically) {Column(modifier = Modifier.fillMaxWidth(),horizontalAlignment = Alignment.CenterHorizontally) {Text(text = message,fontSize = 50.sp,fontWeight = FontWeight.Bold,modifier = Modifier.clickable {message = "我被点击了 $count 次"count++})}}
}

ArkUI 代码如下:

@Entry
@Component
struct Index {count: number = 1@State message: string = 'Hello World'build() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold).onClick(() => {this.message = `我被点击了 ${this.count} 次`;this.count++;})}.width('100%')}.height('100%')}
}

需要注意的是,在 Compose 的实现中,我是为了尽可能的完全模拟 ArkUI 的界面才这样写的,实际上如果想要实现文本居中根本不需要嵌套 RowColumn ,直接使用其中任意一个然后:

Row(modifier = Modifier.fillMaxSize(),verticalAlignment = Alignment.CenterVertically,horizontalArrangement = Arrangement.Center
) {// ……
}   

另外,其实 ArkUI 也不用嵌套,也是直接使用任意一个,然后:

Row() {// ……
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)

猜测这里的 Hello World 示例使用了 Row 嵌套 Column 是为了更多的展示 ArkUI 的特性吧。

另外,这里的 ColumnRow 在默认对齐方式上也有所区别,以 Column 为例,在 ArkUI 中默认是主轴(垂直方向)在 Top ,副轴(水平方向)居中;而 Compose 中则是默认垂直方向(主轴)在 Top,水平方向(副轴)在 Start 。

对于自定义组件的声明方式也有所不同,在 ArkUI 中是通过 struct 结构附加 @Component ;而 Compsoe 则是通过在一个函数上附加 @Composable 表示。

通过上述 Compose 和 ArkUI 的代码对比,我们还可以看出,ArkUI 声明有状态的变量只需要在前面加上 @State 装饰即可; Compose 则需要将其置于 mutableStateOf() 中,并且 Compose 在 Composeable 作用域中的变量还需要包裹在 remember 中,否则每次重组(UI变化)这个变量都会被重复初始化。

另外,对于组件参数的修改,在 Compose 中是通过直接给这个组件函数的参数传值实现,例如修改文本的字号和字重:

Text(text = message,fontSize = 50.sp,fontWeight = FontWeight.Bold,
)

当然,每个组件需要修改的参数也许会有很多,所以不可能每个参数都要给组件函数加一个形参,这会让函数很臃肿,所以在 Compose 中几乎每个组件都有一个 modifier 参数,这个参数中集成了非常多的常用参数修改,例如上述例子中的添加点击监听就是通过 modifier 添加的。

而这个 modifier 的参数修改也是类似于 ArkUI 的链式调用。

对于 ArkUI 来说,所有参数的修改则完全是使用在组件函数之后链式调用来实现的,例如修改字号和字重:

Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)

以上就是对 Compose 和 ArkUI 的简单对比。

总结

经过上述的简单了解,我们可以发现,其实不管是 Android 的 Jetpack Compose 还是 Apple 的 SwiftUI 还是刚才说的鸿蒙的 ArtUI,其实核心思想都是差不多的(毕竟都是声明式 UI 的思想),对于我们普通程序员来说写起来也是差不多的感觉,无非是语法和部分风格上略微有区别。

另外前言中说的事件既是段子也是一部分现实,但是还是希望各位能弄清楚自己的定位,有一个明确的规划,不要东一榔头西一棒子的看见啥有热度就学啥。

当然,有余力的话多了解了解其他技术还是挺有帮助的。

码牛课堂也为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线。大家可以进行参考学习:https://qr21.cn/FV7h05

①全方位,更合理的学习路径
路线图包括ArkTS基础语法、鸿蒙应用APP开发、鸿蒙能力集APP开发、次开发多端部署开发、物联网开发等九大模块,六大实战项目贯穿始终,由浅入深,层层递进,深入理解鸿蒙开发原理!

②多层次,更多的鸿蒙原生应用
路线图将包含完全基于鸿蒙内核开发的应用,比如一次开发多端部署、自由流转、元服务、端云一体化等,多方位的学习内容让学生能够高效掌握鸿蒙开发,少走弯路,真正理解并应用鸿蒙的核心技术和理念。

③实战化,更贴合企业需求的技术点
学习路线图中的每一个技术点都能够紧贴企业需求,经过多次真实实践,每一个知识点、每一个项目,都是码牛课堂鸿蒙研发团队精心打磨和深度解析的成果,注重对学生的细致教学,每一步都确保学生能够真正理解和掌握。

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

鸿蒙开发学习手册》:https://qr21.cn/FV7h05

如何快速入门:

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

鸿蒙开发面试真题(含参考答案):https://qr21.cn/FV7h05

大厂鸿蒙面试题::https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.
鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向


http://www.ppmy.cn/devtools/15618.html

相关文章

SQL获取最后一次的数据

问题 有个表格(id,machineName,value,updatetime),里面比如有10个机台,里面记录了这10个机台的几十万条数据 如何获取每个机台的最后一笔数据? machines表 解决办法 1.首先获得每个机台最后的更新时间 select machineName,max(updatetim…

花粉过敏人群或超2亿?约克VRF中央空调助你健康鲜呼吸

“一朝春雨落,十里桃花开”,暖春、微风、阳光、花香……充满着生机的春天让人心情愉悦,然而对于易过敏人群来说却是“苦不堪言”,经常出现眼圈发红、发痒、睁不开,每天都在“流泪”中度过,同时伴随着咳嗽、打喷嚏、流鼻涕、鼻塞等症状,皮肤也可能会出现红疹、瘙痒等症状。 如何…

MyBatis基础操作

黑马程序员JavaWeb开发教程 文章目录 根据资料中提供的《tlias智能学习辅助系统》页面原型及需求,完成员工管理的需求开发一、环境准备1、准备数据库表emp2、创建一个新的springboot工程,选择引入对应的起步依赖(mybatis、mysql驱动、lombok&…

基于SpringBoot的合家云社区物业管理平台 - 数据库设计文档

合家云数据库设计文档V1.0 社区资产模块 社区表:hjy_community字段名称字段类型是否为空值字段释义community_idbigint(20)NO小区idcommunity_namevarchar(128)YES小区名称community_codevarchar(128)YES小区编码community_provence_codevarchar(32)YES省区划码community_cit…

GitLab存储空间满了

1. 背景 前两天同事反馈,代码推送不到服务器了,GitLab报500错误,我等了一天都变好。登录服务器查看GitLab虚拟机,发现磁盘满了。于是用如下命令释放出了一部分空间: journalctl --vacuum-time1s 释放了1秒前的日志&am…

关于SM2软件加密与硬件加密的问题

SM2国密算法在中国的密码学领域中使用得非常广泛,它是一种基于椭圆曲线公钥密码的算法。日常开发工作中,涉及到SM2算法的加密和解密时,如果一方使用软件实现,而另一方使用硬件实现,可能会遇到一些联调过程中的问题。在…

Linux CAN信号收发模拟

在Linux环境下进行CAN(Controller Area Network)信号的模拟收发通常需要使用一些工具和库。以下是一种可能的方法: SocketCAN驱动程序:SocketCAN是Linux内核中的一个子系统,提供了CAN总线的抽象接口,允许用…

opencv 打开中文路径图报错

img cv.imread(中文图, 1) [ WARN:06.414] global D:\a\opencv-python\opencv-python\opencv\modules\imgcodecs\src\loadsave.cpp (239) cv::findDecoder imread_(‘D:/download/中文.png’): can’t open/read file: check file path/integrity 转换编码无法解决 file_pa…