0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

京东金融APP的鸿蒙之旅系列专题 鸿蒙新特性篇:Hello, 仓颉 World

京东云 来源:京东科技 杨拓 作者:京东科技 杨拓 2024-11-29 10:42 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

作者:京东科技 杨拓

一、仓颉介绍

仓颉是一种通用编程语言,适用于各种应用开发,兼顾开发效率和运行性能,其优势如下:

1、高效编程:语法简明高效,支持多种范式编程。如插值字符串、主构造函数、Flow 表达式、match、if-let、while-let 和重导出等,减少冗余书写,提升开发效率。

2、轻量运行时库:标准库中除 core 包外(libcangjie-std-core < 1MB),其他包都可以按需链接加载。

3、轻量用户线程:仓颉使用用户态线程模型,每个线程都是轻量级的执行实体,拥有独立的执行上下文并且共享内存。创建线程内存开销仅 8KB,切换线程不进入内核态,线程栈支持动态扩缩容,耗时纳秒级。

4、轻量对象:每对象仅额外消耗一个机器字长记录类型信息;不需要计数字段;GC 使用转发表而非转发指针。

5、类型安全:作为静态强类型语言,仓颉通过编译时类型检查识别错误,降低运行时风险,且具备强大的类型推断能力,减少类型标注工作。

二、快速环境配置

1、下载

鸿蒙开发者网站找到下载中心,可以看到DevEco仓颉插件,下载到本地。

最新版下载链接如下:点击此处下载最新版

2、安装

打开DevEco Studio,进入设置,在plugin标签下选择从本地磁盘安装插件,安装后重启IDE。

3、创建

操作与创建ArkTs工程一致。

三、特性介绍

1、Flow 表达式

Flow流表示对输入进行流式处理后得到输出,在仓颉中流操作符有两种:表示数据流向的中缀操作符 |> 和表示函数组合的中缀操作符 ~> 。

1.1、|>

表示对输入数据做一系列的处理,可以使用 |> 来简化。

语法形式就是e1 |> e2,其中 e2 是函数类型的表达式,e1 的类型是 e2 的参数类型的子类型.

示例1:
let gen: Array< Int64 > = []
gen |> forEach{a: Int64 => println("${a}")}

示例2:
func split(words: Array< String >, separator: Rune): Array< String > {
    words |> map { text =>
        text.split(String(separator), removeEmpty: true)
    } |> flatten |> collectArray
}

//示例3: 处理 Unicode 字符串
func split_unicode(text: String, sep: String) {
    let indices = ArrayList< Int64 >()
    text.runes() |> enumerate |> fold(false) { state, e =>
        let current = !sep.contains(e[1].toString())
        if (state != current) { indices.append(e[0]) }
        current
    } |> { valid: Bool => if (valid) { indices.append(text.size) } }

    let runes = text.toRuneArray()
    let result = ArrayList< String >()
    for (i in 0..indices.size:2) {
        result.append(String(runes[indices[i]..indices[i + 1]]))
    }
    return result
}

1.2、~>

当两个单参函数进行组合时可以使用~>。

//示例:
func f(x: Int64): Float64 {
    Float64(x)
}
func g(x: Float64): Float64 {
    x
}
//等价于{ x: Int64 => g(f(x)) },会先对f求值,然后再对g求值,最后才会进行函数的组合
var fg = f ~> g 

建议以上两个流操作符不要用于带默认值的命名参数,因为配置了默认值的命名参数必须给出命名实参才能用

2、变长参数

变长参数是各个语言都有的一种功能,但是在仓颉中当形参最后一个非命名参数是 Array 类型时,就可以作为变长参数使用,不需要特殊的语法形式

func sum(arr: Array< Int64 >) {
    var total = 0
    for (x in arr) {
        total += x
    }
    return total
}

main() {
    println(sum())
    println(sum(1, 2, 3))
}

3、扩展

仓颉扩展提供了对类和接口进行扩展的操作,不需要使用继承或者装饰器模式,就可以给类添加额外的属性和方法。这种场景的优势在于不需要破坏被扩展类型的封装性,就可以添加额外的功能。

可以添加的功能包括:

•添加成员函数

•添加操作符重载函数

•添加成员属性

•实现接口

//示例1:为整型扩展两个属性
extend Int64 {
    public prop r: Index {
        get() { Index.Row(this) }
    }
    public prop c: Index {
        get() { Index.Col(this) }
    }
}
//调用
2.r

//示例2:扩展静态方法
extend Expression {
    static func fromTokens(tokens: List< Token >): Result< Expression, String > {
        match (expressionFunc(tokens).map {t => t[0]}) {
            case Some(e) => Ok(e)
            case None => Err("Invalid Expression!")
        }
    }
}
//调用
Expression.fromTokens

