鸿蒙即将抛弃Android,你还不来学习一下?

2024-03-05 02:52

本文主要是介绍鸿蒙即将抛弃Android,你还不来学习一下?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

最近移动开发圈子里,鸿蒙可谓出尽了风头,先是宣布即将正式发布的 Harmony OS Next 将完全剥离 Android 代码,也就是不再兼容 Android,化身为纯血的鸿蒙,紧接着又启动了鸿蒙生态千帆启航,伴随着的是众多大厂已经启动原生鸿蒙适配,包括支付宝、京东、美团等等。

作为一个整天被内卷的客户端开发,不得不加入了。

本文基于 Harmony OS Api 9,如果文章内容与新版本有不一致的地方,一切以新版本为准,望见谅。

熟悉概念

在正式开始前,我们先了解一下鸿蒙开发相关的概念,打开鸿蒙官网,首先看到的就是这几个套件

下面我们来简单认识下

  • DevEco Studio

    面向 HarmonyOS 应用及元服务开发者提供的集成开发环境(IDE), 助力高效开发。

    也就是鸿蒙开发的 IDE,类似 Android Studio 和 Xcode。

  • ArkTS

    ArkTS 是鸿蒙生态的应用开发语言。它在保持 TypeScript (简称TS)基本语法风格的基础上,对 TS 的动态类型特性施加更严格的约束,引入静态类型。同时,提供了声明式 UI、状态管理等相应的能力,让开发者可以以更简洁、更自然的方式开发高性能应用。

    TypeScript 曾经风靡一时,弥补了 JavaScript 不支持强类型的缺点,作为一名 Java 开发者,更习惯强类型语言。至于 ArkTS 和 TypeScript 的区别可以暂时不用关心,把它当做标准的 TS 用就行。

  • ArkUI

    ArkUI 是一套构建分布式应用界面的声明式 UI 开发框架。它使用极简的 UI 信息语法、丰富的 UI 组件、以及实时界面预览工具,帮助您提升 HarmonyOS 应用界面开发效率30%。您只需使用一套 ArkTS API,就能在多个 HarmonyOS 设备上提供生动而流畅的用户界面体验。

    可以理解为用 TS 实现的一套 UI 组件库,包括常用的组件和布局等,类似于 Google 的 material 组件库和 Swift UI。

  • ArkCompiler

    ArkCompiler 是华为自研的统一编程平台,包含编译器、工具链、运行时等关键部件,支持高级语言在多种芯片平台的编译与运行,并支撑应用和服务运行在手机、个人电脑、平板、电视、汽车和智能穿戴等多种设备上的需求。

    用于将 TS 代码编译为鸿蒙系统可执行指令,俗称编译打包工具。

初识鸿蒙

准备IDE

按照官方教程,我们需要下载鸿蒙开发 IDE,也就是 DevEco Studio,和 AndroidStudio 一样,都是基于 IntelliJ IDEA 二次开发,上手没有难度。

首次启动,需要安装 Node.js、ohpm、Harmony OS SDK 等依赖,根据引导一直下一步即可。

完整依赖如下

可能有些同学还不太熟悉这些概念,我们简单介绍下

  • Node.js

    Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,我们从上面的概念中了解到,鸿蒙使用 TS 作为开发语言,而 TS 实际上就是 JS,因此需要 JS 运行环境也很正常。

  • ohpm

    大家应该都听说过 npm,是一个前端比较常用的包管理工具,ohpm 全称应该是 Open Harmony Package Manager,也就是鸿蒙上使用的包管理工具,类似于 Java 上的 Maven 仓库。

  • Harmony OS SDK

    这个应该很好理解,开发鸿蒙需要的软件开发工具包,相当于 Android 上的 Android SDK。

有一点需要注意,IDE 仅支持特定版本的 Node.js,我本地由于安装了高版本的 Node.js,因此无法继续下一步,建议大家先卸载本地安装的 Node.js,通过 IDE 重新安装支持的版本,因为我自己安装的低版本也会报错🤩

