IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> HarmonyOS 实战——万字分析并学习 JsFACard 项目 -> 正文阅读

[系统运维]HarmonyOS 实战——万字分析并学习 JsFACard 项目

在上一篇中学习原子化服务的文 HarmonyOS 实战——认识服务卡片及运行第一个服务卡片 的后半部分学习了如何运行官方提供的案例,运行效果如下:

这篇文就官方提供的 JsFACard 源码进行学习。JsFACard 的命名是因为这个项目主要是基于 JavaScript 进行实现的 FA(Feature Ability,元服务,代表有界面的 Ability,用于与用户进行交互) 卡片服务(Card 的由来)。

完成了 JsFACard 案例学习应该能够掌握以下技能:

  • 了解原子服务的基础结构

    即 src 的项目结构

  • 了解基础的卡片服务的配置

    即通过修改 config.json 完成卡片服务的配置

  • 了解卡片服务的结构

  • 实现卡片服务的数据交换和状态更新

    通过 json/js 实现状态管理,以及通过 Java 代码中的 onTriggerFormEvent方法 实现卡片状态的更新

项目结构

主要的内容,即 src 目录下的内容如下:

也就是下面的结构:

|- src
|  |- main # 主要内容
|  |  |- java # java 依旧会别用做为主要的实例管理模块
|  |  |  |- 存储其他相关对象的目录
|  |  |  |  |- ...
|  |  |  |- MainAbility # 主程序入口,DevEco Studio生成
|  |  |  |- MyApplication # DevEco Studio生成,不需变更
|  |  |- js # 主要的呈现部分
|  |  |  |- card # 卡片1号
|  |  |  |  |- common # 存放公共资源文件
|  |  |  |  |- i18n # 多语言支持
|  |  |  |  |- pages # 存放所有组件页面
|  |  |  |  |  |- index # 入口,包含对应文件
|  |  |  |  |  |  |- index.css
|  |  |  |  |  |  |- index.hml
|  |  |  |  |  |  |- index.json
|  |  |  |- default # 主程序,非 卡片
|  |  |  |  |- ... # 结构基本一致,除了没有 index.json 文件,取而代之的是 index.js
|  |  |  |  |- app.js # 用于全局JavaScript逻辑和应用生命周期管理
|  |  |  |- jscardtemplate # 卡片2号
|  |  |  |  |- 结构一样
|  |  |  |- jsmusictemplate # 卡片3号
|  |  |  |  |- 结构一样
|  |  |- resources # 共享资源
|  |  |- config.json # 配置文件
|  |- ohosTest # 测试部分,这里不会赘述

基础结构相对而言还是比较简单,理解起来也不是非常的复杂,不过刚开始看的时候不知道还需要写 java 就有些蒙逼。不过最终还是下载了 JsFACard 才算搞明白,原来使用 JavaScript 开发不代表纯 JavaScript 开发这个道理。

上文内容所包含的参考资料有:

  • app.js

    这篇文章讲述了 app.js 的用处

  • JS FA 概述

    卡片服务始终还是依赖于 FA 而进行实现,换言之,不了解 FA 就无法实现卡片服务

  • 代码结构解读

    这个案例是 JS 计步器卡片 这个案例,代码更加复杂一些,所以刚开始没有选择这个案例进行学习

  • 文件组织

    服务卡片的文件组织

源码学习

这是官方提供的案例,下载地址在:https://gitee.com/openharmony/app_samples/tree/master/UI/JsFACard

config.json

完整的配置文件可以在案例中的 JsFACard / entry / src / main / config.json 看到网址在:https://gitee.com/openharmony/app_samples/blob/master/UI/JsFACard/entry/src/main/config.json

config.json 的大体结构如下:

config.json

可以看出来,config.json 有三个最大的,不可缺省 的模块:

  • app

    表示应用的全局配置信息,同一个应用程序中,app 中的信息必须保持一致。

    这里不会赘述。

  • deviceConfig

    应用在具体设备上的配置信息,这里不会赘述。

  • module

    这块是重点,会结合文档详细学习一下。

module

这是重点,module 部分管理所有当前 HAP(HarmonyOS Ability Package) 的配置信息。每个 HAP 是 Ability 的部署包,Ability 为 应用/服务 的基本组成单位,我的理解是某个功能的具体实现。