4、if-let和while-let

仓颉中的条件控制和循环控制基本和ArkTs一致,除了将switch换为match外无区别,但是多了if-let和while-let两个特性表达式。

4.1、if-let

if-let 表达式首先会对条件中 <- 右侧的表达式进行求值,如果此值能匹配 <- 左侧的模式,则执行 if 分支,否则执行 else 分支。

main() {
    let result = Option< Int64 >.Some(2023)

    if (let Some(value) < - result) {
        println("操作成功,返回值为:${value}")
    } else {
        println("操作失败")
    }
}
操作成功,返回值为:2023

仓颉没有null判空,所以提供了option(T)来判空,它里面的取值就是some(v)和none两种情况None为空,表示没有赋值。some表示有赋值

4.2、while-let

while-let 表达式同if-let一样,也是先会对条件中 <- 右侧的表达式进行求值,如果此值能匹配 <- 左侧的模式,则执行循环体,然后重复执行此过程。如果模式匹配失败,则结束循环。

  public static func fromJson(r: JsonReader): FunctionCall {
    var temp_name: String = "";
    var temp_arguments: String = "";
    while (let Some(v) < - r.peek()) {
      match(v) {
        case BeginObject = >
          r.startObject();
          while(r.peek() != EndObject) {
              let n = r.readName()
              match (n) {
                  case "name" => temp_name = r.readValue< String >();
                  case "arguments" => temp_arguments = r.readValue< String >();
                  case unkow => println("unkow key ${unkow}");
              }
          }
          r.endObject();
          break;
        case _ => throw Exception("can't deserialize for FunctionCall");
      }
    }
    return FunctionCall(temp_name, temp_arguments);
  }

5、线程

仓颉中创建一个线程非常简单,只需要使用 spawn{} 即可开启一个新的线程,{}里面就是在新线程中执行的代码,并且可以使用Future获取线程执行结果。

import std.sync.*
import std.time.*

//开启一个线程
let future = spawn { task() }

//执行内容
func task(): Int64 {
    for (_ in 0..M) {
        .....
    }
    return n
}

//使用fut.get()可以等待线程执行完成获取线程执行结果
future.get()

5.1、同步-原子操作

仓颉同样也提供了多种同步机制来确保数据的安全,例如原子操作、互斥锁和条件变量等

原子操作方面仓颉提供整数类型、AtomicBool 类型和AtomicReference引用类型的原子操作来保证同步操作。

let num = AtomicInt64(0);
let list = ArrayList< Future< Int64 >>();
func testAtomic() {
    for (i in 0..100) {
        let fut = spawn {
            sleep(Duration.millisecond)
            num.fetchAdd(1)
        }
        list.append(fut)
    }

    for (f in list) {
        f.get()
    }

    let result = num.load()
    AppLog.info("result = ${result}")//输出100
}

5.2、同步-可重入互斥锁 ReentrantMutex

可重入互斥锁可以保证在任意时刻最多只有一个线程执行区块的代码,当一个线程尝试获取被其他线程持有的锁会被阻塞,直到别的线程释放锁才可以执行区块代码。使用ReentrantMutex需要注意以下两点:

1.在访问共享数据之前,必须尝试获取锁。

2.处理完共享数据后,必须进行解锁,以便其他线程可以获得锁。

import std.sync.*
import std.time.*
import std.collection.*

var num: Int64 = 0;
let list = ArrayList< Future< Int64 >>();
let lock = ReentrantMutex()

func task() {
    sleep(Duration.millisecond)
    lock.lock()
    num++
    lock.unlock()
}

func testMutex() {
    let list = ArrayList< Future< Unit >>()

    for (i in 0..100) {
        let fut = spawn {task()}
        list.append(fut)
    }

    for (f in list) {
        f.get()
    }

    AppLog.info("result = ${num}") //输出100
}

在日常使用中需要手动unlock相当不方便,而且也有可能在异常情况下锁无法释放的问题,为了解决这些问题,仓颉又提供一个 synchronized 关键字,搭配 ReentrantMutex 一起使用。

具体使用方式就是在 synchronized 上加一个 ReentrantMutex 对象即可,然后将同步代码写在synchronized{}中。一个线程在进入 synchronized 修饰的代码块之前,会自动获取 ReentrantMutex 实例对应的锁,如果无法获取锁,则当前线程被阻塞;而线程在退出 synchronized 修饰的代码块之前,会自动释放该 ReentrantMutex 实例的锁。

import std.sync.*
import std.time.*
import std.collection.*

var num: Int64 = 0;
let list = ArrayList< Future< Int64 >>();
let lock = ReentrantMutex()

func task() {
    sleep(Duration.millisecond)
    //        lock.lock()
    //跟上面的示例一样,省去了加,释放锁的操作
    synchronized(lock) {
        num++
    }
    //        lock.unlock()
}

