仓颉编程语言:仓颉-ArkTS 互操作

鸿蒙HarmonyOS应用开发 | 鸿蒙开发技术中的仓颉与ArkTS的互操作机制及实现 / 定义仓颉导出函数,供 ArkTS 调用// 从 JSCallInfo 中获取参数// 转换为仓颉类型// 计算结果// 转换并返回 JSValue在该示例中,addNumber函数接受两个数字参数,并返回它们的和。JSContext提供了与 ArkTS 的交互环境,JSCallInfo则包含 ArkTS 传递的参数。 阅读详情

在 OpenHarmony 系统上,ArkTS 具备完整广泛的生态,为复用 ArkTS 生态,仓颉支持与 ArkTS 高效跨语言互通。

仓颉-ArkTS 互操作基于仓颉 CFFI 能力,通过调用 ArkTS 运行时接口,为用户提供库级别的 ArkTS 互操作能力。

使用场景:

  1. 在 ArkTS 应用开发仓颉模块:把用户仓颉代码封装成为 ArkTS 模块,能够被 ArkTS 代码加载和调用。
  2. 在仓颉应用里使用 ArkTS 存量库:在仓颉代码里创建新的 ArkTS 运行时,并加载和执行 ArkTS 的字节码。

互操作库的主要组成和功能:

  1. JSValue: 统一的 ArkTS 数据类型,在跨语言调用中做传参,对 ArkTS 类型做判断和做数据转换。
  2. JSContext: 一个 ArkTS 互操作上下文,用户创建 ArkTS 数据,辅助把 JSValue 转换为仓颉数据。
  3. JSCallInfo: 一次 ArkTS 函数调用的参数集合,包含所有的入参和 this 指针。
  4. JSRuntime: 一个由仓颉创建的 ArkTS 运行时。

在 ArkTS 应用里开发仓颉模块

开发仓颉互操作模块:

  1. 【仓颉侧】导入互操作库。

    import ohos.ark_interop.*
  2. 【仓颉侧】定义要导出的函数,可被 ArkTS 调用的仓颉函数的类型是固定的:(JSContext, JSCallInfo)->JSValue。

    func addNumber(context: JSContext, callInfo: JSCallInfo): JSValue {
        // 从 JSCallInfo 获取参数列表
        let arg0: JSValue = callInfo[0]
        let arg1: JSValue = callInfo[1]
    
        // 把 JSValue 转换为仓颉类型
        let a: Float64 = arg0.toNumber()
        let b: Float64 = arg1.toNumber()
    
        // 实际仓颉函数行为
        let value = a + b
    
        // 把结果转换为 JSValue
        let result: JSValue = context.number(value).toJSValue()
    
        // 返回 JSValue
        return result
    }
  3. 【仓颉侧】注册要导出的函数。

    // 类名没有影响
    class Main {
        // 定义静态构造函数(也可用全局变量和静态变量的初始化表达式触发)
        static init() {
            // 注册键值对
            JSModule.registerModule {context, exports =>
                exports["addNumber"] = context.function(addNumber).toJSValue()
            }
        }
    }
  4. 【ArkTS 侧】导入 ark_interop_loader,这是一个在 ohos-sdk 中提供的 napi 模块,作为仓颉运行时的启动器和仓颉模块的加载器。

    import {requireCJLib} from "libark_interop_loader.so"
  5. 【ArkTS 侧】定义仓颉库导出的接口。

    interface CangjieLib {
        // 定义的仓颉互操作函数,名称与仓颉侧注册名称一致。一般先定义 ArkTS 函数声明,在实现仓颉函数时根据声明来解析参数和返回。
        addNumber(a: number, b: number): number;
    }
  6. 【ArkTS 侧】导入和调用仓颉库。

    // 导入仓颉库,仓颉模块默认编译产物是 libentry_default.so,用户可以在 cjpm.toml 中修改配置。
    const cjLib = requireCJLib("libentry_default.so") as CangjieLib;
    // 调用仓颉接口
    let result = cjLib.addNumber(1, 2);
    console.log(`1 + 2 = ${result}`);

在仓颉应用里使用 ArkTS 模块

ArkTS 模块的编译产物主要有两种:

  1. C 代码(+ArkTS)编译成 so。
  2. 纯 ArkTS 代码编译成 abc。

加载 ArkTS so 模块

ArkTS so 模块根据部署方式的不同,分为以下几种:

  1. 随系统发布,在镜像的/system/lib64/module目录下。
  2. 随应用(hap)发布,在应用的/libs/arm64-v8a目录下,安装后在设备上的全局路径(通过hdc shell观察到的路径):/data/app/el1/bundle/public/${bundleName}/libs/arm64、沙箱路径(运行时可访问路径):/data/storage/el1/bundle/libs/arm64。
  3. 随动态库(hsp)发布。

这里主要介绍怎么加载随系统发布的 so 模块,这些 so 在 OpenHarmony 的官方文档里会有开发文档。