这里会结合项目结构对 module 中的内容进行分析和学习。

  • package,不可缺省

    package 是 HAP 的包结构名称,在应用内应保证唯一性,建议与 HAP 的工程目录保持一致。

    例如说这个项目的 package 值是 ohos.samples.jsfacard,与 HAP 的工程目录是保持一致的(毕竟官方自己建议这么实施)。

    package

  • name,不可缺省

    HAP 的类名,前缀需要与同级的 package 标签指定的包名一致,也可采用 . 开头的命名方式。

    也就是使用绝对定位和相对定位的关系,原本的值使用的是相对定位,也就是采用 . 开头的命名方式:.MainAbility。这种情况下,系统运行时回去寻找 package 下的类名进行打包。

    本机测试采用绝对定位的方式,也就是 ohos.samples.jsfacard.MainAbility 一样可以运行。

  • mainAbility

    表示 HAP 包的入口 ability 名称,如果存在 page 类型的 ability 就不可缺省。

    案例情况下,name 和 mainAbility 指向的是同一个类——ohos.samples.jsfacard.MainAbility

  • deviceType,不可缺省

    当前 应用/服务 可运行的设备,接受的参数为字符串类型。预设的时候只勾选了手机,所以这里的值是 ["phone"]

  • distro,不可缺省

    HAP 发布的具体描述,包含的信息有:

    • deliveryWithInstall,不可缺省,建议设置为 true

      当前 HAP 是否支持随应用安装。

      设置 false 就代表了安装应用不会安装当前 HAP 的意思?不是很明白这个特性是什么意思。

    • moduleName

      模块名称,这点抬头看最上面即可:

      module-name

      值是 entry

    • moduleType

      表示 HAP 的类型,目前只能在 entry 和 feature 中选择。

    • installationFree

      免安装特性,JsFACard 默认值是 false,感觉设置为 true 也可以吧。

  • abilities,可缺省

    数组格式,其中每个元素表示一个提供的服务,整个数组代表着所有提供的服务。

  • js,可缺省

    数组格式,表示基于 JS UI 框架开发的 JS 模块集合,其中的每个元素代表一个 JS 模块的信息。

    在 JsFACard 之中,除了 default 是默认程序的 UI 之外,其余每一个 JS 对象所对应的都是一个 forms,也就是卡片服务:

    forms-js

abilities

JsFACard 项目里值包含了一个对象,对象的中的键值对包含:

  • skills

    表示 Ability 能够接收的 Intent 的特征,一般使用的时候系统预定义内容。JsFACard 中的配置使用的就是系统预定义内容,具体配置如下:

    {
      "skills": [
        {
          "entities": ["entity.system.home"],
          "actions": ["action.system.home"]
        }
      ]
    }
    
  • name

    表示 ability 的名称。

    鉴于这只是一个 Demo 项目,并且项目的入口和服务的入口是一样的,所以这里的值依旧是 .MainAbility

  • icon

    图标,注意这里的值使用的是 $media:icon,注意看提示:

    icon

    icon 接受值的格式就是 $media:some-value 这样一个格式。

    资源(resources) 下存放资源是有一定程度上的固定格式的。例如说 resources 下存放资源是需要有两级目录,一级子目录为 base 目录限定词目录,二级子目录为资源目录,图解如下:

    resources
    |---base  // 默认存在的目录
    |   |---element
    |   |   |---string.json
    |   |---media
    |   |   |---icon.png
    |---en_GB-vertical-car-mdpi // 限定词目录示例,需要  开发者自行创建
    |   |---element
    |   |   |---string.json
    |   |---media
    |   |   |---icon.png
    |---rawfile  // 默认存在的目录
    

    因为 base 是系统默认存在的目录,当资源目录中没有与设备状态匹配的限定词目录时,会自动引用该目录中的资源文件。以 $media:some-value 为例,系统会自动匹配名为 some-value 的多媒体文件。如果出现多个名字相同的资源,则会默认匹配第一个资源。

    更多细节可以参考:资源文件的分类 中的具体条款。

  • description

    特性和 icon 相似,格式依旧是 $string:some-value,并且会自动匹配第一条数据。

  • formsEnabled

    表示服务是否支持 卡片(forms) 功能,仅是用一 page 类

  • label

    特性和 icon 和 description 相似,格式依旧是 $string:some-value,并且会自动匹配第一条数据。

  • type

    表示服务的类型:

    • page,基于 page 模板开发的 FA,用于提供与用户交互的能力。

      也是这里用到的服务,其余的服务类型不多赘述。

  • forms

    服务卡片的属性,仅在 "formsEnabled": true 时才会起效。接受数据为数组,数组中的每一个对象就是一个服务卡片的属性。

    卡片的属性就比较直截了当,没有什么特别难理解的地方:

    {
      "jsComponentName": "jsmusictemplate",
      "isDefault": true,
      "formConfigAbility": "ability://ohos.samples.jsfacard.MainAbility",
      "scheduledUpdateTime": "10:30",
      "defaultDimension": "2*4",
      "name": "jsmusictemplate",
      "description": "This is a service widget",
      "colorMode": "auto",
      "type": "JS",
      "supportDimensions": ["2*4"],
      "updateEnabled": true,
      "updateDuration": 1
    }
    
  • launchType

    这里的值是 standard,表明服务可以有多个实例,适用于大多数应用场景