func testMutex() {
    let list = ArrayList< Future< Unit >>()

    for (i in 0..100) {
        let fut = spawn {task()}
        list.append(fut)
    }

    for (f in list) {
        f.get()
    }

    AppLog.info("result = ${num}") //输出100
}

四、和ArkTs互操作

现在一般的场景是在已有ArkTs库中使用仓颉,所以可以将仓颉代码封装为ArkTs库,提供给外部使用。

原理就是互操作宏解析被注解修饰的仓颉代码,会自动生成ArkTs声明文件和互操作层代码。

使用步骤:

1.在cj文件中,针对 class、interface 和函数,使用 @Interop[ArkTS] 进行修饰,被修饰的对象是希望被 ArkTS 调用的。

2.在 DevEco Studio 中的仓颉文件或者 module 名称右键选择“Generate Cangjie-ArkTS Interop API”,会在 cangjie 目录下生成 ark_interop_api 的声明文件。

3.ArkTS 侧添加依赖并 import ark_interop_api 即可使用。

仓颉代码:

import ohos.ark_interop.*
import ohos.ark_interop_macro.*

@Interop[ArkTS]
public func sub(a: Int64, b: Int64): Int64 {
    return a - b
}

@Interop[ArkTS]
public class CjDemo {
    public let name: String

    @Interop[ArkTS, Invisible]
    public var id: Float64 = 1.0

    public init(str: String) {
        name = str
    }

    public func add(a: Int64, b: Int64): Int64 {
        return a + b
    }

    public func foo(): Float64 {
        return 1.0
    }
}

生成的代码:

export declare class CjDemo {
    name: string
    add(a: number, b: number): number
    foo(): number
}

export declare interface CustomLib {
    sub(a: number, b: number): number
    CjDemo: {new (str: string): CjDemo}
}

使用:

let cjLib : CustomLib = requireCJLib("libohos_app_cangjie_entry.so") as CustomLib
console.log("result" + cjLib.sub(2, 1))

let class1: CjDemo = new cjLib.CjDemo("arkts call")
console.log("result " + class1.add(5,1))

五、后续规划

鸿蒙应用开发官方目前提供两种编程语言供选择:ArkTs和仓颉。从当前趋势来看,这两种语言是并行发展的,尚不存在某一方被替代的情况,因此对当前的开发工作没有影响,仓颉可以作为技术储备加以学习和掌握。

在开发初期,我们全部使用了ArkTs。然而在实际开发过程中,我们发现了一些痛点:

某些ArkTs的官方API存在性能问题,使得我们在进行性能优化时某些关键点较依赖系统发版。

ArkTs提供了TaskPool和Worker两种线程调用方式,但编写过程较为繁琐,线程间的数据传递存在限制且有性能损耗。