接下来以相册管理模块作为示例,详细的介绍加载流程。

  1. 查看 ArkTS 文档,其导入模块的范本如下。

    import photoAccessHelper from '@ohos.file.photoAccessHelper';
  2. 创建 ArkTS 运行时,准备互操作上下文。

    import ohos.ark_interop.*
    
    func tryLoadArkTSSo() {
        // 创建新的 ArkTS 运行时
        let runtime = JSRuntime()
        // 获取互操作上下文
        let context = runtime.mainContext
        ...
    }
  3. 根据 ArkTS 文档里模块导入名称,推导仓颉的模块导入参数。

    ArkTS 导入名仓颉导入参数说明
    @ohos.file.photoAccessHelper("file.photoAccessHelper")以 @ohos 开头,那么参数只需要去掉 "@ohos."。
    @hms.core.push.pushService("core.push.pushService", prefix: "hms")以非 @ohos 开头,那么参数去掉 "@xxx.",并把 xxx 作为第二个参数。
  4. 导入 ArkTS 模块。

    func tryLoadArkTSSo() {
        ...
        let module = context.requireSystemNativeModule("file.photoAccessHelper")
    }

模块导入进来是一个 JSValue,接下来可以按照操作 ArkTS 数据的方法去操作模块。

在仓颉里操作 ArkTS 数据

从 ArkTS 传过来的参数,其原始类型是JSValue,这是一个匿名类型的数据,首先需要知晓其类型。

  • 通过JSValue.typeof()获取其类型枚举JSType。
  • 通过其他途径(包括但不限于阅读 ArkTS 源码、参考文档以及开发者口述)知晓其类型,然后通过类型校验接口来验证,比如判断是否是 number 类型JSValue.isNumber()。

当知道其类型之后,再把JSValue转换为对应的仓颉类型或 ArkTS 引用。

  • 转换为仓颉类型,比如一个 ArkTS string 转换为仓颉 String,JSValue.toString(JSContext)。
  • 转换为 ArkTS 引用,比如一个 ArkTS string 转换为 JSString,JSValue.asString(JSContext)。

通过仓颉数据来构造 ArkTS 数据,是通过 JSContext 的方法类构造 ArkTS 数据。

一个应用进程可以存在多个 ArkTS 运行时,而 ArkTS 运行时之间的数据是不通用的,任何 ArkTS 数据都归属于一个特定的运行时,因此创建 ArkTS 数据接口是从运行时的角度出发。

以number举例,创建一个number的方式是JSContext.number(Float64)。

ArkTS 主要数据类型对应到仓颉类型的映射如下:

ArkTS 类型仓颉类型安全引用typeof 类型
undefined-JSUndefinedJSType.UNDEFINED
null-JSNullJSType.NULL
booleanBoolJSBooleanJSType.BOOL
numberFloat64JSNumberJSType.NUMBER
stringStringJSStringJSType.STRING
object-JSObjectJSType.OBJECT
Array-JSArrayJSType.OBJECT
bigintBigIntJSBigIntJSType.BIGINT
function-JSFunctionJSType.FUNCTION
symbol-JSSymbolJSType.SYMBOL

安全引用的安全体现在两个方面:

  • 类型安全,特定类型的接口只能从安全引用里访问,总是需要先做显式的类型转换再访问。
  • 生命周期安全,对于由 ArkTS 来分配和回收的对象,安全引用能保障这些对象的生命周期。

操作 ArkTS 对象

从一个互操作函数的实现举例,该函数在 ArkTS 的声明是:addByObject(args: {a: number; b: number}): number。

func addByObject(context: JSContext, callInfo: JSCallInfo): JSValue {
    // 获取首个参数
    let arg0 = callInfo[0]
    // 校验参数0是否是对象,否则返回undefined
    if (!arg0.isObject()) {
        return context.undefined().toJSValue()
    }
    // 把参数0转换为JSObject
    let obj = arg0.asObject(context)
    // 从JSObject获取属性
    let argA = obj["a"]
    let argB = obj["b"]
    // 把JSValue转换为Float64
    let a = argA.toNumber()
    let b = argB.toNumber()

    let result = a + b
    return context.number(result).toJSValue()
}

除了可以从对象上读取属性外,还可以对属性赋值或创建新属性,操作方式为 JSObject[key] = value,其中 key 可以是仓颉 String 、JSString 或 JSSymbol,value 是 JSValue 。

说明

通过 JSObject[key] = value 定义属性时,该属性可写、可枚举、可配置。

对属性赋值在以下几种场景会失败,失败之后没有异常或日志:

  1. 目标对象是 sealed 对象,由 Object.seal() 接口创建的对象具有不可修改的特性,无法创建新的属性和修改原有属性。
  2. 目标属性的 writable 是 false ,由 Object.defineProperty(object, key, {writable: false, value: xxx}) 定义属性时,可以指定属性是否可写。

对于一个未知对象,可以枚举出该对象的可枚举属性:

func handleUnknownObject(context: JSContext, target: JSObject): Unit {
    // 枚举对象的可枚举属性
    let keys = target.keys()
    println("target keys: ${keys}")
}

创建一个新的 ArkTS 对象,可以通过 JSContext.object() 来创建。

对于 ArkTS 运行时,有一个特殊的对象,该对象是 ArkTS 全局对象,在任何 ArkTS 代码里都可以直接访问该对象下的属性,在仓颉侧可以通过 JSContext.global 来访问它。

调用 ArkTS 函数

拿到一个 ArkTS 函数后,可以在仓颉里直接调用,这里以一个互操作函数举例:addByCallback(a: number, b: number, callback: (result: number)=>void): void。

func addByCallback(context: JSContext, callInfo: JSCallInfo): JSValue {
    // 获取参数,并转换为Float64
    let a = callInfo[0].toNumber()
    let b = callInfo[1].toNumber()
    // 把第3个参数转换为JSFunction
    let callback = callInfo[2].asFunction(context)
    // 计算结果
    let result = a + b
    // 从仓颉Float64创建ArkTS number
    let retJSValue = context.number(result).toJSValue()
    // 调用回调函数
    callback.call(retJSValue)
}

这个用例里的函数是不带 this 指针的,针对需要 this 指针的方法调用,可以通过命名参数 thisArg 来指定。

func doSth(context: JSContext, callInfo: JSCallInfo): JSValue {
    let callback = callInfo[0].asFunction(context)
    let thisArg = callInfo[1]

    callback.call(thisArg: thisArg)
}

在 ArkTS 代码里,可以通过 对象.方法(...) 来进行调用,这时会隐式传递 this 指针。

class Someone {
    id: number = 0
    doSth(): void {
        console.log(`someone ${this.id} have done something`)
    }
}

let target = new Someone()

// 这里会隐式传递this指针,调用正常
target.doSth()

let doSth = target.doSth;
// 这里没有传递this指针,会出现异常`can't read property of undefined`
doSth.call()

在仓颉里,对应的写法如下:

func doSth(context: JSContext, callInfo: JSCallInfo): JSValue {
    let object = callInfo[0].asObject(context)
    // 会隐式传递this指针,调用正常
    object.callMethod("doSth")

    let doSth = object["doSth"].asFunction(context)
    // 未传递this指针,会出现异常`can't read property of undefined`
    doSth.call()
    // 显式传递this指针,调用正常
    doSth.call(thisArg: object.toJSValue())
}

在 ArkTS 里操作仓颉对象

这里用例展示的是把仓颉对象分享到 ArkTS 运行时,使用 ArkTS 运行时的内存管理机制来控制仓颉对象的生命周期,并通过相关的互操作接口来访问该对象。

// 定义共享类
class Data <: SharedObject {
    Data(
        // 定义2个属性
        let id: Int64,
        let name: String
    ) {}

    static init() {
        // 注册导出到ark的函数
        JSModule.registerFunc("createData", createData)
        JSModule.registerFunc("setDataId", setDataId)
        JSModule.registerFunc("getDataId", getDataId)
    }

    // 创建共享对象
    static func createData(context: JSContext, _: JSCallInfo): JSValue {
        // 创建仓颉对象
        let data = Data(1, "abc")
        // 创建js对仓颉对象的引用
        let jsExternal = context.external(data)
        // 返回js对仓颉对象的引用
        return jsExternal.toJSValue()
    }

    // 设置对象的id
    static func setDataId(context: JSContext, callInfo: JSCallInfo): JSValue {
        // 读取参数
        let arg0 = callInfo[0]
        let arg1 = callInfo[1]

        // 把参数0转换为js对仓颉对象的引用
        let jsExternal = arg0.asExternal(context)
        // 获取仓颉对象
        let data: Data = jsExternal.cast<Data>().getOrThrow()
        // 把参数1转换为Float64
        let value = arg1.toNumber()

        // 仓颉对象修改属性
        data.id = Int64(value)

        // 返回undefined
        let result = context.undefined().toJSValue()
        return result
    }

    // 获取对象的id
    static func getDataId(context: JSContext, callInfo: JSCallInfo): JSValue {
        let arg0 = callInfo[0]

        let jsExternal = arg0.asExternal(context)

        let data: Data = jsExternal.cast<Data>().getOrThrow()

        let result = context.number(Float64(data.id)).toJSValue()
        return result
    }
}
import {requireCJLib} from "libark_interop_loader.so"
// 定义导出符号
interface CustomLib {
    createData(): undefined
    setDataId(data: undefined, value: number): void
    getDataId(data: undefined): number
}

// 加载自定义库
const cjLib = requireCJLib("libentry_default.so") as CustomLib

// 创建共享对象
let data = cjLib.createData()
// 操作对象属性
cjLib.setDataId(data, 3)
let id = cjLib.getDataId(data)

console.log("id is " + id)