创建项目

IDE 配置完成后,我们新建一个项目,IDE 默认提供了很多模板,值得一提的是,把光标放上去还会提示你支持哪些设备

这里出现了 Ability 的概念,看起来和 Android 中的 Activity 很像,也就是一个页面,我们暂且认为它就是一个页面,后面我们再详细介绍。

除了空页面之外,还支持关于、分类、网格、列表、登录、闪屏等模板,还支持创建 C++ 原生项目。

支持的完整模板如下

我们选择 Empty Ability 下一步

接下来到了项目配置页面,支持的配置如下

  • Project name: 项目名

  • Bundle name: 项目唯一标志,类似于 Android 的 Package name

  • Save location: 保存位置

  • Compile SDK: 编译使用的 SDK 版本

  • Model: 应用模型,有 StageFA 可选,官网推荐使用 Stage,我们就先用这个

  • Enable Super Visual: 官方的解释是启用低代码开发,先不管它

  • Language: 开发语言,Stage 模型只能选 ArkTS,FA 模型还可以选 JS

  • Compatible SDK: 字面意思是兼容的的 SDK 版本,暂时不清楚是最低支持版本(minSdk)还是适配的版本(targetSdk),看起来更像是前者,如果是这样的话鸿蒙走的应该是类似苹果的路线,即新版本出来后开发者必须要适配,否则高版本可能用不了

  • Device type: 支持的设备类型

点击 Finish 即可完成创建

项目结构

创建完的项目是这样的

完整目录如下

├── AppScope // app 默认配置
│  ├── resources // 资源
│  │  └── base
│  │      ├── element // 文案、颜色等资源
│  │      │  └── string.json
│  │      └── media // 图片资源
│  │          └── app_icon.png
│  └── app.json5 // app 配置,包括 名称、图标、bundleName、版本号等
├── entry // entry 文件夹,相当于 Android 项目中的 app module
│  ├── src
│  │  ├── main
│  │  │  ├── ets // 源代码,相当于 Android 项目中的 java 目录
│  │  │  │  ├── entryability
│  │  │  │  │  └── EntryAbility.ts // entry 中的页面,一个 entry 可以有多个 Ability
│  │  │  │  └── pages
│  │  │  │      └── Index.ets
│  │  │  ├── resources
│  │  │  │  ├── base // 和语言无关的通用资源
│  │  │  │  │  ├── element // 文案、颜色等资源
│  │  │  │  │  │  ├── color.json
│  │  │  │  │  │  └── string.json
│  │  │  │  │  ├── media // 图片资源
│  │  │  │  │  │  └── icon.png
│  │  │  │  │  └── profile
│  │  │  │  │      └── main_pages.json // entry 中包含的 pages 路径,相当于前端项目中 app.json 中的 pages
│  │  │  │  ├── en_US // 英文下使用的资源,可以用来配置多语言
│  │  │  │  ├── rawfile // 应该是存放二进制文件的目录,相当于 Android 项目中的 res/raw 目录,暂时还没用到
│  │  │  │  └── zh_CN // 同 en_US
│  │  │  └── module.json5 // entry 内部配置文件,包括名称、包含的 Abilities、pages,默认 Ability 等
│  │  └── ohosTest // 测试代码
│  ├── build-profile.json5 // entry 构建配置,包括应用模型、支持的系统类型等
│  ├── hvigorfile.ts // 暂时不清楚
│  └── oh-package.json5 // entry 对外配置文件,包括名称、版本、许可证、依赖项等
├── hvigor // 暂时不清楚,看着像是 Android 项目中的 grade 目录
│  ├── hvigor-config.json5
│  └── hvigor-wrapper.js
├── oh_modules // 项目依赖的三方库,相当于前端项目中的 node_modules
├── build-profile.json5 // app 构建配置,包括 app 签名、编译 SDK 版本、兼容 SDK 版本,以及包含的 entry 列表
├── hvigorfile.ts // 暂时不清楚
├── hvigorw // 暂时不清楚
├── hvigorw.bat
├── local.properties // 本地配置
├── oh-package.json5 // 项目配置,包括名称、版本、许可证、依赖项等
└── oh-package-lock.json5