jsmusictemplate 的渲染效果如下:

卡片的 UI 暂且不论,上面的数据,如 This is a service widget(forms > description) 和 Js卡片(abilities > label) 均来源于配置。

js

js 接受的也是数组类型,每个数组里面是一个对象:

{
  "pages": ["pages/index/index"],
  "name": "jsmusictemplate",
  "window": {
    "designWidth": 720,
    "autoDesignWidth": true
  },
  "type": "form"
}

滚过一遍 module 和 abilities 就差不多知道这些配置都代表什么意思了。

js 数组中提供的是卡片的 UI 布局,对象中的 nameforms 包含对象中的 jsComponentName

至此,配置内容已经了解的差不多了,更多更具体的内容可以查看 应用配置文件 接下来可以开始着手了解实现的部分。

Java 部分

Java 部分的代码量不是很多,毕竟这个服务卡片的内容其实不是很多,主要的结构如下:

java

MainAbility 是主程序入口,可以选择继承 AceAbilityAbility,这里选择继承的是 AceAbility,应该是可以方便一些,毕竟根据官方文档来说,AceAbility 继承了 Ability

public class AceAbility
extends Ability
implements IAbilityContinuation

实现 MainAbility 主要是为了对服务的生命周期进行管理,官方提供的生命周期为:

life-cycle

成员变量

MainAbility 中声明的几个成员变量有:

public class MainAbility extends AceAbility {
    private static final HiLogLabel TAG = new HiLogLabel(HiLog.DEBUG, 0x0, MainAbility.class.getName());

    private static final String STATUS = "status";

    private static final String PLAY = "play";

    private static final String PAUSE = "pause";

    private static boolean isStatus = true;
}

其中包含:

  • HiLogLabel

    日志类的辅助工具,用于定义日志类型、服务域名和标签

  • STATUS,PLAY,PAUSE

    三个是静态常量,用于定义状态

  • isStatus

    定义当前组件的状态,结合其他几个常量,应该是用来控制音乐的播放。

方法

JsFACard 中重载的方法不是很多,大部分都是调用 super.method() 去调用父类中已经实现的方法,而非重载。直接调用父类实现的方法包含:

  • void onStart(Intent intent)

    必须调用这个函数去设置 UI,在整个服务的生命周期中只能被调用一次

  • ProviderFormInfo onCreateForm(Intent intent)

    调用这个函数会返回一个 ProviderFormInfo 对象,用于在 UI 上显示基础的卡片信息,以及向用户提供一个卡片服务

  • void onUpdateForm(long formId)

    调用函数去通知卡片服务提供商去更新特定的卡片

  • void onDeleteForm(long formId)

    调用函数去通知卡片服务提供商去删除特定的卡片

