我总感觉鸿蒙是资本家的骗局 我先溜了
鸿蒙开发官方文档:https://developer.harmonyos.com/cn/docs/documentation/doc-guides/start-with-js-0000001168007068
一、基础知识
HarmonyOS采用了多种分布式技术,使应用开发与不同终端设备的形态差异无关,从而让开发者能够聚焦上层业务逻辑,更加便捷、高效地开发应用。
一套操作系统可以满足不同能力的设备需求,实现统一OS,弹性部署
技术架构
HarmonyOS整体遵从分层设计,从下向上依次为:内核层、系统服务层、框架层和应用层。系统功能按照“系统 > 子系统 > 功能/模块”逐级展开,在多设备部署场景下,支持根据实际需求裁剪某些非必要的子系统或功能/模块。HarmonyOS技术架构如图所示。
内核层
- 内核子系统:HarmonyOS采用多内核设计,支持针对不同资源受限设备选用适合的OS内核。内核抽象层(KAL,Kernel Abstract Layer)通过屏蔽多内核差异,对上层提供基础的内核能力,包括进程/线程管理、内存管理、文件系统、网络管理和外设管理等。
- 驱动子系统:HarmonyOS驱动框架(HDF)是HarmonyOS硬件生态开放的基础,提供统一外设访问能力和驱动开发、管理框架。
系统服务层
系统服务层是HarmonyOS的核心能力集合,通过框架层对应用程序提供服务。该层包含以下几个部分:
- 系统基本能力子系统集:为分布式应用在HarmonyOS多设备上的运行、调度、迁移等操作提供了基础能力,由分布式软总线、分布式数据管理、分布式任务调度、方舟多语言运行时、公共基础库、多模输入、图形、安全、AI等子系统组成。其中,方舟运行时提供了C/C++/JS多语言运行时和基础的系统类库,也为使用方舟编译器静态化的Java程序(即应用程序或框架层中使用Java语言开发的部分)提供运行时。
- 基础软件服务子系统集:为HarmonyOS提供公共的、通用的软件服务,由事件通知、电话、多媒体、DFX(Design For X) 、MSDP&DV等子系统组成。
- 增强软件服务子系统集:为HarmonyOS提供针对不同设备的、差异化的能力增强型软件服务,由智慧屏专有业务、穿戴专有业务、IoT专有业务等子系统组成。
- 硬件服务子系统集:为HarmonyOS提供硬件服务,由位置服务、生物特征识别、穿戴专有硬件服务、IoT专有硬件服务等子系统组成。
根据不同设备形态的部署环境,基础软件服务子系统集、增强软件服务子系统集、硬件服务子系统集内部可以按子系统粒度裁剪,每个子系统内部又可以按功能粒度裁剪。
框架层
- 框架层为HarmonyOS的应用程序提供了Java/C/C++/JS等多语言的用户程序框架和Ability框架,以及各种软硬件服务对外开放的多语言框架API;同时为采用HarmonyOS的设备提供了C/C++/JS等多语言的框架API,不同设备支持的API与系统的组件化裁剪程度相关。
应用层
- 应用层包括系统应用和第三方非系统应用。HarmonyOS的应用由一个或多个FA(Feature Ability)或PA(Particle Ability)组成。其中,FA有UI界面,提供与用户交互的能力;而PA无UI界面,提供后台运行任务的能力以及统一的数据访问抽象。基于FA/PA开发的应用,能够实现特定的业务功能,支持跨设备调度与分发,为用户提供一致、高效的应用体验。
HarmonyOS关键特性
硬件互助,资源共享——超级终端
一次开发,多端部署
统一OS,弹性部署
分布式技术
包括:分布式软总线、分布式数据管理、分布式文件管理、分布式任务调度、分布式设备虚拟化
分布式软总线:是基础,是底层通信机制(WIFI、BLE等)的软件包装和管理
分布式数据管理:业务与数据分离,跨设备产生、存储和使用数据和本地一样方便
分布式文件管理:跨设备文件访问和访问本地文件一样方便
分布式任务调度:跨设备对应用进行远程启动、远程调用、远程连接以及迁移
分布式数据管理、分布式文件管理、分布式任务调度,这三个是分布式在系统服务层的封装,都会调用到分布式软总线。
分布式设备虚拟化:是分布式在系统应用层从效果出发的描述
一次开发,多端部署
二、创建第一个harmonyos工程
安装DevEco Studio就不用说了
用js或java开发的话选3.1.0以下的因为3.1.0以上的版本用的是低代码(arkts)
{华为自研语言hh}
1.创建和配置新工程
-
打开 DevEco Studio,在欢迎页点击 Create HarmonyOS Project,创建一个新工程。根据工程创建向导,选择需要的 Ability 工程模板,然后点击 Next。
-
如果已经打开了工程,可以在菜单栏选择 File > New > New Project 来创建一个新工程。
-
点击 Next,进入到工程配置阶段,需要根据向导配置工程的基本信息。
-
- Project Name:工程的名称,可以自定义。
-
- Project Type:工程的类型,标识该工程是一个原子化服务(Atomic Service)或传统方式的需要安装的应用。
-
- Package Name:软件包名称,默认情况下,应用ID也会使用该名称,应用发布时,应用ID需要唯一。
-
- Save Location:工程文件本地存储路径。
-
- Compatible API Version:兼容的SDK最低版本。
-
- Device Type:该工程模板支持的设备类型,支持多选。如果勾选多个设备,表示该原子化服务或传统方式的需要安装的应用支持部署在多个设备上。
-
- Show in Service Center:是否在服务中心露出。如果 Project Type 为 Service,则会同步创建一个2 * 2的服务卡片模板,同时还会创建入口卡片;如果 Project Type为Application,则只会创建一个2 * 2的服务卡片模板。
- Show in Service Center:是否在服务中心露出。如果 Project Type 为 Service,则会同步创建一个2 * 2的服务卡片模板,同时还会创建入口卡片;如果 Project Type为Application,则只会创建一个2 * 2的服务卡片模板。
-
点击 Finish,工具会自动生成示例代码和相关资源,等待工程创建完成。
2.使用模拟器运行 Hello World
- 在 DevEco Studio 菜单栏,点击 Tools > Device Manager。
- 选择Remote Emulator登录
- 在浏览器中弹出华为开发者联盟帐号登录界面,请输入已实名认证的华为开发者联盟帐号的用户名和密码进行登录。
- 在设备列表中,选择 Phone 设备,并点击运行按钮,运行模拟器。
- 点击 DevEco Studio 工具栏中的运行按钮运行工程,或使用默认快捷键Shift+F10(Mac为Control+R)运行工程。
- DevEco Studio会启动应用的编译构建,完成后应用即可运行在模拟器上。
三、harmonyos工程结构(基于js)
JS工程目录结构
entry: HarmonyOS工程模块,编译构建生成一个HAP包。
- src > main > js :用于存放js源码。
- src > main > js > MainAbility :应用/服务的入口。
- src > main > js > MainAbility > i18n :用于配置不同语言场景资源内容,比如应用文本词条、图片路径等资源。
- src > main > js > MainAbility > pages :MainAbility包含的页面。
- src > main > js > MainAbility > app.js :承载Ability生命周期。
- src > main > resources :用于存放应用/服务所用到的资源文件,如图形、多媒体、字符串、布局文件等。关于资源文件的详细说明请参考资源限定与访问。
- src > main > config.json :模块配置文件。主要包含HAP包的配置信息、应用/服务在具体设备上的配置信息以及应用/服务的全局配置信息。具体的配置文件说明,详见配置文件说明(JS/ArkTS)。
- build-profile.json5 :当前的模块信息 、编译信息配置项,包括buildOption、targets配置等。
- hvigorfile.js :模块级编译构建任务脚本,开发者可以自定义相关任务和代码实现。
build-profile.json5 : 应用级配置信息,包括签名、产品配置等。
hvigorfile.js : 应用级编译构建任务脚本。
JS UI特性
- 声明式编程:JS Ul框架采用类HTML和CSS声明式编程语言作为页面布局和页面样式的开发语言,页面业务逻辑则支持ECMAScript规范的JavaScript语言。JS UI框架提供的声明式编程,可以让开发者避免编写UI状态切换的代码,视图配置信息更加直观。
- 跨设备:开发框架架构上支持UI跨设备显示能力,运行时自动映射到不同设备类型,开发者无感知,降低开发者多设备适配成本。
- 高性能:开发框架包含了许多核心的控件,如列表、图片和各类容器组件等,针对声明式语法进行了渲染流程的优化。
JS UI的整体架构
JS UI框架包括应用层(Application)、前端框架层(Framework)、引擎层(Engine)和平台适配层( Porting Layer) 。
- Application:应用层是开发者接触最多的一层,在这一层可以通过框架层提供的能力来通过JS来开发上层应用;
- Framework:前端框架层主要完成前端页面解析,以及提供MVVM (Model-View-ViewModel)开发模式、页面路由机制和自定义组件等能力;
- Engine:引擎层主要提供动画解析、DOM(Document Object Model)树构建、布局计算、渲染命令构建与绘制、事件管理等能力;
- Porting Layer:适配层主要完成对平台层进行抽象,提供抽象接口,可以对接到系统平台。比如:事件对接、渲染管线对接和系统生命周期对接等;
JS UI框架支持纯JavaScript、JavaScript和Java混合语言开发。JS FA指基于JavaScript或JavaScript和Java混合开发的FA,FA在HarmonyoS上运行时需要的基类AceAbility :
AceAblility基类:
AceAbility类是JS FA在HarmonyOS上运行环境的基类,继承自Ability。通过JS来开发HarmonyOS需要继承此类来定义自己的Ability,如:
public class MainAbility extends AceAbility {
@override
public void onstart(Intent intent) {
super.onstart(intent);
}
@override
public void onstop(){
super.onStop();
}
}
如何加载JS FA?
JS FA生命周期事件分为应用生命周期和页面生命周期,应用通过AceAbility
类中 setInstanceName()
接口设置该Ability
的实例资源,并通过AceAbility
窗口进行显示以及全局应用生命周期管理。
setInstabceName(String name)
的参数“name”指实例名称,实例名称与config.json
文件
中 profile.application. js.name
的值对应。若未修改实例名,而使用了缺省值default,则无需调用此接口。若修改了实例名,则需在应用Ability
实例的onStart()
中调用此接口,并将参数"“name"设置为修改后的实例名称。
提示:多实例应用的 profile.application.js字段中有多个实例项,使用时请选择相应的实例名称。
setInstanceName()
接口使用方法:在MainAbility
的onStart()
中的super.onStart()
前调用此接口。以JsComponentName
作为实例名称,代码示例如下:
public class MainAbility extends AceAbility {
@override
public void onstart (Intent intent) {
setInstanceName("JSComponentName");// config.json配置文件中ability.js.name的标签值。
super.onStart(intent);
}
}
注意:需在super.onStart(Intent)前调用此接口。
布局单位
布局最基本的信息是布局单位,为了方便开发者进行页面布局而不需要关心不同设备的适配问题,在HarmonyOS的JS UI框架中为不同类型的设备提供了一个基准宽度:
- 智慧屏的基准宽度为720px (px指逻辑像素,非物理像素);穿戴设备的基准宽度为454px;
- 穿戴设备的基准宽度为454px;
如何理解基准宽度呢,以智慧屏为例,720px的基准宽度,如果你在代码设置了某个布局或控件的宽度为720px,那么无论是它运行在1440px的设备上还是运行在其他分辨率的设备上都能实现占满整个屏幕宽度的效果;
我们不妨计算一下,720px的基准宽度,运行在1440px的设备上它的1px代表2px的设备像素,所以对于视觉同学出视觉稿的时候就可以按照720px的宽度标准来进行视觉标注了;
实战:基于JS UI实现计算器界面布局(暂未完成)
<div class="container">
<text class="result">0</text>
<div class="row">
<text class="cell-normal">AC</text>
<text class="cell-normal">+/-</text>
<text class="cell-normal">%</text>
<text class="cell-normal cell-special ">÷</text>
</div>
<div class="row">
<text class="cell-normal">7</text>
<text class="cell-normal">8</text>
<text class="cell-normal">9</text>
<text class="cell-normal cell-special">x</text>
</div>
<div class="row">
<text class="cell-normal">4</text>
<text class="cell-normal">5</text>
<text class="cell-normal">6</text>
<text class="cell-normal cell-special">+</text>
</div>
<div class="row">
<text class="cell-normal">1</text>
<text class="cell-normal">2</text>
<text class="cell-normal">3</text>
<text class="cell-normal cell-special">÷</text>
</div>
<div class="row">
<text class="cell-normal cell-special2">0</text>
<text class="cell-normal">.</text>
<text class="cell-normal cell-special">=</text>
</div>
</div>
.container {
flex-direction: column;
justify-content: center;
align-items: center;
background-color: #bebebe;
}
.result{
text-align: right;
color: #000;
padding-right: 10;
width: 454px;
height: 200px;
}
.row{
flex-direction: row;
}
.cell-normal{
background-color: #a9a9a9;
margin: 1px;
flex: 1;/*占几格*/
color: #000;
text-align: center;
}
.cell-special{
background-color: #f38b24;
}
.cell-special2{
flex: 2;
}
四、页面生命周期解析
基于Java开发的生命周期
Ability生命周期
onStart():当系统首次创建页面实例时,触发该回调:
- 对于一个页面实例,该回调在其生命周期过程中仅触发一次(类似于Android的onCreate)
- 页面在该逻辑后将进入INACTIVE状态;
- 开发者必须重写该方法,并在此配置默认展示的AbilitySlice;
@override
public void onstart(Intent intent) {
super.onStart(intent);
super.setMainRoute(FooSlice.class.getName());
}
onActive():页面会在进入INACTIVE状态后来到前台,然后系统调用此回调:
- 页面在此之后进入
ACTIVE
状态,进入该状态后标志着应用可与用户进行交互(类似Android的onResume()) ; - 在接下来的一定时间内页面将保持在此状态,除非某类事件发生导致页面失去焦点,比如用户点击返回键或导航到其他页面;
- 当页面失去焦点时会触发页面回到
INACTIVE
状态,系统将调用onInactive()
回调; - 在开发时通常在
onActive()
中获取资源,并在onInactive()
中被释放的资源;
onInactive():当页面失去焦点时,系统将调用此回调,此后页面进入INACTIVE
状态(类似Android的onPause())
onBackground():如果页面不再对用户可见,系统将调用此回调通知开发者用户进行相应的资源释放,此后页面进入BACKGROUND
状态:
- 开发者应该在此回调中释放页面不可见时无用的资源,或在此回调中执行较为耗时的状态保存操作;
onForeground():处于BACKGROUND
状态的页面仍然驻留在内存中,当重新回到前台时(比如用户重新导航到此页面),系统将先调用onForeground()
回调通知开发者,而后页面的生命周期状态回到INACTIVE
状态:
- 开发者应当在此回调中重新申请在
onBackground()
中释放的资源,最后页面的生命周期状态进一步回到ACTIVE
状态,系统将通过onActive()
回调通知开发者用户;
onStop():系统将要销毁页面时,将会触发此回调函数,通知用户进行系统资源的释放(类似Android的onDestory()
)销毁页面的可能原因包括以下几个方面:
- 用户通过系统管理能力关闭指定页面,例如使用任务管理器关闭页面;
- 用户行为触发页面的
terminateAbility()
方法调用,例如使用应用的退出功能; - 配置变更导致系统暂时销毁页面并重建;
- 系统出于资源管理目的,自动触发对处于
BACKGROUND
状态页面的销毁;
AbilitySlice生命周期
AbilitySlice作为页面的组成单元,其生命周期是依托于其所属页面生命周期的。AbilitySlice和页面具有相同的生命周期状态和同名的回调,当页面生命周期发生变化时,它的AbilitySlice也会发生相同的生命周期变化。
当一个页面中发生了AbilitySlice的导航切换时,它的生命周期变化将独立于页面的生命周期变化
AbilitySlice生命周期回调与页面的相应回调类似,另外由于AbilitySlice承载具体的页面,开发者必须重写AbilitySlice的onStart()
回调,并在此方法中通过setUIContent()
方法设置页面:
@override
protected void onStart(Intent intent){
super.onStart(intent);
setUIContent(ResourceTable.Layout_main_layout);
}
重写方法测试:
基于JS开发的生命周期
应用生命周期主要有两个:应用创建时调用的onCreate
和应用销毁时触发的onDestroy
。
一个应用中可能会有多个页面,一个页面一般包括onInit
、onReady
、onShow
和onDestroy
等在页面创建、显示和销毁时会触发调用的事件:
- onInit(): 表示页面的数据已经准备好,可以使用
js
文件中的data
数据。 - onReady(): 表示页面已经编译完成,可以将界面显示给用户。
- onShow() : JS UI只支持应用同时运行并展示一个页面,当打开一个页面时,上一个页面就销毁了。当一个页面显示的时候,会调用
onShow
。 - onDestroy() : 页面销毁时被调用。
当应用从页面A跳转到页面B时,首先调用页面A的onDestroy
函数。页面A销毁后,依次调用页面B的onInit,onReady
、onShow
函数来初始化和显示页面B。
重写方法测试:
五、页面跳转和传参
页面跳转和传参(Java)
页面间跳转分为Page (Ability) 内跳转,和Page (Ability) 外跳转两种场景,两种场景跳转都需要借助Intent
,另外传递参数也可以借助Intent
来携带参数(这点和Android类似)∶
Page(Ability)内跳转
这种场景的跳转类似于Android应用内的跳转
在同一Page (Ability) 内跳转进跳转时,当发起跳转的AbilitySlice
和跳转目标的AbilitySlice
处于同一个Page
时,可以通过present()
方法实现跳转︰
@Override
protected void onStart( Intent intent){
Button button = ...;
button.setclickedListener(listener -> present(new TargetSlice(),new Intent()));
}
如果在跳转返回时需要获得其返回结果,则可以使用 presentForResult()
实现跳转。用户从跳转目标AbilitySlice
返回时,系统将回调onResult()
来接收和处理返回结果,此时需要重写该方法。返回结果由跳转目标AbilitySlice
在其生命周期内通过setResult()
进行设置:
鸿蒙系统为每个Page维护了一个AbilitySlice
实例的栈,每个进入前台的AbilitySlice
实例均会入栈。当开发者在调用present()
或presentForResult()
时指定的AbilitySlice
实例已经在栈中存在时,则栈中位于此实例之上的AbilitySlice
均会出栈并被销毁。
Page(Ability)间跳转
这种场景的跳转类似于Android应用间的跳转;
由于不同Page中的AbilitySlice相互不可见,因此无法通过present()
或presentForResult()
方法直接导航到其他Page的AbilitySlice。AbilitySlice作为Page的内部单元,以Action的形式对外暴露
因此可以通过配置lntent的Action导航到目标AbilitySlice。Page间的导航可以使
用startAbility()
或 startAbilityForResult()
方法,获得返回结果的回调为onAbilityResult()
。在Ability中调用setResult()
可以设置返回结果。
另外,能够跳转到其它Page (Ability)的前提是要跳转到的这个Page (Ability)需要配置对应的action:
第一步:配置action
1.首先需要在配置文件中声明对外提供的能力,以便系统据此找到自身并作为候选的请求处理者:
2.在Ability中配置路由以便支持以此action导航到对应的AbilitySlice:
3.在Ability中处理请求,并调用setResult()方法暂存返回结果:
第二步:进行页面间跳转
在上述代码中我们在Ability中构造了Intent以及包含Action的Operation对象,并调用startAbilityForResult()
方法发起请求。然后重写onAbilityResult()
回调方法,对请求结果进行处理。
页面跳转和传参(JS)
为了方便开发者在JS中进行页面跳转,HarmonyOS提供了router
API,通过该API可以完成页面之间的导航:
- router.push: 跳转页面
- router.replace: 将其他页面替换成当前页面
- router.back: 返回上一级页面
- router.clear: 清空栈中其他页面仅保留当前页面
- router.getLength: 获取当前栈里的页面数量
- router.getState: 获取当前页面的状态信息
注意:页面路由需要在页面渲染完成之后才能调用,在onInit和onReady生命周期中页面还处于渲染阶段,在这两个生命周期方法中调用页面路由方法。
通过router.push()跳转到指定页面
方法原型:router.push(OBJECT)
跳转到应用内的指定页面
参数说明:
示例:
如果pageA跳转到pageB(携带参数),再用pageB跳转(back方法)到pageA(携带参数)就用router.replace
方法
示例:
六、调试技巧
点击Run > Edit Configurations > Debugger,在HarmonyOS App中,选择相应模块,可以进行Java/JS/C++调试配置:
如果是在class里就选择java语言
如果是在js里就选择js语言
进行Debug就可以了
不建议使用自动调试 因为有可能无法识别而报错
Debug控制面板跟其他ide一样操作
如果程序已经在运行 又不想结束运行 可以用这种操作把debug贴到运行中的程序进行调试 巧用Attach
选择相应的语言进行调试即可