鸿蒙中支持多种 Module,常用的有以下两种:

  • Entry

    相当于 Android 中的 Application Module,每个 Entry 都可以独立运行。

  • Library

    相当于 Android 中的 Library Module,仅可被依赖,无法独立运行。

Stage 模型

Stage模型概念图

  • UIAbility组件

    UIAbility 组件是一种包含 UI 界面的应用组件,主要用于和用户交互。UIAbility 组件是系统调度的基本单元,为应用提供绘制界面的窗口;一个 UIAbility 组件中可以通过多个页面来实现一个功能模块。每一个 UIAbility 组件实例,都对应于一个最近任务列表中的任务。

  • WindowStage

    每个 UIAbility 类实例都会与一个 WindowStage 类实例绑定,该类提供了应用进程内窗口管理器的作用。它包含一个主窗口。也就是说 UIAbility 通过 WindowStage 持有了一个窗口,该窗口为 ArkUI 提供了绘制区域。

  • Context

    Context 是应用中对象的上下文,其提供了应用的一些基础信息,例如 resourceManager(资源管理)、applicationInfo(当前应用信息)、dir(应用开发路径)、area(文件分区)等,以及应用的一些基本方法,例如 createBundleContext()、getApplicationContext()等。UIAbility 组件和各种 ExtensionAbility 派生类组件都有各自不同的 Context 类。分别有基类Context、ApplicationContext、AbilityStageContext、UIAbilityContext、ExtensionContext、ServiceExtensionContext等Context。

  • Page

    Ability 是一个窗口,不包含内容,而 Page 则是真正显示内容的载体,Page 需要依附于 Ability 才能显示。

上手

熟悉了上面的这些概念,我们就可以尝试开发一个鸿蒙上的 App 了。

之前学习 Compose 的时候做了一个「玩 Android」App,为了方便使用现成接口和 UI,我们来做一个鸿蒙版的「玩 Android」,顺便对比下鸿蒙和 Compose 在 UI 层的差异。

UI开发

和 Compose、SwiftUI 类似,鸿蒙的 ArkUI 也采用声明式开发范式,这也是现代UI开发的共识,相比命令式UI,声明式UI更加简洁高效。

ArkUI 提供了官方组件和布局,基本能够满足常见 UI 界面的开发

组件的详细使用这里不再赘述,我们以文章 Item 布局作为示例,来对比下鸿蒙和 Compose 的实现方式

先看下展示效果

代码对比

可以看出,语法的相似度非常高,以至于我后来直接把 Compose 的代码复制来过改一下就能用了🤣

简单介绍下差异

  • 鸿蒙使用 struct 定义组件,而 Compose 使用方法

  • 鸿蒙仅支持在 ArkUI 中使用除组件外的特定操作符,包括 if-else、ForEach 等,而 Compose 比较自由,可以在 UI 方法中使用任意代码

  • 鸿蒙通过组件对象提供的方法设置属性,如 Text("").fontSize(10),而 Compose 则是在构造函数中设置,如 Text(text = "", fontSize = 10.sp)

由于鸿蒙和 Compose 都是声明式 UI,因此状态更新也比较相似,都是状态驱动 UI 更新,此外,鸿蒙和 Compose 都支持通过 Diff 算法完成差量更新。

页面路由

在项目结构部分提到,鸿蒙上 UI 界面的单位包括 Ability 和 page。

一般来说一个功能只需要一个 Ability 呢 就够了,那什么时候需要多个 Ability 呢?