JSExternal 对象在 ArkTS 里的类型会被识别为 undefined ,直接使用它来作为参数很容易被传递错误的参数。在使用前一个用例的仓颉库时:

...
// 创建共享对象
let data = cjLib.createData()
// 操作对象属性
cjLib.setDataId(undefined, 3) // 错误的参数,应该传递的是仓颉引用,但是编译器能通过编译
let id = cjLib.getDataId(data)
...

把仓颉对象的引用挂在 JSObject 上传递到 ArkTS

在实际开发接口时,可以把 JSExternal 对象绑定到一个 JSObject 对象上,把 JSExternal 的数据隐藏起来,以此来提高接口的安全性。

下面通过一个例子来展示:

// 定义共享类
class Data <: SharedObject {
    Data(
        // 定义2个属性
        var id: Int64,
        let name: String
    ) {}

    static init() {
        // 注册导出到ark的函数
        JSModule.registerFunc("createData", createData)
    }

    // 创建共享对象
    static func createData(context: JSContext, _: JSCallInfo): JSValue {
        let data = Data(1, "abc")
        let jsExternal = context.external(data)

        // 创建空JSObject
        let object = context.object()
        // 把js对仓颉对象的引用挂在JSObject的隐藏属性上
        object.attachCJObject(jsExternal)

        // 为js对象增加2个方法
        object["setId"] = context.function(setDataId).toJSValue()
        object["getId"] = context.function(getDataId).toJSValue()

        return object.toJSValue()
    }

    // 设置对象的id
    static func setDataId(context: JSContext, callInfo: JSCallInfo): JSValue {
        // 获取this指针
        let thisArg = callInfo.thisArg
        let arg0 = callInfo[0]

        // 把this指针转换为JSObject
        let thisObject = thisArg.asObject(context)
        // 从JSObject上获取隐藏属性
        let jsExternal = thisObject.getAttachInfo().getOrThrow()
        // 从js对仓颉对象的引用上获取仓颉对象
        let data = jsExternal.cast<Data>().getOrThrow()
        // 把参数0转换为Float64
        let value = arg0.toNumber()

        // 修改仓颉对象的属性
        data.id = Int64(value)

        let result = context.undefined()
        return result.toJSValue()
    }

    // 获取对象的id
    static func getDataId(context: JSContext, callInfo: JSCallInfo): JSValue {
        let thisArg = callInfo.thisArg
        let thisObject = thisArg.asObject(context)
        let jsExternal = thisObject.getAttachInfo().getOrThrow()
        let data = jsExternal.cast<Data>().getOrThrow()

        let result = context.number(Float64(data.id)).toJSValue()
        return result
    }
}
import {requireCJLib} from "libark_interop_loader.so"
// 定义导出符号
interface Data {
    setId(value: number): void
    getId(): number
}

interface CustomLib {
    createData(): Data
}

// 加载自定义库
const cjLib = requireCJLib("libentry_default.so") as CustomLib

// 创建共享对象
let data = cjLib.createData()
// 操作对象属性
data.setId(3)
let id = data.getId()

console.log("id is " + id)

为仓颉共享对象创建 JSClass

把所有的对象操作方法直接挂在对象上,一方面占用内存比较大,另一方面创建对象的开销比较大。对于追求性能的场景,可以定义一个 JSClass 来加速对象创建和减小内存占用。

// 定义共享类
class Data <: SharedObject {
    Data(
        // 定义2个属性
        var id: Int64,
        let name: String
    ) {}

    static init() {
        // 注册导出到ark的类
        JSModule.registerClass("Data") { context =>
            // 创建JSClass
            let clazz = context.clazz(jsConstructor)
            // 增加方法
            clazz.addMethod(context.string("setId"), context.function(setDataId))
            clazz.addMethod(context.string("getId"), context.function(getDataId))

            return clazz
        }
    }

    // js构造函数
    static func jsConstructor(context: JSContext, callInfo: JSCallInfo): JSValue {
        // 获取this指针
        let thisArg = callInfo.thisArg
        // 转换为JSObject
        let thisObject = thisArg.asObject(context)
        // 创建创建对象
        let data = Data(1, "abc")
        // 创建js对仓颉对象的引用
        let jsExternal = context.external(data)
        // 设置JSObject属性
        thisObject.attachCJObject(jsExternal)
        return thisObject.toJSValue()
    }

    // 设置对象的id
    static func setDataId(context: JSContext, callInfo: JSCallInfo): JSValue {
        // 获取this指针
        let thisArg = callInfo.thisArg
        // 把this指针转换为JSObject
        let thisObject = thisArg.asObject(context)
        // 从JSObject上获取隐藏属性
        let jsExternal = thisObject.getAttachInfo().getOrThrow()
        // 从js对仓颉对象的引用上获取仓颉对象
        let data = jsExternal.cast<Data>().getOrThrow()

        let arg0 = callInfo[0]
        // 把参数0转换为Float64
        let value = arg0.toNumber()

        // 修改仓颉对象的属性
        data.id = Int64(value)

        let result = context.undefined()
        return result.toJSValue()
    }

