教不会你算我输系列 | 手把手教你HarmonyOS应用开发
点击蓝字,关注我们
01
1.1 技术概念
在万物智联时代重要机遇期,鸿蒙系统结合移动生态发展的趋势,提出了三大技术理念:一次开发,多端部署;可分可合,自由流转;统一生态,原生智能。
1.1.1 一次开发,多端部署
一次开发,多端部署:指的是一个工程,一次开发上架,多端按需部署。目的是为了支撑开发者高效的开发多种终端设备上的应用。为了实现这一目的,鸿蒙系统提供了几个核心能力,包括多端开发环境,多端开发能力及多端分发机制。
1.1.2 可分可合,自由流转
元服务是鸿蒙系统提供的一种全新的应用形态,具有独立入口,用户可以通过点击、碰一碰,扫一扫等方式直接触发,无需显式安装,由程序框架后台静默安装使用,可为用户提供便捷服务。
可分可合:在开发态,开发者通过业务解耦,把不同的业务拆分为多个模块。在部署态,开发者可以将一个或多个模块自由组合,打包成一个 App Pack 统一上架。在分发运行态,每个 HAP 都可以单独分发满足用户单一使用场景,也可以多个 HAP 组合分发满足用户更加复杂的使用场景。
自由流转:传统应用只能在单个设备内运行,当用户有多个设备,且要完成多个任务时,则要在多个设备间来回切换。因此应用能够在设备之间流转,不间断给用户提供服务的能力就变得非常重要。鸿蒙系统提供了自由流转的能力,使得开发者可以方便的开发出跨越多个设备的应用,用户也能够方便的使用这些功能。
1.1.3 统一生态,原生智能
统一生态:移动操作系统和桌面操作系统的跨平台应用开发框架不尽相同,从渲染方式的角度可以归纳为 WebView 染原、原生渲染和自渲染这三类,鸿蒙系统对应的提供系统 WebView、ArkUI 框架和 XComponent 能力来支撑三种类型的跨平台框架的接入。
原生智能:鸿蒙系统内置强大的 AI 能力,面相鸿蒙生态应用的开发,通过不同层次的 AI 能力开放,满足开发者的不同开发场景下的诉求,降低应用的开发门槛,帮助开发者快速实现应用智能化。
1.2 开发套件
鸿蒙开发套件包含设计、开发、测试、运维套件以及 OS 开放能力集:
HarmonyOS Design:鸿蒙视觉设计套件
ArkTS:鸿蒙编程语言
ArkUI:鸿蒙声明式UI框架
ArkCompoler:鸿蒙方舟编译器
DevEco Studio:鸿蒙开发IDE
DevEco Testing:鸿蒙测试套件
AppGallery Connect:鸿蒙应用发布套件
1.2.1 开发语言 - ArkTS
1.2.2 ArkUI框架
ArkUI框架提供开发者两种开发方式:基于ArkTS的声明式开发范式和基于JS扩展的类Web开发范式。声明式开发范式更加简洁,类 Web 开发范式对 Web 及前端开发者更友好。
(1)ArkTS的声明式开发范式:
采用声明式开发范式进行应用开发,相同场景下,对比类 Web 开发范式代码更为精简,并且在性能内存方面进一步优化提升。另外 ArkUI 框架还提供了 API 扩展机制,通过此种机制进行封装风格统一的 JS 接口。包括状态管理、布局、组件化、装饰器、动效、事件交互、绘制能力、混合开发、跨平台。
状态管理
声明式开发范式的核心思想是数据驱动 UI 变化,通过提供的状态进行数据管理,这里状态管理指的是,管理数据发生变化时,UI 组件更新的范围。
@State:组件拥有的状态属性,当@State 装饰的变量更改时,组件会重新渲染更新 UI。
@Link:组件依赖于其父组件拥有的某些状态属性,当任何一个组件中的数据更新时,另一个组件的状态都会更新,父子组件重新渲染。
@Prop:类似@Link,但子组件所做的更改不会同步到父组件上,属于单向传递。
@Provide:作为数据的提供方,可以更新其子节点的数据,并触发页面渲染。
@Consume:在感知到@Provide 数据更新后,会触发当前自定义组件的重新渲染。
@StorageLink :组件通过使用@StorageLink(key)装饰的状态变量,与AppStorage 建立双向数据绑定,key为 AppStorage 中的属性键值。当创建包含@StorageLink 的状态变量的组件时,该状态变量的值将使用AppStorage 中的值进行初始化。在 UI 组件中对@StorageLink 的状态变量所做的更改将同步到AppStorage ,并从 AppStorage 同步到任何其他绑定实例中。
@StorageProp :组件通过使用@StorageProp(key)装饰的状态变量,将与 AppStorage 建立单向数据绑定,key 标识 AppStorage 中的属性键值。当创建包含@StoageProp 的状态变量的组件时,该状态变量的值将使用 AppStorage 中的值进行初始化。AppStorage 中的属性值的更改会导致绑定的 UI 组件进行状态更新。
布局
用特定的组件或者属性来管理用户页面所放置 UI 组件的大小和位置。ArkUI 框架支持多种布局方式,如弹性布局、列表、宫格、栅格布局等。
组件化
装饰器
@Builder:可通过 @Builder 装器进行描述,该装器可以修饰函数,此函数可以在 build() 函数之外声明,并在 build() 函数中或其他 @Builder 修的函数中使用,在一个自定义组件内快速生成多个布局内容。
@Style:声明式范式为了避免开发者对重复样式的设置,通过@Styles 装器可以将多条样式设置提炼成一个方法,直接在组件声明的位置使用。
@Extend:@Extend 装饰器可以将新的属性函数添加到内置组件上,如Text、Column、Button 等,快速扩展原生组件。
动效
1、属性动画:组件的某些通用属性变化时,可以通过属性动画实现渐变效果,比如设置组件size。
2、显式动画:全局 animateTo 显式动画接口,指定由于闭包代码导致的状态变化插入过渡动效,比如组件的显示与隐藏。
3、转场动画:转场动画包括间转场、组件内过渡转场和共享元素转场三种,比如页面跳转。
事件交互
1、UI 组件事件:比如 TextInput 输入框产生的 onEditChange 输入文本变更事件,List 列表组件产生的 onScrollIndex 列表滚动事件。
2、交互事件:点击事件,拖拽事件,焦点事件,触摸事件,按键事件,鼠标事件,手势事件等
绘制能力
混合开发
ArkUI 框架提供了XComponent 组件,支持加载应用动态库、NAPI 跨语言调用,进行 C++绘制能力的开发。
跨平台
鸿蒙生态构建了 ArkUI 跨平台框架的核心设施,将相应的能力扩展到 iOS 和 Android平台上。开发者可以通过一份代码,结合相应的工具链,同时生成多个 OS 平台的应用工程,并可编译出相应的应用程序,在相应的平台运行。
(2)类Web 开发范式:
类 Web 范式的整体接口采用与传统 Web 开发相似的设计理念,采用 HML、CSS与 JS 三种类型的文件进行页面开发,开发者可以基于此范式方便地进行 UI 构建,同时提供数据绑定机制,支持通过 JS 进行数据更新,进而更新UI。
GEEK TALK
02
第一个鸿蒙版Hello World
2.1 开发环境搭建
安装DevEco Studio(最新版为3.3.1)
下载DevEco Studio(https://developer.huawei.com/consumer/cn/deveco-studio/)并安装(如果看不到就继续往下拉...)
安装NodeJS
打开DevEco Studio,如果已经安装NodeJS会自动载入路径
△注意:最新LTS版本为18,但鸿蒙要求v14.I9.1~v17.0.0,官方IDE为v16.19.1。
手动安装NodeJS:
// 安装nvm,nvm可以管理多版本NodeJS。npm install -g n// 安装v16.19.1nvm install v16.19.1// 切换使用指定的版本nodenvm use v16.19.1// 彻底切换版本nvm alias default v16.19.1
安装Ohpm(Open Harmony Pack Manager)鸿蒙包管理系统,安装第三库
安装Harmony SDK
2.2 创建第一个鸿蒙应用
Create Project -> Application -> Empty Ability
Next
Mode:ArkTS的声明式开发范式与类Web 开发范式,官方主推Stage 声明式开发范式。
2.3 项目结构(有一点点像Android Studio!!)
2.3.1 工程目录
AppScope -> app.json5:应用配置信息,包含报名、应用名、版本等
entry为该工程下的Module应用包
entryability -> EntryAbility.ts 为该Module的启动入口
entry -> pages 存放页面文件
entry -> resources 存放资源文件
entry -> module.json5 配置页面信息,可以用与router跳转
entry -> ohosTest 为测试模块
2.4 运行
2.4.1 真机运行
遥遥领先连续点击 "系统版本号" 开启开发者模式。
系统 -> 更新,开启abd调试。
插入数据线,选择 "传输文件"。
证书配置:File > Project Structure > Project > Signing Configs界面,勾选“Automatically generate signature”。
GEEK TALK
03
小试牛刀 - 度加首页
3.1 底部Tab
使用Tabs组件创建底部tabbar,子page分别为CreatePage、ClassPage、MinePage。
import promptAction from '@ohos.promptAction'import { TabbarItem } from '../Models/TabbarItem'import { ClassPage } from './ClassPage'import { CreatePage } from './CreatePage'import { MinePage } from './MinePage'@Entry@Componentstruct Index {// tabbar数据源tabList: TabbarItem[] = [{defaultIcon: $r("app.media.home"),activeIcon: $r("app.media.home_focus"),title:"创作"},{defaultIcon: $r("app.media.class"),activeIcon: $r("app.media.class_focus"),title:"课堂"},{defaultIcon: $r("app.media.my"),activeIcon: $r("app.media.my_focus"),title:"我的"},]// 记录索引,index从0开始@State tabActivityIndex: number = 0// 展示tabbaritem@BuilderTabbarBuilder(item: TabbarItem, index: number) {Column({space: 5}) {Image(this.tabActivityIndex == index ? item.activeIcon : item.defaultIcon).width(24).aspectRatio(1)Text(item.title).fontSize(12).fontColor(this.tabActivityIndex == index ? "#FFFFFF" :"#99FFFFFF")}}build() {Column() {// TabsTabs({index: this.tabActivityIndex}) {ForEach(this.tabList,(item: TabbarItem, index: number) => {TabContent() {if (index == 0) {CreatePage()} else if (index == 1) {ClassPage()} else {MinePage()}}.tabBar(this.TabbarBuilder(item,index))} )}.barPosition(BarPosition.End) // tab放在底部.onChange((index: number) => {this.tabActivityIndex = indexpromptAction.showToast({message:index.toString()})})}.height("100%").width("100%").backgroundColor("#16141F").justifyContent(FlexAlign.Center)}}
3.2 创作page
import { ActivityComponent } from '../Components/ActivityComponent'import { HotComponent } from '../Components/HotComponent'// 创作页面@Componentexport struct CreatePage {// 活动下标@State activitySelectedIndex: number = 0build() {Column() {// 迎新年行Row() {Row(){Text("🎉迎新年,瓜分万元流量券") // 新玩法!AI帮你制作专属贺岁视频🧧.width("178").fontColor("rgba(255,255,255,0.6)").fontSize(14)Image($r("app.media.img_home_link_arrow")).width(16).aspectRatio(1)}.width(194).padding({left: 10})Row(){Image($r("app.media.img_reward_star")).width(16).height(16)Text("签到").width("40").fontColor("rgba(255,255,255,0.6)").fontSize(14).textAlign(TextAlign.Center)}.width(66)}.height("50").width("100%").justifyContent(FlexAlign.SpaceBetween)// 开始创作Row({space: 10}){// 开始创作Stack() {Image($r("app.media.img_home_entrance")).width("100%").height("100%").borderRadius(8) // 圆角Image($r("app.media.jiandao1")).width(30).aspectRatio(1).margin({bottom: 30})Text("开始创作").width(56).height(20).fontColor("rgba(255,255,255,1)").fontSize(14).margin({top: 30})}.height("100%").layoutWeight(1) // 告诉占满剩余.borderRadius(8) // 圆角// 草稿箱Column(){Text("2").fontColor("#fff").fontSize(20).margin({top: 15})Text("草稿箱").fontColor("#fff").fontSize(14).margin({top: 10})}.backgroundColor("#282438").height("100%").width(86).borderRadius(8) // 圆角}.height(86).width("100%").justifyContent(FlexAlign.SpaceBetween).padding({left: 15, right: 15})// AI提词器Row() {Row() {Column() {Image($r("app.media.home_prompter")).width(40).height(28)Text("AI 提词").fontColor("#fff").fontSize(10)}.width(70).height(70).justifyContent(FlexAlign.Center).margin({left: 15})Column() {Image($r("app.media.home_ttv")).width(40).height(28)Text("AI 成片").fontColor("#fff").fontSize(10)}.width(70).height(70).justifyContent(FlexAlign.Center)Column() {Image($r("app.media.home_digital_human")).width(40).height(28)Text("AI 数字人").fontColor("#fff").fontSize(10)}.width(70).height(70).justifyContent(FlexAlign.Center)Column() {Image($r("app.media.home_subtitle")).width(40).height(28)Text("文字提取").fontColor("#fff").fontSize(10)}.width(70).height(70).justifyContent(FlexAlign.Center).margin({right: 15})}.height("100%").width("100%").backgroundColor("#282438").borderRadius(8).justifyContent(FlexAlign.SpaceBetween)}.backgroundColor("#16141F").height(76).width("100%").padding({left: 15, right: 15}).margin({top: 15}).borderRadius(8)// 春节活动Row() {Row() {Image($r("app.media.huodong1")).borderRadius(8)}.height("100%").width("100%").backgroundColor("#282438").justifyContent(FlexAlign.SpaceBetween)}.backgroundColor("#16141F").height(76).width("100%").padding({left: 15, right: 15}).margin({top: 15}).borderRadius(8)// 热点Row() {Row() {Image(this.activitySelectedIndex == 0 ? $r("app.media.img_hot_tab_focus") : $r("app.media.img_hot_tab")).width(45).height(25)}.margin({left: 20}).width(45).height(54).onClick( () => {this.activitySelectedIndex = 0})Text("活动").fontSize(18).fontColor(this.activitySelectedIndex == 0 ? "rgba(255,255,255,0.4)" : "rgba(255,255,255,1)").margin({left: 20}).onClick( () => {this.activitySelectedIndex = 1}).margin({left: 15, top: 2}).height(54)}.margin({top: 5}).height(54)if (this.activitySelectedIndex == 0){HotComponent()} else {ActivityComponent()}}.width("100%").height("100%").alignItems(HorizontalAlign.Start)}}
3.3 热点Component
import { hotContentItemList, hotContentItemList2, HotContentItemModel } from '../Models/HotContentItemModel'import { HotContentComponent } from './HotContentComponent'import { TabItemListComponent, TabItemModel } from './TabItemListComponent'// 热点@Componentexport struct HotComponent {// 定义item的数据源@StatetabItemModelList: TabItemModel[] = [new TabItemModel({title: "全网"}),new TabItemModel({title: "娱乐"}),new TabItemModel({title: "情感"}),new TabItemModel({title: "历史"}),new TabItemModel({title: "健康"}),new TabItemModel({title: "社会"}),new TabItemModel({title: "科技"}),new TabItemModel({title: "春节"})]// 选中的item下标@State selectedIndex: number = 0// 热点内容列表@State hotContentItemList: HotContentItemModel[] = hotContentItemList// 热点内容列表2.为了模拟不同tab下的数据@State hotContentItemList2: HotContentItemModel[] = hotContentItemList2build() {Column() {Text("全网热点 每分钟更新").fontColor("rgba(255,255,255,0.3)").fontSize(12).margin({left: 15, top: 0})// 热点tab列表TabItemListComponent({itemModelList: $tabItemModelList, selectedIndex: $selectedIndex})if (this.selectedIndex == 0) {// 热点内容HotContentComponent({itemList: this.hotContentItemList})} else if (this.selectedIndex == 1) {// 热点内容HotContentComponent({itemList: this.hotContentItemList2})} else {// 热点内容HotContentComponent({itemList: this.hotContentItemList})}}.alignItems(HorizontalAlign.Start).width("100%").layoutWeight(1).padding({bottom: 80})}}
3.4 活动Component
import { createActivityListItemList, CreateActivityListItemModel } from '../Models/CreateActivityListItemModel'// 活动@Componentexport struct ActivityComponent {@State createActivityListItemList: CreateActivityListItemModel[] = createActivityListItemList// 时间戳格式化formatDateTime(timestamp: number) {let dateTime = new Date(timestamp * 1000)const year = dateTime.getFullYear();const month = dateTime.getMonth() + 1 < 10 ? '0' + (dateTime.getMonth() + 1) : dateTime.getMonth() + 1;const date = dateTime.getDate() < 10 ? '0' + dateTime.getDate() : dateTime.getDate();const hours = dateTime.getHours() < 10 ? '0' + dateTime.getHours() : dateTime.getHours();const minutes = dateTime.getMinutes() < 10 ? '0' + dateTime.getMinutes() : dateTime.getMinutes();const seconds = dateTime.getSeconds() < 10 ? '0' + dateTime.getSeconds() : dateTime.getSeconds();return (month + '月' + date + '日');}build() {List() {ForEach(this.createActivityListItemList, (model: CreateActivityListItemModel, index: number) => {ListItem() {Row() {Image(model.pic).width(114).height(64).margin({left: 15}).borderRadius(8)Column() {Text(model.name).fontColor(Color.White).fontWeight(14)Text(this.formatDateTime(model.start_time) + "-" + this.formatDateTime(model.end_time)).fontColor("rgba(255,255,255,0.3)").fontWeight(12).margin({top:10})}.alignItems(HorizontalAlign.Start).margin({left: 15})}}.height(84)})}.width("100%").aspectRatio(1)}}
GEEK TALK
04
基础图谱
鸿蒙基础架构 | ||||
开发环境搭建 | ||||
ArkTS与开发范式 | TS/ArkTS、FA/Stage模型 | |||
| 应用开发 | UIAbility、 Component生命周期 | onCreate、onDestroy、onWindowStageCreate、onWindowStageDestroy、onForeground、onBackground... | ||
装饰器 | @Entry、@Preview、@Component、@State、@Builder、@Prop、@Link... | |||
数据存储&持久化 | AppStorage、LocalStorage、PersistentStorage... | |||
条件判断 | if、for、switch | |||
ArkUI | Column、Row、List、Stack、Scroll、Text、Image、Button、Tabs... | |||
对象 | interface、implements、extends | |||
路由 | router.pushUrl、router.replaceUrl、router.back | |||
权限申请 | abilityAccessCtrl | |||
网络请求 | httpRequest | |||
通知 | notificationManager | |||
手势 | gesture()、onTouch() | |||
动画 | animation()、animateTo()、 sharedTransition() | |||
媒体播放 | AVPlayer | |||
国际化 | configuration.getLocale() | |||
组件化 | SharedLibrary、StaticLibrary | |||
第三方依赖 | ohpm | |||
应用测试 | HarmonyOS Test | |||
HarmonyOS JUnit | ||||
OpenHarmony Test | ||||
应用上架 | AppGallery Connect | |||
END
千万级高性能长连接Go服务架构实践