前言
上回说到,用`C++`来写`UI`界面的开发效率不如`JS+HTML`来的高,但设备开发又免不了要通过内核态来操作硬件,这里我们就要先打通从`JS框架`到设备驱动之间的联系。本章基于`HDF驱动`和`JS+CSS+HTML`的技术上进行,相关内容可以回顾`用鸿蒙开发AI应用(五)HDF 驱动补光灯`和`用鸿蒙开发AI应用(六)UI篇`这两篇。
在第六篇里,我们已经体验了一下使用JS
开发界面的流程,这里简单分析ace
的实现原理。
先看框架图,类小程序的Web应用先编译成js bundle
包,通过JS Data binding
来获取对象映射。其中Observer
实现了一个极简的MVVC模型,用于将DOM
上的对象分为4种类型(render、data、styleSheet、function),采用数据劫持机制分别挂载到观察者的原型函数上,并转化到C++
函数执行。
查看源码就会发现,JS
的部分是极简的,JS 引擎采用了JerryScript
,每一个标签对应了一个C++
的Component
类。绝大部分功能实际都由C++
实现,包括事件注册和触发,页面路由,控件的更新,超时,文件操作,命令行输出等等... 所有API的100%对接不禁让我想起了一个动画片。
进入C++
领域后就方便很多了,此时的ACE
仍处于用户态,用HDF
上的消息机制就能触达内核,无非是再包装一层API的套娃操作。
这里有一系列@system.xxx
的模块提供了访问APP、Router、Audio、Sensor等等的设备能力。ohos_module_config.h
定义了JS框架
中模块的别名到模块初始化函数的对应关系。其中app
,dfx
,router
模块是必有的。
// Config information for built-in JS modules of OHOS platform
const Module OHOS_MODULES[] = {
#ifdef ENABLE_MODULE_REQUIRE_TEST
{"sample", InitSampleModule},
#endif
{"app", InitAppModule},
#ifdef FEATURE_MODULE_AUDIO
{"audio", InitAudioModule},
#endif // FEATURE_MODULE_AUDIO
{"dfx", InitDfxModule},
{"router", InitRouterModule},
#ifdef ENABLE_MODULE_CIPHER
{"cipher", InitCipherModule},
#endif
};
背景知识介绍完了,就可以根据业务的复杂度来依葫芦画瓢的自定义ace模块封装功能,这里为了简化操作,我们直接在内置的app
模块上挂载操作。
修改foundation/ace/frameworks/lite/src/core/modules/app_module.h
,加入ToggleLed
函数并初始化。
namespace OHOS {
namespace ACELite {
class AppModule final : public MemoryHeap {
public:
static JSIValue ToggleLed(const JSIValue thisVal, const JSIValue* args, uint8_t argsNum);
void InitAppModule(JSIValue exports)
{
JSI::SetModuleAPI(exports, "getInfo", AppModule::GetInfo);
JSI::SetModuleAPI(exports, "terminate", AppModule::Terminate);
JSI::SetModuleAPI(exports, "toggleLed", AppModule::ToggleLed);
}
} // namespace ACELite
} // namespace OHOS
#endif // OHOS_ACELITE_APP_MODULE_H
在foundation/ace/frameworks/lite/src/core/modules/app_module.cpp
中加入,
#include
#include
#include
#include
#include "hdf_sbuf.h"
#include "hdf_io_service_if.h"
#define LED_WRITE_READ 1
#define LED_SERVICE "led_service"
JSIValue AppModule::ToggleLed(const JSIValue thisVal, const JSIValue *args, uint8_t argsNum)
{
HILOG_ERROR(HILOG_MODULE_ACE, "led button pressed.");
printf("led button pressed\n");
struct HdfIoService *serv = HdfIoServiceBind(LED_SERVICE, 0);
if (serv == NULL)
{
printf("fail to get service %s\n", LED_SERVICE);
return JSI::CreateUndefined();
}
static struct HdfDevEventlistener listener = {
.callBack = OnDevEventReceived,c
.priv = (void *)"Service0"};
if (HdfDeviceRegisterEventListener(serv, &listener) != HDF_SUCCESS)
{
printf("fail to register event listener\n");
return JSI::CreateUndefined();
}
const char *send_cmd = "toggle LED";
if (SendEvent(serv, send_cmd))
{
printf("fail to send event\n");
return JSI::CreateUndefined();
}
if (HdfDeviceUnregisterEventListener(serv, &listener))
{
printf("fail to unregister listener\n");
return JSI::CreateUndefined();
}
HdfIoServiceRecycle(serv);
return JSI::CreateUndefined();
}
修改 foundation/ace/frameworks/lite/src/core/modules/BUILD.gn
include_dirs = [
"presets",
"maplejs",
"//test/lite/testservice/include",
"//vendor/huawei/watchgt/devkit/hal/include",
"//foundation/distributedschedule/interfaces/innerkits/samgr_lite/communication/mpc/transport",
"//foundation/distributedschedule/interfaces/innerkits/samgr_lite/communication/mpc",
"//drivers/hdf/lite/include/host",
"$HDF_FRAMEWORKS/ability/sbuf/include",
"$HDF_FRAMEWORKS/core/shared/include",
"$HDF_FRAMEWORKS/core/host/include",
"$HDF_FRAMEWORKS/core/master/include",
"$HDF_FRAMEWORKS/include/core",
"$HDF_FRAMEWORKS/include/utils",
"$HDF_FRAMEWORKS/utils/include",
"$HDF_FRAMEWORKS/include/osal",
"$HDF_FRAMEWORKS/adapter/syscall/include",
"$HDF_FRAMEWORKS/adapter/vnode/include",
]
修改foundation/ace/frameworks/lite/BUILD.gn
config("ace_lite_config") {
include_dirs = [
...
]
include_dirs += [
"//drivers/hdf/lite/include/host",
"$HDF_FRAMEWORKS/ability/sbuf/include",
"$HDF_FRAMEWORKS/core/shared/include",
"$HDF_FRAMEWORKS/core/host/include",
"$HDF_FRAMEWORKS/core/master/include",
"$HDF_FRAMEWORKS/include/core",
"$HDF_FRAMEWORKS/include/utils",
"$HDF_FRAMEWORKS/utils/include",
"$HDF_FRAMEWORKS/include/osal",
"$HDF_FRAMEWORKS/adapter/syscall/include",
"$HDF_FRAMEWORKS/adapter/vnode/include",
]
}
shared_library("ace_lite") {
public_deps = [
"//base/security/frameworks/crypto_lite/js/builtin:ace_kit_cipher",
"//foundation/graphic/lite/frameworks/surface:surface",
"//foundation/multimedia/frameworks/camera_lite:camera",
"//foundation/multimedia/frameworks/player_lite:player",
"//foundation/multimedia/interfaces/kits/player_lite/js/builtin:audio_api",
"//third_party/bounds_checking_function:libsec_shared",
"//third_party/cJSON:cjson_shared",
"//third_party/jerryscript/jerry-core:jerry-core_shared",
"//third_party/jerryscript/jerry-ext:jerry-ext_shared",
"//third_party/jerryscript/jerry-libm:jerry-libm_shared",
"//third_party/jerryscript/jerry-port/default:jerry-port-default_shared",
"//utils/native/lite/js/builtin:ace_utils_kits",
"//utils/native/lite/timer_task:ace_kit_timer",
"//drivers/hdf/lite/manager:hdf_core",
"//drivers/hdf/lite/adapter/osal/posix:hdf_posix_osal",
]
}
python build.py my_hi3516dv300 -b debug
涉及到HDF
驱动和ACE
框架,就要重新完整烧录镜像文件。
前面我们在内置app
模块上增加了函数taggleLed
,对于DevEco Studio
来说是无法直接获取的,所以要先增加api
接口。
修改\Huawei\Sdk\js\2.0.1.93\api\smartVision\@system.app.d.ts
文件
export default class App {
/**
* Obtains the declared information in the config.json file of an application.
*/
static getInfo(): IAppInfo;
/**
* Destroys the current ability.
*/
static terminate(): void;
/**
* 翻转Led
*/
static toggleLed(): void;
}
这里我们在之前的UI篇
的程序中,借用一个滑动事件来触发这个翻转Led的操作。
修改entry/src/main/js/default/pages/index/index.js
swiperChange (e) {
this.swiperPage = e.index;
if(e.index == 0){
this.iconcheckedColor = '#ffffff';
this.iconUncheckedColor = '#262626';
}else{
this.iconcheckedColor = '#262626';
this.iconUncheckedColor = '#ffffff';
}
// 调用翻转Led函数
app.toggleLed();
}
其中toggleLed
这个函数名要与之前ace
模块初始化注册时,JSI::SetModuleAPI
中定义的字符串名称相匹配。
将应用打包成Hap
改名为MyUILed.hap
具体nfs
映射目录,参考前篇所述。
mkdir nfs
mount 192.168.1.52:/nfs /nfs nfs
cd nfs
./dev_tools/bin/bm set -s disable
./dev_tools/bin/bm install -p MyUILed.hap
滑动主界面,看到Led灯
切换就说明程序运行成功了。
简单总结一下整个流程,
主页面index
,引入import app from '@system.app'
模块;
事件中调用app.toggleLed();
来发起操作;
通过JS框架
打包成MyUILed.hap
(本质是一个zip包);
安装到设备上后,就被解压还原成JS代码
;
设备上的ace
框架通过JSI::SetModuleAPI
,将JS代码
通过关键字"toggleLed"
映射成对应的C++
函数AppModule::ToggleLed
;
AppModule::ToggleLed
函数中找到LED_SERVICE
,发送HDF
消息
内核中HDF
驱动收到消息后,真正执行了LED翻转操作
。
至此,鱼和熊掌是可以兼得了,我们既能高效的以类小程序方式来构建漂亮的UI
部分,又能高效的用C++
来访问设备底层功能,可以为后续大量的AI
密集计算铺平了道路。
本期相关文件资料,可在公众号“深度觉醒”,后台回复:“ohos08”,获取下载链接。
下一篇预告
最近更新 DevEco Device Tools 2.0,
整合了很多 hpm 和 hos 命令,
下一篇,我们先来尝鲜一下新版的烧录工具,
敬请期待...
往期推荐
用鸿蒙开发AI应用(触摸屏控制LED)
用鸿蒙开发AI应用(UI篇)
用鸿蒙开发AI应用(HDF 驱动补光灯)