    // 获取对象的id
    static func getDataId(context: JSContext, callInfo: JSCallInfo): JSValue {
        let thisArg = callInfo.thisArg
        let thisObject = thisArg.asObject(context)
        let jsExternal = thisObject.getAttachInfo().getOrThrow()
        let data = jsExternal.cast<Data>().getOrThrow()

        let result = context.number(Float64(data.id)).toJSValue()
        return result
    }
}
import {requireCJLib} from "libark_interop_loader.so"
// 定义Data的接口
interface Data {
    setId(value: number): void
    getId(): number
}

interface CustomLib {
    // 定义Data的构造函数(JSClass)
    Data: {new (): Data}
}

// 加载自定义库
const cjLib  = requireCJLib("libentry_default.so") as CustomLib

// 创建共享对象
let data = new cjLib.Data()
// 操作对象属性
data.setId(3)
let id = data.getId()

console.log("id is " + id)

ArkTS 互操作和仓颉多线程

ArkTS 是单线程执行的虚拟机,在运行时上没有对并发做任何的容错;而仓颉在语法上支持内存共享的多线程。

如果在互操作的场景不加限制的使用多线程,可能会导致无法预期的错误,因此需要一些规范和指引来保证程序正常执行:

  1. ArkTS 代码以及大部分互操作接口只能在 ArkTS 线程上执行,否则会抛出仓颉异常。
  2. 在进入其他线程前,需要把所有依赖的 ArkTS 数据转换为仓颉数据。
  3. 在其他线程如果想要使用 ArkTS 接口,需要通过 context.postJSTask 切换到 ArkTS 线程来执行。

下面通过一个用例来展示具体做法,该用例是互操作函数,该函数的功能是对两个数字相加,并调用回调来返回相加数。

import {requireCJLib} from "libark_interop_loader.so"
// 定义导出的接口
interface CustomLib {
    addNumberAsync(a: number, b: number, callback: (result: number)=>void): void
}
// 导入仓颉库
const cjLib = requireCJLib("libentry_default.so") as CangjieLib;
// 调用仓颉函数
cjLib.addNumberAsync(1, 2, (result)=> {
    console.log("1 + 2 = " + result)
})
// 类名没有影响
class Main {
    // 定义静态构造函数
    static init() {
        // 注册键值对
        JSModule.registerFunc("addNumberAsync", addNumberAsync)
    }
}

func addNumberAsync(context: JSContext, callInfo: JSCallInfo): JSValue {
    // 从JSCallInfo获取参数列表
    let arg0: JSValue = callInfo[0]
    let arg1: JSValue = callInfo[1]
    let arg2: JSValue = callInfo[2]
    // 把JSValue转换为仓颉类型
    let a: Float64 = arg0.toNumber()
    let b: Float64 = arg1.toNumber()
    let callback = arg2.asFunction(context)
    // 新建仓颉线程
    spawn {
        // 实际仓颉函数行为
        let value = a + b
        // 发起异步回调
        context.postJSTask {
            // 创建result
            let result = context.number(value)
            // 调用js回调
            callback.call(result)
        }
    }

    // 返回 void
    return context.undefined()
}

在 ArkTS 存在着 Promise,这是对回调机制的一种封装,配合 async 、 await 的语法让回调机制变成同步调用的形式。对于上一个用例,使用 Promise 的形式来定义接口和访问:

// 接口定义
func addNumberAsync(context: JSContext, callInfo: JSCallInfo): JSValue {
    // 参数转换为仓颉类型
    let a = callInfo[0].toNumber()
    let b = callInfo[1].toNumber()
    // 创建PromiseCapability对象
    let promise = context.promiseCapability()
    // 创建新线程
    spawn {
        // 在新线程执行仓颉逻辑
        let result = a + b
        // 切换到ArkTS线程
        context.postJSTask {
            // 在ArkTS线程执行resolve
            promise.resolve(context.number(result).toJSValue())
        }
    }
    // 返回Promise
    promise.toJSValue()
}
// ArkTS 调用
import {requireCJLib} from "libark_interop_loader.so"
// 定义导出的接口
interface CustomLib {
    addNumberAsync(a: number, b: number): Promise<number>
}

async function main() {
    // 导入仓颉库
    const cjLib = requireCJLib("libentry_default.so") as CangjieLib;
    // 调用仓颉函数
    let result = await cjLib.addNumberAsync(1, 2)
    console.log("1 + 2 = " + result)
}

main()
AI大模型API中转站选型指南:12项关键指标与实战评估 随着AI大模型技术的快速迭代,企业面临着一个核心挑战:如何在基础模型频繁更新的背景下,为业务系统选择稳定可靠的API中转服务?本文基于真实生产环境测试数据,从12个关键维度系统评估主流API中转站平台,帮助技术团队在模型快速演进的生态中做出科学选型决策。 1. API中转站的核心价值与业务场景 1.1 什么是API中转站 API中转站(API Gateway/Aggregator)是连接业务系统与多个大模型厂商的中间层服务。它通过统一的接口封装了不同厂商的API协议差异,为企业提供标准化的模型调用能力。在实 阅读详情