举个🌰,如果你当前开发的是通讯录应用,当其他应用需要选择联系人时,需要打开通讯录的选人页面,而打开外部应用页面的最小单元为 Ability,因为 page 无法独立显示,这时你就需要创建多个 Ability。

Ability 跳转

  1. 在 EntryAbility 中,通过调用 startAbility() 方法启动 UIAbility,want 为 UIAbility 实例启动的入口参数,其中 bundleName 为待启动应用的 Bundle 名称,abilityName 为待启动的 UIAbility 名称,moduleName 在待启动的 UIAbility 属于不同的 Module 时添加,parameters 为自定义信息参数。
let wantInfo = {deviceId: '', // deviceId为空表示本设备bundleName: 'com.example.myapplication',abilityName: 'FuncAbility',moduleName: 'module1', // moduleName非必选parameters: { // 自定义信息info: '来自EntryAbility Index页面',},
}
// context为调用方UIAbility的AbilityContext
this.context.startAbility(wantInfo).then(() => {// ...
}).catch((err) => {// ...
})
  1. 在 FuncAbility 的生命周期回调文件中接收 EntryAbility 传递过来的参数。
import UIAbility from '@ohos.app.ability.UIAbility';
import Window from '@ohos.window';export default class FuncAbility extends UIAbility {onCreate(want, launchParam) {// 接收调用方UIAbility传过来的参数let funcAbilityWant = want;let info = funcAbilityWant?.parameters?.info;// ...}
}
  1. 如需要停止当前UIAbility实例,通过调用 terminateSelf() 方法实现。
// context为需要停止的UIAbility实例的AbilityContext
this.context.terminateSelf((err) => {// ...
});

同时,鸿蒙也提供了 startAbilityForResult 来实现启动 Ability 并获取返回结果,详细使用可以看官方文档。

可以看出,Ability 和 Android 中的 Activity 不仅在形态上相似,在使用上也基本一致,唯一不同的是,Activity 可以直接显示内容,而 Ability 则是一个容器,需要依赖 page 显示内容。

page 跳转

鸿蒙提供了 Router 模块,通过 url 地址完成 page 之间的路由。

Router 模块提供了两种跳转模式,分别是 router.pushUrl()router.replaceUrl(),这两种模式决定了目标页是否会替换当前页。

  • router.pushUrl():目标页不会替换当前页,而是压入页面栈。这样可以保留当前页的状态,并且可以通过返回键或者调用 router.back() 方法返回到当前页。

  • router.replaceUrl():目标页会替换当前页,并销毁当前页。这样可以释放当前页的资源,并且无法返回到当前页。

同时,Router 模块提供了两种实例模式,分别是 StandardSingle。这两种模式决定了目标 url 是否会对应多个实例。

  • Standard:标准实例模式,也是默认情况下的实例模式。每次调用该方法都会新建一个目标页,并压入栈顶。

  • Single:单实例模式。即如果目标页的url在页面栈中已经存在同 url 页面,则离栈顶最近的同 url 页面会被移动到栈顶,并重新加载;如果目标页的 url 在页面栈中不存在同 url 页面,则按照标准模式跳转。

function onJumpClick(): void {router.pushUrl({url: 'pages/Detail', // 目标urlparams: {id: 123} // 添加params属性,传递自定义参数}, (err) => {if (err) {console.error(`Invoke pushUrl failed, code is ${err.code}, message is ${err.message}`);return;}console.info('Invoke pushUrl succeeded.');})
}

在目标页中,可以通过调用 Router 模块的 getParams() 方法来获取传递过来的参数。

在目标页中,可以通过调用 Router 模块的 getParams() 方法来获取传递过来的参数。

const params = router.getParams(); // 获取传递过来的参数对象
const id = params['id']; // 获取id属性的值

page 的设计思路和前端比较接近,相比 Android 的 Activity 更加轻量,相比 Fragment 使用起来更加简单。

和 Fragment 不同的是,page 是全屏展示,而 Fragment 则比较自由,展示大小可以自定义。

其实很多年前 Android 上就有单 Activity 多 Fragment 的架构设计,最近 Google 也推出了 Jetpack Navigation 库,专门用来做 Fragment 路由,然而用起来并不友好,网上吐槽不少。

单从页面架构设计来看,鸿蒙确实遥遥领先~

网络请求

在互联网时代,网络请求是 App 中最常用的功能之一。

鸿蒙中的网络开发比较简单,官方提供了 Http 模块,可以快速发送网络请求,为了减少重复代码,我们可以封装一个统一的请求方法

async function requestSync<T>(path: string, method: http.RequestMethod, extraData?: Object): Promise<Response<T>> {return new Promise<Response<T>>((resolve, reject) => {let url = BASE_URL + path;let uri = parseUri(url);let header = {};if (method === http.RequestMethod.POST) {header["Content-Type"] = "application/x-www-form-urlencoded";if (!extraData) {// POST 必须有请求体,否则会报 Parameter errorextraData = {};}}let httpRequest = http.createHttp();hilog.info(0, TAG, `start request, path: ${path}, method: ${method}, extraData: ` + JSON.stringify(extraData));httpRequest.request(url,{method: method,expectDataType: http.HttpDataType.OBJECT,header: header,extraData: extraData},(err, data) => {let res = new Response<T>()if (!err && data.responseCode === 200) {Object.assign(res, data.result)hilog.info(0, TAG, `request success, path: ${path}, result: ${JSON.stringify(res)}`)} else {hilog.error(0, TAG, `request error, path: ${path}, error: ${JSON.stringify(err)}`)res.errorCode = data?.responseCode??-1res.errorMsg = err?.message??""}resolve(res);})})
}

同时提供统一的返回结构 Response

export class Response<T> {errorCode: number = 0errorMsg: string = ""data: T = nullisSuccess(): boolean {return this.errorCode === 0}isSuccessWithData(): boolean {return this.errorCode === 0 && !!this.data}
}

当需要发请求的时候,一行代码就能搞定

async getHomeArticleList(page: number): Promise<Response<ArticleList>> {return requestSync(`/article/list/${page}/json`, http.RequestMethod.GET);
}

得益于 TS 中的 Promise,我们可以将繁琐的异步回调转为同步调用,和 Kotlin 中的协程有异曲同工之妙,强烈推荐!

持久化

App 的状态保存,免不了要使用持久化数据,持久化主要分为 KV 键值对、SQL 数据库,而鸿蒙上 KV 键值对又分为 Preference 和 KVStore。

  • 用户首选项(Preferences):通常用于保存应用的配置信息。数据通过文本的形式保存在设备中,应用使用过程中会将文本中的数据全量加载到内存中,所以访问速度快、效率高,但不适合需要存储大量数据的场景。

  • 键值型数据库(KV-Store):一种非关系型数据库,其数据以“键值”对的形式进行组织、索引和存储,其中“键”作为唯一标识符。适合很少数据关系和业务关系的业务数据存储,同时因其在分布式场景中降低了解决数据库版本兼容问题的复杂度,和数据同步过程中冲突解决的复杂度而被广泛使用。相比于关系型数据库,更容易做到跨设备跨版本兼容。

  • 关系型数据库(RelationalStore):一种关系型数据库,以行和列的形式存储数据,广泛用于应用中的关系型数据的处理,包括一系列的增、删、改、查等接口,开发者也可以运行自己定义的SQL语句来满足复杂业务场景的需要。

Preferences

用户首选项为应用提供Key-Value键值型的数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。

设计上同样是内存 + 磁盘缓存,首次使用时会将全量数据读取到内存中,因此不是和存储大量数据。

接口如下

用法基本和 Android 中的 SharedPreference 一致,鸿蒙还提供了 on 和 off 来订阅数据变更,遥遥领先~

不过,还是要吐槽一下,目前 DevEcoStudio 3.1.1 正式版每次运行会清楚 App 数据,导致持久化保存的数据全部被清空,希望新版本能优化下。

KV-Store

相比 Preferences,KV-Store 在数据大小上没有太多限制,猜测底层实现应该和 MMKV 一样,通过共享内存完成磁盘同步,因此性能要高不少。

接口如下

鸿蒙直接把 Android 上三方库的功能给内置了,再次领先~

数据库

当我们需要存储复杂数据结构时,就需要用到数据库了,鸿蒙也提供了便捷操作数据库的接口

这么方便的操作数据库,在没有 Room、GreenDAO 等开源库之前,简直不敢想,Android 原生的数据库操作,简直是噩梦😭 鸿蒙仍然领先~

多线程

在移动应用中,免不了要处理各种后台任务,比如接口请求、数据存储、埋点上报等等,因此多线程的性能,直接影响了应用的流畅度。

鸿蒙提供了 TaskPoolWorker 两种并发能力

看到第一行,线程间内存不共享的时候,我震惊了,操作系统课堂上老师不是说线程共享进程的资源吗?难道物理学不存在了?

内存不共享的好处是不用担心对象被并发修改,但是坏处是线程间通信必须复制对象,对于多线程同步大量数据的场景,性能应该会有不小的影响。网上搜了下,也没找到鸿蒙这么设计的原因。

既然如此,我们先看下怎么使用吧

TaskPool

TaskPool 支持在主线程封装任务抛给任务队列,系统选择合适的工作线程,进行任务的分发及执行,再将结果返回给主线程,支持任务的执行、取消,工作线程数量上限为 4。

使用

import taskpool from '@ohos.taskpool';@Concurrent
function imageProcessing(dataSlice: ArrayBuffer) {// 步骤1: 具体的图像处理操作及其他耗时操作return dataSlice;
}function histogramStatistic(pixelBuffer: ArrayBuffer) {// 步骤2: 分成三段并发调度let number = pixelBuffer.byteLength / 3;let buffer1 = pixelBuffer.slice(0, number);let buffer2 = pixelBuffer.slice(number, number * 2);let buffer3 = pixelBuffer.slice(number * 2);let task1 = new taskpool.Task(imageProcessing, buffer1);let task2 = new taskpool.Task(imageProcessing, buffer2);let task3 = new taskpool.Task(imageProcessing, buffer3);taskpool.execute(task1).then((ret: ArrayBuffer[]) => {// 步骤3: 结果处理});taskpool.execute(task2).then((ret: ArrayBuffer[]) => {// 步骤3: 结果处理});taskpool.execute(task3).then((ret: ArrayBuffer[]) => {// 步骤3: 结果处理});
}

和 Java 中的 ThreadPool 比较类似,通过任务队列 + 线程池实现多任务并发,相比 ThreadPool,TaskPool 的接口比较简单,内部自动分派线程,不支持配置线程类型和数量,此外,TaskPool 的最大线程数为4,感觉放在现在动辄 8 核的处理器上,好像差了点意思,似乎不能完全发挥处理器性能?

Worker

创建 Worker 的线程称为宿主线程(不一定是主线程,工作线程也支持创建 Worker 子线程),Worker 自身的线程称为 Worker 子线程(或 Actor 线程、工作线程)。每个 Worker 子线程与宿主线程拥有独立的实例,包含基础设施、对象、代码段等。Worker 子线程和宿主线程之间的通信是基于消息传递的,Worker 通过序列化机制与宿主线程之间相互通信,完成命令及数据交互。

使用

  1. 在主线程中通过调用 ThreadWorker 的 constructor() 方法创建 Worker 对象,当前线程为宿主线程。
import worker from '@ohos.worker';const workerInstance = new worker.ThreadWorker('entry/ets/workers/MyWorker.ts');
  1. 在宿主线程中通过调用 onmessage() 方法接收 Worker 线程发送过来的消息,并通过调用 postMessage() 方法向 Worker 线程发送消息。

例如向 Worker 线程发送训练和预测的消息,同时接收 Worker 线程发送回来的消息。

// 接收Worker子线程的结果
workerInstance.onmessage = function(e) {// data:Worker线程发送的信息let data = e.data;console.info('MyWorker.ts onmessage');
}workerInstance.onerror = function (d) {// 接收Worker子线程的错误信息
}// 向Worker子线程发送训练消息
workerInstance.postMessage({ 'type': 0 });
// 向Worker子线程发送预测消息
workerInstance.postMessage({ 'type': 1, 'value': [90, 5] });
  1. 在 Worker 线程中通过调用 onmessage() 方法接收宿主线程发送的消息内容,并通过调用 postMessage() 方法向宿主线程发送消息。

例如在Worker线程中定义预测模型及其训练过程,同时与主线程进行信息交互。

import worker, { ThreadWorkerGlobalScope, MessageEvents, ErrorEvent } from '@ohos.worker';let workerPort: ThreadWorkerGlobalScope = worker.workerPort;// 定义训练模型及结果 
let result;// 定义预测函数
function predict(x) {return result[x];
}// 定义优化器训练过程
function optimize() {result = {};
}// Worker线程的onmessage逻辑
workerPort.onmessage = function (e: MessageEvents) {let data = e.data// 根据传输的数据的type选择进行操作switch (data.type) {case 0:// 进行训练optimize();// 训练之后发送主线程训练成功的消息workerPort.postMessage({ type: 'message', value: 'train success.' });break;case 1:// 执行预测const output = predict(data.value);// 发送主线程预测的结果workerPort.postMessage({ type: 'predict', value: output });break;default:workerPort.postMessage({ type: 'message', value: 'send message is invalid' });break;}
}

此外,Worker 的创建和销毁耗费性能,需要自行管理已创建的 Worker 并重复使用。Worker 空闲时也会一直运行,因此当不需要 Worker 时,需要调用 terminate() 接口或 parentPort.close() 方法主动销毁 Worker。Worker 存在数量限制,支持最多同时存在 8 个 Worker。

可以看出,Worker 更接近 Java 中使用 new Thread() 原生创建线程的方式,不同的是 Worker 一旦创建,就会一直运行,占用 CPU 资源,只能手动停止,而 Java 中的线程则更加“智能”,一旦线程中的逻辑执行完,即自动进入 TERMINATED 状态,释放 CPU 资源,并自动销毁。

因此,推荐大家使用 TaskPool。

看起来鸿蒙上使用多线程不太领先,存在各种限制,比如线程间内存不共享,线程数量存在上限。

开发体验

在完整开发完一个 App 后,简单总结下鸿蒙原生开发的优缺点。

优点

  • 原生支持声明式 UI

    虽然目前 Compose 和 SwiftUI 也支持声明式,但推出这么久之后,真正在线上大规模使用的产品寥寥无几,顶多在新业务上尝试使用,所以鸿蒙在 UI 上有天然优势。

  • 系统 API 封装性高

    以发送网络请求为例,在 Android 上如果用原生的方式,各种配置用起来头大,而鸿蒙则是通过一个方法即可发起请求,并将常用配置都作为参数开放出来,其他 API 也是类似,鸿蒙的系统 API 用起来更加便捷。

  • TS 学习成本低

    对于有前端开发经验的同学来说,几乎是0成本,即使没用过 TS/JS,上手也非常简单。

  • Android 同学上手快

    首先 IDE 使用基本一致,其次系统架构和 API 定义也非常相似,可以说是一一对应,如果你刚好又用过 Compose 或 Flutter,那 ArkUI 也不成问题。

缺点

  • 系统不开源

    在使用系统 API 的时候,有时想看下内部实现,习惯性的点击方法查看源码,发现看不了,如果遇到系统 API 有问题,就很难定位了。

  • 配套工具还未完善

    在开发中遇到一些bug,比如模拟器上 WebView 无法滚动,而真机正常,还有一些体验不太好的地方,比如每次重新运行会先卸载再安装,导致重新运行后数据丢失,在社区咨询后得知后期都会修复。

  • 社区不够成熟

    目前比较活跃的社区只有官方的开发者社区,但是目前看下来干活不多,大多是来咨询问题或者反馈bug的,而且很多反馈没人回应,可能是问题太多了忙不过来。

  • 开源框架较少

    官方维护了一个 开源库列表 ,刚接触鸿蒙时,里面的开源库还寥寥无几,没想到几个月后的现在已经非常丰富了,包括和 OKHttp 能力基本一致的网络库 httpclient ,但是稳定性还需要大家去验证。

深入了解

在开发的过程中,我一直在想一个问题,鸿蒙使用 TS 作为开发语言,那么运行时到底是通过 JS Runtime 还是其他方式呢?如果是 JS Runtime,那和 WebApp 有什么区别呢?如果是通过转译的方式,那虚拟机最终执行的代码是什么形式的?

这些问题可能只有完全了解了 ArkCompiler 才能回答,不过目前官网对 ArkCompiler 的介绍仅局限于短短一页,我们只能尝试分析打包产物,看能否看出一些端倪。

先解包 hap 看下目录结构

!

resources 和 module.json、pack.info 是资源和配置信息,主要看 ets 目录

先看 modules.abc(857KB)

abc 的全称是 Ark byte code,即方舟字节码,暂时不清楚这里保存了什么信息,以及它的运行原理。

接着看 sourceMaps.map(828KB) 和 symbolMap.map(287KB)

可以看出这两个文件保存了所有 TS 文件的原始信息和 mappings 映射,mappings 是不是很眼熟?

没错,这里可能是将 TS 源码进行了混淆,在安装或运行时,虚拟机再通过某种方式获取到真正要执行的 TS 代码。官网上的介绍也能佐证

到这里,我们对鸿蒙运行时有了一个大致的轮廓:虚拟机执行的仍然是原始 TS 代码,只是在编译时被加密了,以此保证源码安全。

我大胆猜测,鸿蒙虚拟机内置了 JS Runtime,用来解释执行 TS 代码,至于 UI 渲染,则是 ArkUI 将 TS 代码映射为相应的 UI 控件,并进行布局和渲染。

More? ArkUI-X

你以为鸿蒙只是一个独立于 Android 和 iOS 的移动操作系统吗?鸿蒙的野心不止于此!

要知道,鸿蒙剥离 Android 之后,需要各个应用开发商重新基于鸿蒙开发一次应用,这个成本绝对不可忽视,尤其对于头部大型 App 来说,业务极其复杂,开发成本也非常大。

因此,除了积极和开发商沟通外,鸿蒙也在考虑如何降低开发成本,当大家还在考虑能否将 Android/iOS 代码转为鸿蒙时,华为悄悄的发布了 ArkUI-X

简单来说,使用 ArkUI 完成鸿蒙应用开发后,可以快速构建 Android 和 iOS 平台的应用,相当于鸿蒙版的 Flutter。

总结

本文主要介绍了鸿蒙相关的概念,以及如何上手开发一个鸿蒙原生应用,通过开发一个鸿蒙版的「玩 Android」,带领大家熟悉 ArkUI 和常用 API 的使用,基于开发体验总结了现阶段鸿蒙开发的优势和存在的问题,通过对 hap 包的简单分析了解了鸿蒙运行时的大致轮廓,最后介绍了鸿蒙上的跨平台开发框架 ArkUI-X,希望读完本文对大家有帮助。

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

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

入门必看:https://qr21.cn/FV7h05
1.  应用开发导读(ArkTS)
2.  ……

HarmonyOS 概念:https://qr21.cn/FV7h05

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全

如何快速入门: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

这篇关于鸿蒙即将抛弃Android,你还不来学习一下?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/775106

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学