我们计划利用仓颉的优势来解决这些问题,以打造更为健壮的鸿蒙版京东金融应用。

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 编程语言
    +关注

    关注

    10

    文章

    1959

    浏览量

    38918
  • 仓颉
    +关注

    关注

    0

    文章

    16

    浏览量

    209
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    鸿蒙OpenHarmony【标准系统编写“Hello World”程序】 (基于RK3568开发板)

    编写“Hello World”程序 下方将展示如何在单板上运行第一个应用程序,其中包括新建应用程序、编译、烧写、运行等步骤,最终输出“Hello World!”。 前提条件 已参考[创
    的头像 发表于 04-24 17:32 1886次阅读
    <b class='flag-5'>鸿蒙</b>OpenHarmony【标准系统编写“<b class='flag-5'>Hello</b> <b class='flag-5'>World</b>”程序】 (基于RK3568开发板)

    鸿蒙Harmony是如何影响Android工程师的呢?

    宣布“纯血版鸿蒙4.0”后,国内各大领先企业诸如腾讯、网易以及阿里巴巴不仅积极与华为展开深度合作,更为鸿蒙生态体系注入新的活力。华为游戏、支付宝、高德地图、哔哩哔哩、阿里、钉钉等一系列著名 A
    发表于 01-14 22:14

    【HarmonyOS HiSpark Wi-Fi IoT 套件试用连连载】我和鸿蒙的亲密接触----hello_world

    的是官方推荐的linux下编译,window上使用visual code进行烧录。或不多说,马上命令鸿蒙做事,让他跟大伙打个招呼,sys"hello world!"由于代码放在虚拟机中
    发表于 11-13 19:56

    【HarmonyOS HiSpark Wi-Fi IoT 套件试用连载】+打印hello world

    Hi3861第一个程序:打印hello_world通过学习鸿蒙系统的文件架构,大概知道了hi3861芯片的启动过程,对于hi3861外设开发,不需要涉及到内核方面的改动,所以只需要知道hi3861
    发表于 01-06 22:38

    鸿蒙系统和安卓系统下的京东有什么不一样

    京东的排列位置可以看出京东对华为鸿蒙适配的重视程度,而这段时间网上也有人体验到了鸿蒙版的京东APP
    的头像 发表于 01-04 11:23 4344次阅读

    鸿蒙的第一个世界版Hello World

    天生支持国际化,在 Previewer(预览器)设置中选择英文(en-US): Hello World。 恭喜!第一个 Hello World 完成了! 尝试来改几个字吧,打开左侧文
    的头像 发表于 01-21 10:05 2675次阅读
    <b class='flag-5'>鸿蒙</b>的第一个世界版<b class='flag-5'>Hello</b> <b class='flag-5'>World</b>

    鸿蒙系统京东V10.0.2 版本已上架

    随着鸿蒙 2.0 的发布,华为部分手机用户迎来鸿蒙时代,京东作为华为鸿蒙 OS 的合作 APP,首次投入
    的头像 发表于 06-25 16:55 4429次阅读
    <b class='flag-5'>鸿蒙</b>系统<b class='flag-5'>京东</b>V10.0.2 版本已上架

    在现有安卓应用的基础上扩展鸿蒙特性

    鸿蒙混合打包,在现有安卓应用的基础上扩展鸿蒙特性,以最快的速度布局鸿蒙生态! 本项目只是《京东 APP
    的头像 发表于 06-30 09:39 2061次阅读
    在现有安卓应用的基础上扩展<b class='flag-5'>鸿蒙</b>的<b class='flag-5'>特性</b>

    如何在鸿蒙开发板上输出Hello World

    HarmonyOS 开发快速入门:鸿蒙开发板上输出 Hello World
    的头像 发表于 01-15 16:03 2198次阅读

    鸿蒙上点亮LED灯

    上一我们成功的在鸿蒙开发板上输出了 Hello World!这一将带大家点亮 LED 灯。
    的头像 发表于 01-16 10:28 2641次阅读

    京东与华为鸿蒙合作 京东启动鸿蒙原生应用开发

     1月10日,华为和京东宣布正式合作,共同推动鸿蒙原生应用的开发,进一步完善鸿蒙生态系统。
    的头像 发表于 01-11 16:22 1669次阅读

    京东金融APP鸿蒙之旅系列专题特性:意图框架接入

    作者:京东科技 杨拓 一、意图框架服务介绍 HarmonyOS NEXT引入了多项创新特性,其中的意图框架能够将应用中的业务功能智能分发至手机的各大系统入口,其中系统入口包括小艺对话、小艺搜索和小艺
    的头像 发表于 10-14 17:10 1349次阅读
    <b class='flag-5'>京东</b><b class='flag-5'>金融</b><b class='flag-5'>APP</b>的<b class='flag-5'>鸿蒙</b><b class='flag-5'>之旅</b><b class='flag-5'>系列</b><b class='flag-5'>专题</b> 新<b class='flag-5'>特性</b><b class='flag-5'>篇</b>:意图框架接入

    京东金融APP鸿蒙之旅系列专题 鸿蒙工程化:Hvigor构建技术

    作者:京东科技 杨拓 一、意图框架服务介绍 HarmonyOS NEXT引入了多项创新特性,其中的意图框架能够将应用中的业务功能智能分发至手机的各大系统入口,其中系统入口包括小艺对话、小艺搜索和小艺
    的头像 发表于 10-14 17:11 1510次阅读
    <b class='flag-5'>京东</b><b class='flag-5'>金融</b><b class='flag-5'>APP</b>的<b class='flag-5'>鸿蒙</b><b class='flag-5'>之旅</b><b class='flag-5'>系列</b><b class='flag-5'>专题</b> <b class='flag-5'>鸿蒙</b>工程化:Hvigor构建技术

    Taro鸿蒙技术内幕系列(一):如何将React代码跑在ArkUI上

    基于 Taro 打造的京东鸿蒙 APP 已跟随鸿蒙 Next 系统公测,本系列文章将深入解析 Taro 如何实现使用 React 开发高性能
    的头像 发表于 10-25 17:24 997次阅读
    Taro<b class='flag-5'>鸿蒙</b>技术内幕<b class='flag-5'>系列</b>(一):如何将React代码跑在ArkUI上

    【HarmonyOS 5】金融应用开发鸿蒙组件实践

    【HarmonyOS 5】金融应用开发鸿蒙组件实践 ##鸿蒙开发能力 ##HarmonyOS SDK应用服务##鸿蒙金融类应用 (
    的头像 发表于 07-11 18:20 752次阅读
    【HarmonyOS 5】<b class='flag-5'>金融</b>应用开发<b class='flag-5'>鸿蒙</b>组件实践