相关推荐

stm32最小系统_完成一个最小FOC矢量控制系统所需的基本模块和功能配置

当我们读懂 FOC 矢量控制的基本原理之后,便迫不及待的想动手尝试,去实现一个矢量控制系统,让电机先转起来,有一个直观的感受。因此,我们需要设计实现一个矢量控制的最小系统,具备矢量控制的基本功能,满足电机矢量运行的基本条件。首先,看一下矢量控制的基本架构:矢量控制架构如上图所描述,矢量控制系统包括实时电流的采集、clarke 变换、park 变换、SVPWM 、实时角度的反馈和计算以及电流环和速...

weixin_39524048的博客 1784

华为的开发语言有2种,分别是ArkTS和仓颉,他们的区别是什么?

总的来说,仓颉和 ArkTS 共同发展,在鸿蒙生态中形成优势互补。未来开发者可以根据业务场景需要,对新增业务部分,根据具体场景选择使用仓颉编程语言或 ArkTS 进行增量开发。

橙-极纪元-JJYCheng 2万+

RTX 50系显卡实战3DGS:从源码编译到避坑指南

本文详细介绍了在RTX 50系显卡上成功运行3D Gaussian Splatting(3DGS)的完整实战指南。针对新显卡架构与旧版预编译包不兼容的核心问题,文章提供了从源码编译CUDA扩展的清晰步骤,包括环境配置、依赖安装、核心模块diff-gaussian-rasterization和simple-knn的编译避坑方法,并涵盖了数据准备、模型训练与实时预览的全流程,帮助开发者高效利用新硬件进行3D重建。

weixin_28314575的博客 264

仓颉语言与ArkTS互操作

在 OpenHarmony 系统上,ArkTS 具备完整广泛的生态,为复用 ArkTS 生态,仓颉支持与 ArkTS 高效跨语言互通。仓颉-ArkTS 互操作基于仓颉 CFFI 能力,通过调用 ArkTS 运行时接口,为用户提供库级别的 ArkTS 互操作能力。

程小二的博客 3075

当图像配准遇见边缘计算:低功耗设备的优化实践

本文探讨了在边缘计算环境下优化图像配准技术的实践方法,重点解决低功耗设备如树莓派和Jetson Nano上的算力瓶颈问题。通过算法轻量化、硬件加速和内存优化等策略,实现了毫秒级图像配准,适用于智能安防、移动医疗等实时性要求高的场景。

weixin_29054195的博客 389

Unity3D 简单水面shader的实现

实现思路 今天来实现一个简单的水面效果。水面的话主要是以下几个效果叠加 1.水面的动态波浪效果,我们这里使用最简单的正弦波加上法线贴图来模拟。如果想要更精确的波动效果可以考虑使用Gerstner波 2.不同水深颜色不同的效果,使用该顶点深度和顶点坐标的差值来计算深度,然后映射到颜色 3.浪涌效果,还是根据顶点深度和顶点坐标的差值来计算深度,然后叠加上一层浪涌 4.折射效果,用grabpass,采样之后进行偏移,最后叠加上去。 代码实现 首先是顶点位移函数,使用正弦函数代入顶点的x坐标和z坐标来算,因为这里写

o83290102o5的博客 1万+

华为大变革?仓颉编程语言会代替ArkTS吗?

ArkTS:ArkTS是HarmonyOS优选的主力应用开发语言,它在保持TypeScript基本语法风格的基础上,通过规范强化静态检查和分析,提升了代码的健壮性和运行性能。ArkTS提供了声明式UI范式、状态管理支持等能力,让开发者可以以更简洁、更自然的方式开发高性能应用。ArkTS是鸿蒙生态中应用开发的重要工具,深受开发者的喜爱。仓颉编程语言:仓颉是华为为鸿蒙量身打造的全场景智能应用编程语言,旨在支持鸿蒙系统下的全场景应用开发。它具有原生智能化、全场景支持、高性能和强安全的特点。

4414

Hyper-V集群部署指南

工欲善其事,必先利其器。搭建一个方便管理的服务集群,是分布式学习很重要的环境。 目前流行的虚拟机产品主要为Vmware 和 win10自带的Hyper-V(win10家庭版无此功能),网上关于Vmware的教程更多,显然它更容易上手,但Hyper-V也算是一个很不错的产品,这里记录一下我的学习过程: 目录 Hyper-V的安装 服务集群的搭建 虚拟机的网络通信 NAT网络的搭建 端口映射 Linux的静态IP 虚拟机的自启动问题 Hyper-V的安装 也不能算说是安装,如果你的win1

Swing 6923

