天天看點

#沖刺創作新星# #跟着小白一起學鴻蒙# [十] JS應用控制LED

作者:王石

在《#跟着小白一起學鴻蒙# [九] 寫個簡單的LED驅動》我們熟悉了如何在開源鴻蒙開發驅動應用,并通過hdc工具拷貝至開發闆運作。在《#跟着小白一起學鴻蒙#[六]第一個hap應用》的文章裡我們學會如何用IDE工具編寫hap應用并他通過IDE工具安裝至開發闆運作。我們學會了hap程式的開發,但是hap程式是運作在标準系統的應用,對于輕量和小型系統我們應該如何進行開發呢?接下來我們需要學習下如何開發JS應用。

輕量、小型系統JS應用

Ace(foundation/arkui/ace_engine_lite)是OpenHarmony的輕量級系統所使用的UI架構子系統,為開發者提供JS-UI開發架構。包括.html,.css,.js。開發者可以通過DevEco工具進行開發。其中JS的引擎采用三方庫裡的JerryScript(jerryscript是IoT裝置上的輕量級JS引擎,支援ECMAScript 5.1标準,适配低記憶體硬體,最小運作在64KB RAM和小于200KB的flash,提供C API)。關于jerryscript的詳細介紹可以看如下參考連結(https://github.com/jerryscript-project/jerryscript )

#沖刺創作新星# #跟着小白一起學鴻蒙# [十] JS應用控制LED

詳細的内容介紹在一下連結内可以看到官方的說明:

參考連結:https://gitee.com/openharmony/arkui_ace_engine_lite

此樣例參考小熊派設計,使用小熊派HM_Micro開發闆進行驗證

JS-UI開發流程

graph LR
 --> 配置工程 --> 增加LED程式 --> 添加JSAPI接口
           
st=>start: 建立工程
sub1=>subroutine: 配置工程
sub2=>subroutine: 增加LED程式
sub3=>subroutine: 添加JSAPI接口
e=>end: 編譯運作
st(right)->sub1(right)->sub2(right)->sub3(right)->e

           
  1. 建立[Lite]Empty Ability
    #沖刺創作新星# #跟着小白一起學鴻蒙# [十] JS應用控制LED
  2. 配置工程
    #沖刺創作新星# #跟着小白一起學鴻蒙# [十] JS應用控制LED
  3. 工程結構說明
#沖刺創作新星# #跟着小白一起學鴻蒙# [十] JS應用控制LED

工程目錄主要在entry裡,有以下内容:

  • .preview: 界面預覽目錄;
  • build: 工程編譯目錄;
  • src:包括i18n(國際化翻譯路徑),pages(界面目錄,index.css, index.hml, index.js)
  1. 工程預覽
#沖刺創作新星# #跟着小白一起學鴻蒙# [十] JS應用控制LED
  1. 添加按鍵功能控制LED燈
    • 修改index.hml
    <div class="container">
        <text class="title">
            {{ $t('strings.hello') }} {{ title }}
        </text>
    
        <div class="rowcontainer">
            <text class="content" if="{{statu == '0'}}">[狀态:{{ $t('strings.ledoff') }}]</text>
            <text class="content" if="{{statu == '1'}}">[狀态:{{ $t('strings.ledon') }}]</text>
            <text class="content" onclick="ledon">
                {{ $t('strings.ledon') }}
            </text>
            <text class="content" onclick="ledoff">
                {{ $t('strings.ledoff') }}
            </text>
            <text class="content" onclick="ledtoggle">
                {{ $t('strings.ledtoggle') }}
            </text>
        </div>
    
        <text class="content" onclick="exit">
            {{ $t('strings.exit') }}
        </text>
    
    </div>
               
    • 修改index.css
    .container {
        width: 100%;
        height: 100%;
        flex-direction: column;
        justify-content: center;
        align-items: center;
    }
    
    .title {
        width: 200px;
        font-size: 30px;
        text-align: center;
    }
    
    .content{
        width: 200px;
        font-size: 30px;
        text-align: center;
    }
    
    .rowcontainer {
        width: 100%;
        height: 50%;
        flex-direction: row;
        justify-content: center;
        align-items: center;
    }
               
    • 修改index.js
    var led = {open:1,close:0,change:2}
    import app from '@system.app';
    export default {
        data: {
            title: "",
            statu:'0'
        },
        onInit() {
            this.title = this.$t('strings.world');
        },
        ledon(e) {
            let that = this
            console.info("ledon")
            app.ledcontrol({
                code:led.open,
                success(res){
                    that.statu = res.led_status
                },
                fail(res,code){
                    console.error("ledon error")
                },
                complete(){
                    console.info("ledon complete")
                }
            })
        },
        ledoff(e) {
            let that = this
            console.info("ledoff")
            app.ledcontrol({
                code:led.close,
                success(res){
                    that.statu = res.led_status
                },
                fail(res,code){
                    console.error("ledoff error")
                },
                complete(){
                    console.info("ledoff complete")
                }
            })
        },
        ledtoggle(e) {
            let that = this
            console.info("ledtoggle")
            app.ledcontrol({
                code:led.change,
                success(res){
                    that.statu = res.led_status
                },
                fail(res,code){
                    console.error("ledtoggle failed")
                },
                complete(){
                    console.info("ledtoggle complete")
                }
            })
        },
        exit(e) {
            app.terminate()
        },
    
    }
               
    • 預覽
#沖刺創作新星# #跟着小白一起學鴻蒙# [十] JS應用控制LED
  • 将代碼編譯成hap包:點選編輯器最左下角的OhosBuild Varilants,打開編譯模式選擇視圖,編譯模式分debug和release,選擇release模式;
  • 點選編輯器上方菜單欄的Build->Build Hap(s)/App(s)->Build Hap(s),系統就會開始自動編譯代碼成hap包,等到下方Build Output無編譯錯誤,就表示代碼編譯完成了。
  1. 增加JS的API接口
    • 在app_module.h裡增加接口
      JSI::SetModuleAPI(exports, "ledcontrol", AppModule::ToggleLed);
                 
    • 在app_module.cpp裡增加接口實作
      #include "hdf_sbuf.h"
      #include "hdf_io_service_if.h"
      
      #define LED_WRITE_READ 1
      #define LED_SERVICE "hdf_led"
      
      ......
      
      
      static int OnDevEventReceived(void *priv, uint32_t id, struct HdfSBuf *data)
      {
          uint32_t value;
          HdfSbufReadUint32(data, &value);
          HILOG_ERROR(HILOG_MODULE_ACE,"%s: dev event received: %u %u\n", (char *)priv, id, value);
      
          return HDF_SUCCESS;
      }
      
      static int GpioWriteRead(struct HdfIoService *serv, int32_t eventData, int32_t *val)
      {
          int ret = HDF_FAILURE;
          struct HdfSBuf *data = HdfSBufObtainDefaultSize();
          struct HdfSBuf *reply = HdfSBufObtainDefaultSize();
      
          if (data == NULL || reply == NULL) {
              HILOG_ERROR(HILOG_MODULE_ACE,"fail to obtain sbuf data\n");
              return ret;
          }
      
          if (!HdfSbufWriteUint8(data, (uint8_t)eventData))
          {
              HILOG_ERROR(HILOG_MODULE_ACE,"fail to write sbuf\n");
              HdfSBufRecycle(data);
              HdfSBufRecycle(reply);
              return ret;
          }
      
          ret = serv->dispatcher->Dispatch(&serv->object, LED_WRITE_READ, data, reply);
          if (ret != HDF_SUCCESS)
          {
              HILOG_ERROR(HILOG_MODULE_ACE,"fail to send service call\n");
              HdfSBufRecycle(data);
              HdfSBufRecycle(reply);
              return ret;
          }
          if (!HdfSbufReadInt32(reply, val))
          {
              HILOG_ERROR(HILOG_MODULE_ACE,"fail to get service call reply\n");
              ret = HDF_ERR_INVALID_OBJECT;
              HdfSBufRecycle(data);
              HdfSBufRecycle(reply);
              return ret;
          }
          HILOG_ERROR(HILOG_MODULE_ACE,"Get reply is: %d\n", val);
      
          HdfSBufRecycle(data);
          HdfSBufRecycle(reply);
          return ret;
      }
      
      JSIValue AppModule::ToggleLed(const JSIValue thisVal, const JSIValue *args, uint8_t argsNum)
      {
          HILOG_ERROR(HILOG_MODULE_ACE, "led button pressed.");
      
          struct HdfIoService *serv = HdfIoServiceBind(LED_SERVICE);
          if (serv == NULL)
          {
              HILOG_ERROR(HILOG_MODULE_ACE,"fail to get service2 %s\n", LED_SERVICE);
              return JSI::CreateUndefined();
          }
      
          if ((args == nullptr) || (argsNum == 0) || (JSI::ValueIsUndefined(args[0]))) {
              return JSI::CreateUndefined();
          }
      
          JSIValue success = JSI::GetNamedProperty(args[0], CB_SUCCESS);
          JSIValue fail = JSI::GetNamedProperty(args[0], CB_FAIL);
          JSIValue complete = JSI::GetNamedProperty(args[0], CB_COMPLETE);
      
          int32_t num = (int32_t)JSI::GetNumberProperty(args[0], "code");
      
          int32_t replyData = 0;
      
          if (GpioWriteRead(serv, num, &replyData))
          {
              HILOG_ERROR(HILOG_MODULE_ACE,"fail to send event\n");
              JSI::CallFunction(fail, thisVal, nullptr, 0);
              JSI::CallFunction(complete, thisVal, nullptr, 0);
              JSI::ReleaseValueList(success, fail, complete);
              return JSI::CreateUndefined();
          }
      
          JSIValue result = JSI::CreateObject();
          JSI::SetNumberProperty(result, "led_status", replyData);
      
          JSIValue argv[ARGC_ONE] = {result};
          JSI::CallFunction(success, thisVal, argv, ARGC_ONE);
          JSI::CallFunction(complete, thisVal, nullptr, 0);
          JSI::ReleaseValueList(success, fail, complete, result);
      
          HdfIoServiceRecycle(serv);
      
          return JSI::CreateUndefined();
      }
                 
    • foundation\ace\ace_engine_lite\ace_lite.gni

      中添加HDF頭檔案路徑
      ace_lite_include_dirs += [
      	
      	......
      	
          "//drivers/framework/ability/sbuf/include",
          "//drivers/framework/include/core",
          "//drivers/framework/include/utils",
          "//drivers/adapter/uhdf/posix/include",
      ]
                 
    • 添加編譯依賴
      修改foundation\ace\ace_engine_lite\frameworks\BUILD.gn,在public_deps中添加以下代碼
      
      "//drivers/adapter/uhdf/manager:hdf_core",
      
      修改foundation\ace\ace_engine_lite\test\ace_test_config.gni,在extra_deps中添加以下代碼
      
      "//drivers/adapter/uhdf/manager:hdf_core",
                 

總結

  1. 基于JS擴充的類Web開發範式的方舟開發架構,采用經典的HML、CSS、JavaScript三段式開發方式。使用HML标簽檔案進行布局搭建,使用CSS檔案進行樣式描述,使用JavaScript檔案進行邏輯處理。UI元件與資料之間通過單向資料綁定的方式建立關聯,當資料發生變化時,UI界面自動觸發更新。此種開發方式,更接近Web前端開發者的使用習慣,快速将已有的Web應用改造成方舟開發架構應用。主要适用于界面較為簡單的中小型應用開發;
  2. 通過appmodule調用framework層接口,然後通過framework接口調用Hdf接口。

這樣我們就有了自己的driver和配置,後面的章節我們會講如何在hap應用裡調用驅動接口

參考連結:

https://docs.openharmony.cn/pages/v3.1/zh-cn/application-dev/ui/ui-js-overview.md/

https://gitee.com/bearpi/bearpi-hm_micro_app/blob/master/README.md

代碼參考:

https://gitee.com/wshikh/ohosexample

繼續閱讀