重载的方法有:

  • void onTriggerFormEvent(long formId, String message)

    代码如下:

    public class MainAbility extends AceAbility {
          @Override
          protected void onTriggerFormEvent(long formId, String message) {
              super.onTriggerFormEvent(formId, message);
              ZSONObject zsonObject = new ZSONObject();
              // 主要目的就是为了更新 isStatus 的状态 和更新 zsonObject
              if (isStatus) {
                  zsonObject.put(STATUS, PAUSE);
                  isStatus = false;
              } else {
                  zsonObject.put(STATUS, PLAY);
                  isStatus = true;
              }
              FormBindingData formBindingData = new FormBindingData(zsonObject);
              try {
                  updateForm(formId, formBindingData);
              } catch (FormException e) {
                  HiLog.info(TAG, "onTriggerFormEvent:" + e.getMessage());
              }
          }
      }
    

    这个函数会根据触发的事件要操作的行为去进行下面的操作:

    1. 创建新的 ZSONObject 对象

    2. 将对应的状态存储到 ZSONObject 对象中

    3. 更新成员变量 isStatus

    4. 将 ZSONObject 对象 写入 FormBindingData 对象 中去

    5. 调用更新卡片的功能去将 FormBindingData 写入对应的卡片服务中去,从而实现卡片服务

    这一步实现了卡片数据的交互,写入进卡片的数据有两种:

    "status": "play""status": "pause",这两个值在之后的 JS 部分中会有用。

JS 卡片开发指导 中对调用的 API 以及对实现有更具体的描写。

所以说 Ability 到底是不是 Controller 的一种来着,感觉有点像啊……

JavaScript 部分

JavaScript 部分内容分为两块:

  • 模块入口

    注意,这不是主程序入口,主程序入口依旧是 Java 中的 MainAbility。模块目录结构如下:

    appjs

    还是比较直观的,pages 负责页面的不同组件,其下 hml 文件是 HML 的模板文件,负责框架;css 文件负责样式;js 文件负责行为。

    app.js 负责应用级别的生命周期管理。

    具体程序内容这里不会详细学习,简单的了解一下内容即可。

  • 卡片服务应用

    这里以 jsmusictemplate 为例,主要是因为 jsmusictemplate 实现的功能比其他两个卡片服务更多一些。

    jsmusictemplate 的目录结构如下:

    jsmusic-temp

    可以看到,卡片服务和 FA 服务的结构是非常相似的,最大的区别在于 pages 下存在一个 .json文件,而非 .js文件。

    这大概是因为这个页面的逻辑比较简单,不需要其他一些默认值和函数,所以使用 .json文件 实现会简单一些。在另一个更加复杂的项目——JS 计步器卡片中,使用的依旧是 .js文件:

    step-counter

jsmusictemplate

这里主要学习的依旧是 jsmusictemplate,先来看看这个页面长什么样的:

music-card

可以看出页面被规划成了 左边的音乐播放 和 右边的常用功能 两个部分。.hml, .css.json 应该就是基于这两个模块进行实现的。

hml

鉴于 .hml文件 是 HTML 的模板文件,基于之前的分析,实现起来应该是这样的结构:

|- container
|  |- play-music
|  |- shortcuts

实现的结构也是这样的:

hml

  • 音乐播放功能

    其中,播放音乐的功能使用了 stack标签 去实现,根据 文档-stack 上所描述,这个标签起到的是让元素堆叠的效果,也就是让 svg 文件堆叠在背景图片上。

    stack标签 会让后面的元素堆叠到前面的元素上,因此结构里第一个元素是背景图片,第二个元素才是播放的 icon。

    注意看一下 icon 的实现代码:

    <image
      src="/common/{{ status }}.svg"
      onclick="messageEvent"
      class="status-image"
    ></image>
    

    这里的功能其实与 Java 部分的代码和 json 功能都有联动,{{}} 应该是 Mustache Syntax,中间的 status 属于变量名,可以获得 status 这个变量。回想一下 Java 代码中会通过 updateForm 更新的状态:"status": "play""status": "pause",所以这里 src 的数据有两种:"/common/play.svg""/common/pause.svg",common 文件夹中的确存在这两个文件:

    状态的变更则由 messageEvent 进行触发,这里的参数是由 json 提供的,等到 json 部分再去具体化。

  • 快捷键功能

    即右边的搜索、播放等功能,基本结构如下:

    <div class="main-div medium-display-index">
      <div class="wrap-div medium-display-index">
        <image src="/common/ic_search.svg" class="image-div"></image>
        <text class="image-text">{{ $t('strings.search') }}</text>
      </div>
      <div class="wrap-div medium-display-index">
        <image src="/common/ic_favor.svg" class="image-div"></image>
        <text class="image-text">{{ $t('strings.favor') }}</text>
      </div>
      <div class="wrap-div small-display-index">
        <image src="/common/ic_ranking.svg" class="image-div"></image>
        <text class="image-text">{{ $t('strings.ranking') }}</text>
      </div>
      <div class="wrap-div small-display-index">
        <image src="/common/ic_recommend.svg" class="image-div"></image>
        <text class="image-text">{{ $t('strings.recommend') }}</text>
      </div>
    </div>
    
    <div class="main-div small-display-index">
      <div class="wrap-div medium-display-index">
        <image src="/common/ic_ranking.svg" class="image-div"></image>
        <text class="image-text">{{ $t('strings.ranking') }}</text>
      </div>
      <div class="wrap-div medium-display-index">
        <image src="/common/ic_recommend.svg" class="image-div"></image>
        <text class="image-text">{{ $t('strings.recommend') }}</text>
      </div>
      <div class="wrap-div small-display-index">
        <image src="/common/ic_favor.svg" class="image-div"></image>
        <text class="image-text">{{ $t('strings.favor') }}</text>
      </div>
      <div class="wrap-div small-display-index">
        <image src="/common/ic_search.svg" class="image-div"></image>
        <text class="image-text">{{ $t('strings.search') }}</text>
      </div>
    </div>
    

    这里分别使用了两个 div 去实现不同的功能,而在不同 div 之间,元素的类名是不一样的,这是通过 CSS 去控制显示的内容,去进行布局。

    只显示一个 div 和同时显示两个 div 的效果如下:

    • 保留父元素为 medium-display-index

      即,保留第一个 div

      med-display-index

    • 保留父元素为 small-display-index

      即,保留第二个 div

      sm-display-index

    • 保留两个元素

      both-index

可以看到元素中都是 small-display-index 的元素被隐藏了,这是由 CSS 控制的。

css

CSS 的大部分内容都是比较常见的,除了 small-display-indexmedium-display-index 中使用的 display-index 之前是没有见过的。

display-index原子布局 中的新特性,文档中的说明是:

该适用于 div 等支持 flex 布局的容器组件中的子组件上,当容器组件在 flex 主轴上尺寸不足以显示下全部内容时,按照 display-index 值从小到大的顺序进行隐藏,具有相同 display-index 值的组件同时隐藏,默认值为 Infinity,表示不隐藏。

更具另外一份文档,也就是 通用样式 中可以得知,display 的默认值是 flex,所以当空间不够的时候,small-display-index 的值就会被隐藏掉。

至于官方为什么这么实现,我觉得和多端适配有关系,下面是平板上显示的效果,能看到和手机上的效果完全不一样:

pad-music

这个布局看起来是完全隐藏了 small-display-index,只显示 medium-display-index 中内容。因为在平板上的高度足够的关系,所以 medium-display-index 中的 4 个元素可以全都显示出来。

至于 small-display-indexmedium-display-index 加起来的宽度,则通过 音乐播放 的界面去控制的。

关于布局这方面真的还需要好好学习一下。

json

json 相对而言是三个部分中最简单的部分,因为这里没有什么特别复杂的逻辑,完整的代码只有 13 行:

{
  "data": {
    "status": "play"
  },
  "actions": {
    "messageEvent": {
      "action": "message",
      "params": {
        "message": "music change status"
      }
    }
  }
}

可以看到,在结构体中有 dataactions 两大模块,data 负责的就是数据,它所导出的数据被 src="/common/{{ status }}.svg" 所获取;actions 则负责事件,它所导出的数据被 onclick="messageEvent" 所获取。结合 Java 中的代码,音乐播放器中的事件流程就是这样的:

点击事件
java内部更新
触发JS状态更新
初始化
渲染
onTriggerFormEvent
更新Status
更新卡片

触发效果是这样的:

总结

至此,JsFACard 这个项目就已经掌握得差不多了,下一步学习的目标就打算学习一下另一个更加复杂的案例:JS 计步器卡片


本文正在参与“有奖征文 | HarmonyOS 征文大赛”活动,活动链接为:https://marketing.csdn.net/p/ad3879b53f4b8b31db27382b5fc65bbc

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-07-30 13:08:37  更:2021-07-30 13:08:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 17:38:18-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码