鸿蒙系统扫盲(七):勘误补充总结,收个尾

这是笔者鸿蒙扫盲系列的最后一篇了,准备对过去的六篇扫盲系列文章,错误的地方做一些勘误,并且补充更新一些朋友们感兴趣的知识,最后收个尾。

memoryjs的专栏 2477

Word论文插入带有方括号序号的参考文献——尾注+交叉引用

文章目录1. 尾注功能2. 为参考文献的序号添加方括号3. 调整参考文献前的序号大小4. 重复引用同一参考文献5. 将交叉引用的序号改为带有方括号的上标序号格式6. 删除参考文献前的尾注横线 1. 尾注功能 首先使用尾注功能顺序插入参考文献,暂时不考虑方括号问题,具体操作如下: ① 将光标放在需要插入参考文献角标的地方 ② 在word菜单栏,“引用-脚注部分右下角箭头”(用来设置尾注格式) ③如...

Your_Raymond的博客 9万+

用ADSP-21569+SigmaStudio打造音频处理系统:从硬件连接到实时调参实战

本文详细介绍了如何利用ADSP-21569 DSP芯片和SigmaStudio图形化编程工具构建高性能音频处理系统。从硬件接口配置、信号流设计到实时参数调试,提供工业级开发中的实用技巧,包括低延迟优化、动态参数插值等关键技术,帮助开发者快速实现专业音频处理方案。

9o8p7i6u5y的博客 980

华为的仓颉和ArkTS这两门语言有什么区别

在运行时实例内存隔离的基础上,ArkCompiler通过共享运行实例中的不可变或者不易变的对象、内建代码块、方法字节码等技术手段,优化了并发运行实例的启动性能和内存开销。了解TypeScript的朋友都应该知道,其实TypeScript就是JavaScript的改进版本,是JS的超级,现在又出了个ArkTS,说是TS的超集。ArkTS的官网介绍说,ArkTS是TypeScript的进一步强化版本,简单来说就是包含了TS的风格,但是做了一些改进。而且,还说了是原生智能化,天生原场景,仔细关注这句话【

qq_31860647的博客 3965

基于STM32的家庭环境监测毕业设计(全部资料)

本文设计并实现了一套基于STM32的家庭环境监测系统,具备温湿度、烟雾浓度和PM2.5检测功能,通过OLED屏实时显示数据,并能自动或手动控制风扇、加湿器等设备。系统采用分层架构设计,包含数据采集、处理、显示和控制执行模块,经测试性能稳定可靠。研究结果表明,该系统能有效提升家庭环境监测与控制的智能化水平,但仍存在传感器精度和智能化程度不足等问题,未来可进一步优化硬件配置并引入AI算法提升性能。

3254968421的博客 462

仓颉编程语言开发指南(下)

文章目录仓颉编程语言开发指南(上)十五、宏1.宏的简介2.Tokens 相关类型和 quote 表达式Token 类型Tokens 类型quote 表达式和插值3.语法节点节点的解析使用解析表达式和声明的函数使用构造函数进行解析节点的组成部分BinaryExpr 案例FuncDecl 案例使用 quote 插值语法节点4.宏的实现非属性宏属性宏嵌套宏宏定义中嵌套宏调用宏调用中嵌套宏调用嵌套宏之间的消息传递5.编译、报错与调试宏的编译和使用并行宏展开diagReport 报错机制使用 --debug-macr

欢迎来到我的学习笔记薄 2994

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

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

作者:京东科技 杨拓一、仓颉介绍仓颉是一种通用编程语言,适用于各种应用开发,兼顾开发效率和运行性能,其优势如下:1、高效编程:语法简明高效,支持多种范式编程。如插值字符串、主构造函数、Flow 表达式、match、if-let、while-let 和重导出等,减少冗余书写,提升开发效率。2、轻量运行时库:标准库中除 cor...

qq_35485206的博客 685

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

技术前瞻:华为鸿蒙HarmonyOS NEXT仓颉语言Beta招募亮点提前曝光

未来,开发者可以根据业务场景的需要,对新增业务部分选择使用仓颉编程语言或ArkTS进行增量开发,特别是在高吞吐量、高频读写的数据处理场景、高频交互高负载场景以及对启动时延敏感的场景中,仓颉语言将是更佳的选择。随着数字化转型的加速,编程语言作为软件开发的基石,其创新与演进一直是技术界的焦点。华为鸿蒙 HarmonyOS NEXT 仓颉语言的发布,不仅是华为技术创新的又一里程碑,更是对全球开发者的一次盛大邀请。随着Beta招募活动的启动,我们有理由相信,这将激发出更多的创新火花,推动整个行业的技术进步。

xixixihahaha_的博客 639

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

华为公布《鸿蒙编程语言白皮书》V1.0 版:解读适用场景

鸿蒙编程语言白皮书

持之以恒 1370

C 盘瘦身:用 RAPR 清理 Windows 驱动存储的完整流程

Driver Store Explorer(RAPR)是一款免费开源的 Windows 驱动存储(DriverStore)清理工具。每次更新驱动,系统都会把旧版本留在存储里,日积月累能占掉好几 GB 的 C 盘空间。读完本文,你可以把整个驱动存储备份下来、让工具自动挑出旧驱动,再安全地删除它们。 ## 🖥️ 安装并打开 RAPR,认识列表里的列 RAPR 只能在 Windows 上运行,需要

gitblog_02541的博客 405

【HarmonyOS鸿蒙系统开发技术】2.HarmonyOS NEXT版本

另外未来面向鸿蒙 AI 原生应用开发,仓颉提供了面向开发者全套的内嵌 Agent DSL 的编程框架,便于开发者高效的完成鸿蒙 AI 原生应用开发。本次升级为开发者预览版尝鲜升级,提供仓颉语言 SDK、开发者指南以及相关的 DevEco Studio 插件,供开发者使用仓颉语言开发、调试及运行 HarmonyOS Next 应用。《荀子・解蔽》称:“好书者众矣,而仓颉独传者壹也”,该名字也是代表了团队成员对于仓颉编程语言的寄托,希望该语言能够被广大开发者喜爱,并且广为流传。为官网仓颉编程语言问答页面显示,

xzs51job的专栏 1633

开箱!开发者的第一个仓颉语言程序 | 文末赠书

文末赠书Hello, Cangjie2024 年华为 HDC 大会上官宣仓颉编程语言,开启开发者预览。小异闻讯按捺不住激动的心情,第一时间就关注官方公众号,并申请体验资格。经过两天的漫长等待后,终于通过审核,拿到了仓颉编译工具。现在小异就给大家来开箱体验。开发环境:Debian GNU/Linux 11(bullseye)编译工具:cjc(Cangjie Compiler: 0.51.4)“He...

程序猿DD 723

Mem Reduct 中文界面配置指南:3 条路径快速切换简体中文

用 Windows 的久了都知道,内存占用一高,程序就开始卡。Mem Reduct 是一款轻量级实时内存管理工具,它通过系统内部的 Native API(Windows 未公开的内部接口,程序借它释放系统缓存与空闲页面)监控并清理物理内存、分页文件占用。它的功能没问题,可原版只有英文界面——菜单里 Standing by、Standby list 这类词,新手很难对号入座。 这篇指南写给三类人:

gitblog_01121的博客 917

鸿蒙原生应用元服务开发-仓颉ArkTS相互操作(一)

对于 ArkTS 运行时,有一个特殊的对象,该对象是 ArkTS 全局对象,在任何 ArkTS 代码里都可以直接访问该对象下的属性,在仓颉侧可以通过 JSContext.global 来访问它。一个应用进程可以存在多个 ArkTS 运行时,而 ArkTS 运行时之间的数据是不通用的,任何 ArkTS 数据都归属于一个特定的运行时,因此创建 ArkTS 数据接口是从运行时的角度出发。2.在仓颉应用里使用 ArkTS 存量库:在仓颉代码里创建新的 ArkTS 运行时,并加载和执行 ArkTS 的字节码。

weixin_69135651的博客 1393

【HarmonyOS NEXT+AI】问答05:ArkTS和仓颉编程语言怎么选?

在“HarmonyOS NEXT+AI大模型打造智能助手APP(仓颉版)”课程中,学员提问为何使用仓颉编程语言而非鸿蒙主推的ArkTS。鸿蒙系统自2019年起支持多种编程语言,早期为JS和C/C++,后引入Java,自HarmonyOS 3起主推ArkTS,而HarmonyOS NEXT则引入仓颉语言。仓颉是华为自研的现代编程语言,具备高效编程、安全可靠、轻松并发和卓越性能等特性,适用于高性能高并发场景,未来将与ArkTS共同发展,为鸿蒙应用开发者提供高效开发体验。开发者可根据个人偏好和职业需求选择编程语言

老卫的技术站 1712

深入探索鸿蒙HarmonyOS应用开发:仓颉与ArkTS互操作实践

作为OpenHarmony专属开发的轻量级语言,仓颉(Cangjie)在设计之初就考虑了与ArkTS(TypeScript超集)的深度协同。除基础函数调用外,更复杂的场景包括:仓颉主动加载ArkTS模块(如相册管理)、操作ArkTS传递的复杂对象、通过回调函数实现异步通信。整个过程中最惊喜的是实时预览功能,可以立即看到ArkTS调用仓颉函数的返回结果,这种即时反馈对调试互操作接口帮助很大。通过AI辅助生成的初始项目结构非常规范,特别是自动处理的模块注册逻辑大大降低了入门门槛。互操作的核心实现原理。

SilvermistOwl67的博客 1058
上一篇: 仓颉编程语言:仓颉-C 互操作
下一篇: 仓颉编程语言:cjc 使用
懂你的大海
博客等级 码龄6年 9049粉丝 1381原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值